From 65a1d849b6b9f39cae3b5242e9daf58609238f66 Mon Sep 17 00:00:00 2001 From: june Date: Tue, 16 Jul 2024 20:32:22 +0800 Subject: [PATCH] =?UTF-8?q?[2024-07-16](UPDATE):=20=E4=B8=8A=E4=BC=A0?= =?UTF-8?q?=E9=A1=B5=E9=9D=A2=E5=AE=8C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .air.toml | 4 ++-- corelib/template.go | 40 ++++++++++++++++++++++++++++++++-------- handler/domain.go | 31 +++++++++++++++++++++++++++++++ handler/index.go | 23 +++++++++++++---------- handler/login.go | 2 +- handler/picture.go | 32 ++++++++++++++++++++++++++++++++ handler/profile.go | 19 ------------------- handler/settings.go | 19 ------------------- handler/user.go | 32 ++++++++++++++++++++++++++++++++ middleware/auth.go | 12 +++++++----- router/router.go | 21 ++++++++++----------- view/domain.html | 45 +++++++++++++++++++++++++++++++++++++++++++++ view/index.html | 37 ++++++++++++++++++++++++------------- view/layout.html | 33 ++++++++++++++++++++++----------- view/login.html | 1 + view/picture.html | 10 ++++++++++ view/profile.html | 4 ---- view/settings.html | 4 ---- view/user.html | 10 ++++++++++ 19 files changed, 272 insertions(+), 107 deletions(-) create mode 100644 handler/domain.go create mode 100644 handler/picture.go delete mode 100644 handler/profile.go delete mode 100644 handler/settings.go create mode 100644 handler/user.go create mode 100644 view/domain.html create mode 100644 view/picture.html delete mode 100644 view/profile.html delete mode 100644 view/settings.html create mode 100644 view/user.html diff --git a/.air.toml b/.air.toml index 061113f..202d38d 100644 --- a/.air.toml +++ b/.air.toml @@ -16,7 +16,7 @@ full_bin = "./tmp/main picgo ./deploy/picgo-dev.yml" include_dir = [] include_ext = ["go", "html", "js", "css", "yml"] include_file = [] -kill_delay = "0s" +kill_delay = "1s" log = "build-errors.log" poll = false poll_interval = 0 @@ -39,7 +39,7 @@ main_only = false time = false [misc] -clean_on_exit = false +clean_on_exit = true [proxy] app_port = 0 diff --git a/corelib/template.go b/corelib/template.go index 5f5f260..43a77d4 100644 --- a/corelib/template.go +++ b/corelib/template.go @@ -5,17 +5,41 @@ import ( "net/http" ) -func TemplateHandler(w http.ResponseWriter, data any, filenames ...string) { - // 解析模板文件 - tmpl, err := template.ParseFiles(filenames...) +// noRender 是一个自定义模板函数,忽略不渲染传入的内容 +func noRender(s string) template.HTML { + return template.HTML(s) +} + +func TemplateHandler(w http.ResponseWriter, data any, name string, filenames ...string) { + // 创建一个新的基础模板,并将自定义函数注册到模板中 + baseTmpl := template.New("base").Funcs(template.FuncMap{ + "noRender": noRender, + }) + + // 添加一个简单的内容以确保基础模板不是空的 + _, err := baseTmpl.Parse(`{{define "base"}}{{end}}`) if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return + Logger.Errorf("Error creating base template: %v", err) + http.Error(w, "Error executing template", http.StatusInternalServerError) } - // 渲染模板并写入响应 - err = tmpl.Execute(w, data) + // 解析布局模板及其子模板 + _, err = baseTmpl.ParseFiles(filenames...) if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) + Logger.Errorf("Error parsing templates: %v", err) + http.Error(w, "Error executing template", http.StatusInternalServerError) } + + // 加载所有模板文件 + tmpl, err := baseTmpl.Clone() + if err != nil { + Logger.Errorf("error cloning base template: %v", err) + http.Error(w, "Error executing template", http.StatusInternalServerError) + } + err = tmpl.ExecuteTemplate(w, name, data) + if err != nil { + Logger.Errorf("Error executing template: %v", err) + http.Error(w, "Error executing template", http.StatusInternalServerError) + } + } diff --git a/handler/domain.go b/handler/domain.go new file mode 100644 index 0000000..4408dc2 --- /dev/null +++ b/handler/domain.go @@ -0,0 +1,31 @@ +package handler + +import ( + "github.com/gorilla/csrf" + "net/http" + "picgo/corelib" + "picgo/data" + "picgo/model" +) + +func DomainHandler(w http.ResponseWriter, r *http.Request) { + if r.Method == "GET" { + var ( + err error + user model.SysUser + ) + username := r.Context().Value("username").(string) + if user, err = data.SysUserGetCacheByUsername(username); err != nil { + http.Error(w, "IndexHandler SysUserGetCacheByUsername Error", http.StatusInternalServerError) + return + } + tmpData := map[string]interface{}{ + csrf.TemplateTag: csrf.TemplateField(r), + } + tmpData["Title"] = "域名管理" + tmpData["Active"] = r.URL.Path + tmpData["IsSuper"] = user.IsSuper + w.Header().Add("X-CSRF-Token", csrf.Token(r)) + corelib.TemplateHandler(w, tmpData, "layout.html", "view/layout.html", "view/domain.html") + } +} diff --git a/handler/index.go b/handler/index.go index db71a97..90762f1 100644 --- a/handler/index.go +++ b/handler/index.go @@ -4,25 +4,28 @@ import ( "github.com/gorilla/csrf" "net/http" "picgo/corelib" + "picgo/data" + "picgo/model" ) func IndexHandler(w http.ResponseWriter, r *http.Request) { if r.Method == "GET" { + var ( + err error + user model.SysUser + ) + username := r.Context().Value("username").(string) + if user, err = data.SysUserGetCacheByUsername(username); err != nil { + http.Error(w, "IndexHandler SysUserGetCacheByUsername Error", http.StatusInternalServerError) + return + } tmpData := map[string]interface{}{ csrf.TemplateTag: csrf.TemplateField(r), } tmpData["Title"] = "Dashboard" tmpData["Active"] = r.URL.Path - //data := struct { - // Title string - // Active string - // //csrfField string - //}{ - // Title: "Dashboard", - // Active: r.URL.Path, - // //csrfField: string(csrf.TemplateField(r)), - //} + tmpData["IsSuper"] = user.IsSuper w.Header().Add("X-CSRF-Token", csrf.Token(r)) - corelib.TemplateHandler(w, tmpData, "view/layout.html", "view/index.html") + corelib.TemplateHandler(w, tmpData, "layout.html", "view/layout.html", "view/index.html") } } diff --git a/handler/login.go b/handler/login.go index b30b20d..27b1693 100644 --- a/handler/login.go +++ b/handler/login.go @@ -17,7 +17,7 @@ func LoginHandler(w http.ResponseWriter, r *http.Request) { tmpData := map[string]interface{}{ csrf.TemplateTag: csrf.TemplateField(r), } - corelib.TemplateHandler(w, tmpData, "view/login.html") + corelib.TemplateHandler(w, tmpData, "login.html", "view/login.html") case http.MethodPost: loginService(w, r) default: diff --git a/handler/picture.go b/handler/picture.go new file mode 100644 index 0000000..fd52e03 --- /dev/null +++ b/handler/picture.go @@ -0,0 +1,32 @@ +package handler + +import ( + "github.com/gorilla/csrf" + "net/http" + "picgo/corelib" + "picgo/data" + "picgo/model" +) + +func PictureHandler(w http.ResponseWriter, r *http.Request) { + if r.Method == "GET" { + var ( + err error + user model.SysUser + ) + username := r.Context().Value("username").(string) + if user, err = data.SysUserGetCacheByUsername(username); err != nil { + http.Error(w, "IndexHandler SysUserGetCacheByUsername Error", http.StatusInternalServerError) + return + } + tmpData := map[string]interface{}{ + csrf.TemplateTag: csrf.TemplateField(r), + } + tmpData["Title"] = "图片管理" + tmpData["Active"] = r.URL.Path + tmpData["IsSuper"] = user.IsSuper + w.Header().Add("X-CSRF-Token", csrf.Token(r)) + corelib.TemplateHandler(w, tmpData, "layout.html", "view/layout.html", "view/picture.html") + + } +} diff --git a/handler/profile.go b/handler/profile.go deleted file mode 100644 index 5a287fb..0000000 --- a/handler/profile.go +++ /dev/null @@ -1,19 +0,0 @@ -package handler - -import ( - "net/http" - "picgo/corelib" -) - -func ProfileHandler(w http.ResponseWriter, r *http.Request) { - if r.Method == "GET" { - data := struct { - Title string - Active string - }{ - Title: "Admin Dashboard", - Active: r.URL.Path, - } - corelib.TemplateHandler(w, data, "view/layout.html", "view/profile.html") - } -} diff --git a/handler/settings.go b/handler/settings.go deleted file mode 100644 index 133004d..0000000 --- a/handler/settings.go +++ /dev/null @@ -1,19 +0,0 @@ -package handler - -import ( - "net/http" - "picgo/corelib" -) - -func SettingsHandler(w http.ResponseWriter, r *http.Request) { - if r.Method == "GET" { - data := struct { - Title string - Active string - }{ - Title: "Admin Dashboard", - Active: r.URL.Path, - } - corelib.TemplateHandler(w, data, "view/layout.html", "view/settings.html") - } -} diff --git a/handler/user.go b/handler/user.go new file mode 100644 index 0000000..e0a687c --- /dev/null +++ b/handler/user.go @@ -0,0 +1,32 @@ +package handler + +import ( + "github.com/gorilla/csrf" + "net/http" + "picgo/corelib" + "picgo/data" + "picgo/model" +) + +func UserHandler(w http.ResponseWriter, r *http.Request) { + if r.Method == "GET" { + var ( + err error + user model.SysUser + ) + username := r.Context().Value("username").(string) + if user, err = data.SysUserGetCacheByUsername(username); err != nil { + http.Error(w, "IndexHandler SysUserGetCacheByUsername Error", http.StatusInternalServerError) + return + } + tmpData := map[string]interface{}{ + csrf.TemplateTag: csrf.TemplateField(r), + } + tmpData["Title"] = "用户管理" + tmpData["Active"] = r.URL.Path + tmpData["IsSuper"] = user.IsSuper + w.Header().Add("X-CSRF-Token", csrf.Token(r)) + corelib.TemplateHandler(w, tmpData, "layout.html", "view/layout.html", "view/user.html") + + } +} diff --git a/middleware/auth.go b/middleware/auth.go index 41e7290..fc0cc47 100644 --- a/middleware/auth.go +++ b/middleware/auth.go @@ -1,10 +1,12 @@ package middleware import ( + "context" "net/http" "picgo/configs" "picgo/corelib" "picgo/data" + "picgo/model" "strings" ) @@ -22,14 +24,14 @@ func LoginMiddleware(next http.Handler) http.Handler { return } var ( - //user model.SysUser - err error + user model.SysUser + err error ) - if _, err = data.SysUserSelectByUsername(username); err != nil { + if user, err = data.SysUserSelectByUsername(username); err != nil { http.Redirect(w, r, "/login", http.StatusFound) return } - - next.ServeHTTP(w, r) + ctx := context.WithValue(r.Context(), "username", user.Username) + next.ServeHTTP(w, r.WithContext(ctx)) }) } diff --git a/router/router.go b/router/router.go index bd9c819..97d237f 100644 --- a/router/router.go +++ b/router/router.go @@ -24,18 +24,17 @@ func InitRouter() *mux.Router { ) // 创建新的路由器 r := mux.NewRouter() - // 处理静态文件 - //staticDir := "static" + // 不需要鉴权路由 r.PathPrefix("/static/").Handler(http.StripPrefix("/static/", handler.StaticHandler())) - r.HandleFunc("/login", handler.LoginHandler) - r.HandleFunc("/captcha", handler.CaptchaHandler) - - r.HandleFunc("/settings", handler.SettingsHandler) - r.HandleFunc("/profile", handler.ProfileHandler) - r.HandleFunc("/api/v1/upload", handler.UploadFileHandler) - // 路由鉴权 - r.Handle("/", middleware.LoginMiddleware(http.HandlerFunc(handler.IndexHandler))).Methods(http.MethodGet) - // 应用 CORS 中间件 + r.HandleFunc("/login", handler.LoginHandler).Methods(http.MethodGet, http.MethodPost) // 登录 + r.HandleFunc("/captcha", handler.CaptchaHandler).Methods(http.MethodGet) // 验证码接口 + // 需要鉴权路由 + r.Handle("/", middleware.LoginMiddleware(http.HandlerFunc(handler.IndexHandler))).Methods(http.MethodGet) // 后台首页 + r.Handle("/domain", middleware.LoginMiddleware(http.HandlerFunc(handler.DomainHandler))).Methods(http.MethodGet) // 域名管理 + r.Handle("/user", middleware.LoginMiddleware(http.HandlerFunc(handler.UserHandler))).Methods(http.MethodGet) // 用户管理 + r.Handle("/picture", middleware.LoginMiddleware(http.HandlerFunc(handler.PictureHandler))).Methods(http.MethodGet) // 图片管理 + r.Handle("/api/v1/upload", middleware.LoginMiddleware(http.HandlerFunc(handler.UploadFileHandler))).Methods(http.MethodPost) // 图片上传接口 + // 应用 CORS CSRF 中间件 http.Handle("/", middleware.CorsMiddleware(CSRF(r))) return r diff --git a/view/domain.html b/view/domain.html new file mode 100644 index 0000000..b0bfdea --- /dev/null +++ b/view/domain.html @@ -0,0 +1,45 @@ +{{define "style"}} +{{end}} +{{define "content"}} + + + + + + + + + + + + + + + + + + + + + + + + + + +
#FirstLastHandle
1MarkOtto@mdo
2JacobThornton@fat
+{{end}} +{{define "script"}} +{{end}} \ No newline at end of file diff --git a/view/index.html b/view/index.html index d242b25..d58d603 100644 --- a/view/index.html +++ b/view/index.html @@ -9,7 +9,7 @@
-
+

拖拽文件到此区域或 只支持上传图片文件,最大支持2M,上传后支持复制url

-
+
- -
+
{{ .csrfField }} {{template "content" .}}
+ {{template "script" .}} diff --git a/view/login.html b/view/login.html index ff2ef1a..f5f532c 100644 --- a/view/login.html +++ b/view/login.html @@ -1,3 +1,4 @@ + diff --git a/view/picture.html b/view/picture.html new file mode 100644 index 0000000..65916ad --- /dev/null +++ b/view/picture.html @@ -0,0 +1,10 @@ +{{define "style"}} +{{end}} + +{{define "content"}} +

Picture

+

Picture page content goes here.

+{{end}} + +{{define "script"}} +{{end}} diff --git a/view/profile.html b/view/profile.html deleted file mode 100644 index d96106d..0000000 --- a/view/profile.html +++ /dev/null @@ -1,4 +0,0 @@ -{{define "content"}} -

Profile

-

Profile page content goes here.

-{{end}} \ No newline at end of file diff --git a/view/settings.html b/view/settings.html deleted file mode 100644 index e8c7ab8..0000000 --- a/view/settings.html +++ /dev/null @@ -1,4 +0,0 @@ -{{define "content"}} -

Settings

-

Settings page content goes here.

-{{end}} \ No newline at end of file diff --git a/view/user.html b/view/user.html new file mode 100644 index 0000000..9ad6ad7 --- /dev/null +++ b/view/user.html @@ -0,0 +1,10 @@ +{{define "style"}} +{{end}} + +{{define "content"}} +

User

+

User page content goes here.

+{{end}} + +{{define "script"}} +{{end}} \ No newline at end of file