From 0014bdb9ffc1247f6b3b290c7ab38f93fd329e07 Mon Sep 17 00:00:00 2001 From: Victor_Jay Date: Sun, 21 Jun 2026 23:12:18 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=93=81=E7=89=8CCMS=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E5=8C=96=20+=20cookie=5Fsecure=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - config.yaml 新增 server.cookie_secure 配置项 - site_settings 新增 site_name/site_tagline/contact_email 站点信息 - SiteInfo 结构体扩展,支持运行时从 DB 读取站点品牌信息 - cookie.go 使用 cfg.Server.CookieSecure 替代 Mode!=debug 判断 - SetSessionCookie/ClearSessionCookie 支持 siteSettings 运行时覆盖 - 模板替换硬编码 MetaLab → {{.SiteName}}(nav/header/footer/home/login/register/guidelines/admin,12+处) - 管理后台站点设置页新增站点名称/标语/联系邮箱输入项 - 页脚版权年份改为动态 CurrentYear - 注册成功消息 CMS 化(后端+前端) --- config.yaml | 1 + internal/common/cookie.go | 8 ++--- internal/common/helper.go | 11 ++++++ internal/config/config.go | 5 +-- internal/config/site_settings.go | 15 ++++++-- internal/controller/auth_api_login.go | 4 +-- internal/controller/auth_api_register.go | 8 ++--- internal/controller/auth_controller.go | 7 ++++ internal/middleware/auth.go | 11 ++++-- internal/middleware/auth_admin.go | 4 +-- internal/middleware/site_info.go | 3 ++ internal/router/deps_core.go | 2 +- templates/MetaLab-2026/html/auth/login.html | 2 +- .../MetaLab-2026/html/auth/register.html | 6 ++-- templates/MetaLab-2026/html/home/index.html | 6 ++-- .../MetaLab-2026/html/layout/footer.html | 2 +- .../MetaLab-2026/html/layout/header.html | 4 +-- templates/MetaLab-2026/html/layout/nav.html | 2 +- templates/MetaLab-2026/static/js/register.js | 2 +- templates/admin/html/layout/base.html | 4 +-- templates/admin/html/site-settings/index.html | 34 +++++++++++++++++-- 21 files changed, 106 insertions(+), 35 deletions(-) diff --git a/config.yaml b/config.yaml index 1c07de1..1995c07 100644 --- a/config.yaml +++ b/config.yaml @@ -4,6 +4,7 @@ server: port: 8080 mode: debug # debug | release | test + cookie_secure: false # Cookie Secure 标志(生产环境应设为 true,部分反向代理环境下可能需要灵活控制) database: host: 127.0.0.1 diff --git a/internal/common/cookie.go b/internal/common/cookie.go index 5d05182..0bcd15b 100644 --- a/internal/common/cookie.go +++ b/internal/common/cookie.go @@ -17,8 +17,8 @@ const ( // SetSessionCookie 写入会话 ID Cookie // rememberMe=true → Cookie maxAge=30天,关浏览器后仍保持登录 // rememberMe=false → Cookie maxAge=空闲超时(默认2h),与Redis TTL一致,关浏览器后自动清除 -func SetSessionCookie(c *gin.Context, sid string, rememberMe bool, cfg *config.Config) { - secure := cfg.Server.Mode != "debug" +func SetSessionCookie(c *gin.Context, sid string, rememberMe bool, cfg *config.Config, siteSettings *config.SiteSettings) { + secure := siteSettings.CookieSecure() c.SetSameSite(http.SameSiteLaxMode) var maxAge int @@ -33,8 +33,8 @@ func SetSessionCookie(c *gin.Context, sid string, rememberMe bool, cfg *config.C } // ClearSessionCookie 清除会话 ID Cookie(logout 调用) -func ClearSessionCookie(c *gin.Context, cfg *config.Config) { - secure := cfg.Server.Mode != "debug" +func ClearSessionCookie(c *gin.Context, cfg *config.Config, siteSettings *config.SiteSettings) { + secure := siteSettings.CookieSecure() c.SetCookie(SessionCookieName, "", -1, "/", "", secure, true) } diff --git a/internal/common/helper.go b/internal/common/helper.go index 7ec9627..6f4860c 100644 --- a/internal/common/helper.go +++ b/internal/common/helper.go @@ -11,6 +11,7 @@ import ( "path/filepath" "regexp" "strconv" + "time" "metazone.cc/mce/internal/model" @@ -159,10 +160,20 @@ func RedirectToLogin(c *gin.Context) { // injectSiteInfo 从 context 注入站点展示信息 func injectSiteInfo(c *gin.Context, data gin.H) { + if siteName, exists := c.Get("site_name"); exists { + data["SiteName"] = siteName + } + if siteTagline, exists := c.Get("site_tagline"); exists { + data["SiteTagline"] = siteTagline + } + if contactEmail, exists := c.Get("site_contact_email"); exists { + data["ContactEmail"] = contactEmail + } if framework, exists := c.Get("site_framework"); exists { data["Framework"] = framework } if copyrightStart, exists := c.Get("site_copyright_start"); exists { data["CopyrightStart"] = copyrightStart } + data["CurrentYear"] = time.Now().Year() } diff --git a/internal/config/config.go b/internal/config/config.go index 1186d77..3b3055b 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -29,8 +29,9 @@ type AuditConfig struct { } type ServerConfig struct { - Port string `mapstructure:"port"` - Mode string `mapstructure:"mode"` + Port string `mapstructure:"port"` + Mode string `mapstructure:"mode"` + CookieSecure bool `mapstructure:"cookie_secure"` } type DatabaseConfig struct { diff --git a/internal/config/site_settings.go b/internal/config/site_settings.go index 65d3010..f6e847c 100644 --- a/internal/config/site_settings.go +++ b/internal/config/site_settings.go @@ -112,18 +112,29 @@ func (s *SiteSettings) IsAuditTypeEnabled(auditType string, defaultVal bool) boo // SiteInfoDefaults 站点展示信息的默认值 type SiteInfoDefaults struct { - Framework string // 附加标识,默认 "Framework: MetaZone" + SiteName string // 站点名称,默认 "MetaLab" + SiteTagline string // 站点标语,默认 "下一代开发者社区" + ContactEmail string // 联系/申诉邮箱,默认 "metazone@foxmail.com" + Framework string // 附加标识,默认 "Framework: MetaGenesis" CopyrightStart string // 版权起始年份,默认 "2024" } // SiteInfo 获取站点展示信息(优先 DB 设置,fallback 默认值) func (s *SiteSettings) SiteInfo() SiteInfoDefaults { return SiteInfoDefaults{ - Framework: s.Get("site.framework", "Framework: MetaZone"), + SiteName: s.Get("site.name", "MetaLab"), + SiteTagline: s.Get("site.tagline", "下一代开发者社区"), + ContactEmail: s.Get("site.contact_email", "metazone@foxmail.com"), + Framework: s.Get("site.framework", "Framework: MetaGenesis"), CopyrightStart: s.Get("site.copyright_start", "2024"), } } +// CookieSecure 返回 cookie_secure 配置值(优先站点设置,fallback config.yaml) +func (s *SiteSettings) CookieSecure() bool { + return s.GetBool("site.cookie_secure", App.Server.CookieSecure) +} + // IsRegistrationEnabled 是否允许新用户注册 func (s *SiteSettings) IsRegistrationEnabled() bool { return s.GetBool("registration.enabled", true) diff --git a/internal/controller/auth_api_login.go b/internal/controller/auth_api_login.go index c989dc7..19ef937 100644 --- a/internal/controller/auth_api_login.go +++ b/internal/controller/auth_api_login.go @@ -78,7 +78,7 @@ func (ac *AuthController) Login(c *gin.Context) { return } - common.SetSessionCookie(c, sid, req.RememberMe, ac.cfg) + common.SetSessionCookie(c, sid, req.RememberMe, ac.cfg, ac.siteSettings) common.OkWithMessage(c, user, "登录成功") } @@ -108,6 +108,6 @@ func (ac *AuthController) ConfirmRestore(c *gin.Context) { return } - common.SetSessionCookie(c, sid, req.RememberMe, ac.cfg) + common.SetSessionCookie(c, sid, req.RememberMe, ac.cfg, ac.siteSettings) common.OkWithMessage(c, user, "注销已撤销,欢迎回来") } diff --git a/internal/controller/auth_api_register.go b/internal/controller/auth_api_register.go index 06a2f51..3db1b2b 100644 --- a/internal/controller/auth_api_register.go +++ b/internal/controller/auth_api_register.go @@ -69,9 +69,9 @@ func (ac *AuthController) Register(c *gin.Context) { return } - common.SetSessionCookie(c, sid, req.RememberMe, ac.cfg) + common.SetSessionCookie(c, sid, req.RememberMe, ac.cfg, ac.siteSettings) - common.OkWithMessage(c, user, "注册成功!欢迎加入 MetaLab") + common.OkWithMessage(c, user, "注册成功!欢迎加入 "+ac.siteSettings.SiteInfo().SiteName) } // Logout 退出登录:删除服务端 session + 清除 Cookie @@ -79,7 +79,7 @@ func (ac *AuthController) Logout(c *gin.Context) { if sid, err := c.Cookie(common.SessionCookieName); err == nil && sid != "" { _ = ac.sessionManager.Destroy(sid) } - common.ClearSessionCookie(c, ac.cfg) + common.ClearSessionCookie(c, ac.cfg, ac.siteSettings) common.OkMessage(c, "已退出登录") } @@ -93,7 +93,7 @@ func (ac *AuthController) RefreshToken(c *gin.Context) { s, err := ac.sessionManager.Validate(sid) if err != nil || s == nil { - common.ClearSessionCookie(c, ac.cfg) + common.ClearSessionCookie(c, ac.cfg, ac.siteSettings) common.Error(c, http.StatusUnauthorized, "登录凭证已失效,请重新登录") return } diff --git a/internal/controller/auth_controller.go b/internal/controller/auth_controller.go index 9fa2ecd..77e7109 100644 --- a/internal/controller/auth_controller.go +++ b/internal/controller/auth_controller.go @@ -3,6 +3,7 @@ package controller import ( "html/template" "net/http" + "strings" "metazone.cc/mce/internal/common" "metazone.cc/mce/internal/config" @@ -45,6 +46,12 @@ func (ac *AuthController) RegisterPage(c *gin.Context) { } guidelines = content } + // 替换准则中的硬编码站点名和邮箱 + siteInfo := ac.siteSettings.SiteInfo() + guidelines = template.HTML(strings.ReplaceAll( + strings.ReplaceAll(string(guidelines), "MetaLab", siteInfo.SiteName), + "metazone@foxmail.com", siteInfo.ContactEmail, + )) c.HTML(http.StatusOK, "auth/register.html", common.BuildPageData(c, gin.H{ "Title": "注册", "ExtraCSS": "/static/css/auth.css", diff --git a/internal/middleware/auth.go b/internal/middleware/auth.go index 4ff54d8..a7c2e43 100644 --- a/internal/middleware/auth.go +++ b/internal/middleware/auth.go @@ -22,6 +22,7 @@ type AuthMiddleware struct { cfg *config.Config sessionManager *session.Manager levelSvc autoCheckIner + siteSettings *config.SiteSettings } // NewAuthMiddleware 构造函数 @@ -29,6 +30,12 @@ func NewAuthMiddleware(cfg *config.Config, sm *session.Manager) *AuthMiddleware return &AuthMiddleware{cfg: cfg, sessionManager: sm} } +// WithSiteSettings 链式注入站点设置(用于 cookie_secure 运行时覆盖) +func (am *AuthMiddleware) WithSiteSettings(ss *config.SiteSettings) *AuthMiddleware { + am.siteSettings = ss + return am +} + // WithLevelService 链式注入等级服务(用于自动签到) func (am *AuthMiddleware) WithLevelService(svc autoCheckIner) *AuthMiddleware { am.levelSvc = svc @@ -60,7 +67,7 @@ func (am *AuthMiddleware) Required() gin.HandlerFunc { // abortUnauthorized 统一处理未认证:API 返回 JSON,页面请求跳转登录 func (am *AuthMiddleware) abortUnauthorized(c *gin.Context) { - common.ClearSessionCookie(c, am.cfg) + common.ClearSessionCookie(c, am.cfg, am.siteSettings) if strings.HasPrefix(c.Request.URL.Path, "/api/") { c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{ "success": false, "message": "登录已过期,请重新登录", @@ -81,7 +88,7 @@ func (am *AuthMiddleware) Optional() gin.HandlerFunc { s, err := am.sessionManager.Validate(sid) if err != nil || s == nil { - common.ClearSessionCookie(c, am.cfg) + common.ClearSessionCookie(c, am.cfg, am.siteSettings) c.Next() return } diff --git a/internal/middleware/auth_admin.go b/internal/middleware/auth_admin.go index 9676713..408ad40 100644 --- a/internal/middleware/auth_admin.go +++ b/internal/middleware/auth_admin.go @@ -14,7 +14,7 @@ func (am *AuthMiddleware) AdminAuth() gin.HandlerFunc { return func(c *gin.Context) { sid, err := c.Cookie(common.SessionCookieName) if err != nil || sid == "" { - common.ClearSessionCookie(c, am.cfg) + common.ClearSessionCookie(c, am.cfg, am.siteSettings) c.Redirect(http.StatusFound, "/") c.Abort() return @@ -23,7 +23,7 @@ func (am *AuthMiddleware) AdminAuth() gin.HandlerFunc { s, err := am.sessionManager.Validate(sid) if err != nil || s == nil { log.Printf("[AdminAuth] session invalid path=%s → 302", c.Request.URL.Path) - common.ClearSessionCookie(c, am.cfg) + common.ClearSessionCookie(c, am.cfg, am.siteSettings) c.Redirect(http.StatusFound, "/") c.Abort() return diff --git a/internal/middleware/site_info.go b/internal/middleware/site_info.go index 1128704..0f1294f 100644 --- a/internal/middleware/site_info.go +++ b/internal/middleware/site_info.go @@ -11,6 +11,9 @@ import ( func InjectSiteInfo(siteSettings *config.SiteSettings) gin.HandlerFunc { return func(c *gin.Context) { info := siteSettings.SiteInfo() + c.Set("site_name", info.SiteName) + c.Set("site_tagline", info.SiteTagline) + c.Set("site_contact_email", info.ContactEmail) c.Set("site_framework", info.Framework) c.Set("site_copyright_start", info.CopyrightStart) c.Set("is_maintenance", siteSettings.IsMaintenanceEnabled()) diff --git a/internal/router/deps_core.go b/internal/router/deps_core.go index c3d1e3d..43a51bd 100644 --- a/internal/router/deps_core.go +++ b/internal/router/deps_core.go @@ -80,7 +80,7 @@ func buildDepsCore(db *gorm.DB, cfg *config.Config, siteSettings *config.SiteSet rateLimiter := middleware.NewRateLimiter() authCtrl := controller.NewAuthController(authService, sessionMgr, rateLimiter, cfg, siteSettings) avatarSvc := service.NewAvatarService(userRepo) - authMdw := middleware.NewAuthMiddleware(cfg, sessionMgr) + authMdw := middleware.NewAuthMiddleware(cfg, sessionMgr).WithSiteSettings(siteSettings) return userRepo, sessionMgr, authService, rateLimiter, authCtrl, avatarSvc, authMdw } diff --git a/templates/MetaLab-2026/html/auth/login.html b/templates/MetaLab-2026/html/auth/login.html index 6e0fa07..f311623 100644 --- a/templates/MetaLab-2026/html/auth/login.html +++ b/templates/MetaLab-2026/html/auth/login.html @@ -5,7 +5,7 @@
-

登录 MetaLab

+

登录 {{.SiteName}}

回到开发者社区

diff --git a/templates/MetaLab-2026/html/auth/register.html b/templates/MetaLab-2026/html/auth/register.html index ef95bf3..75849e7 100644 --- a/templates/MetaLab-2026/html/auth/register.html +++ b/templates/MetaLab-2026/html/auth/register.html @@ -13,7 +13,7 @@
{{else if .RegistrationEnabled}}

创建账号

-

加入 MetaLab 开发者社区

+

加入 {{.SiteName}} 开发者社区

@@ -38,7 +38,7 @@ 非共享设备推荐
- + {{else}} @@ -59,7 +59,7 @@