feat: 品牌CMS配置化 + cookie_secure配置
- 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 化(后端+前端)
This commit is contained in:
@ -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)
|
||||
}
|
||||
|
||||
|
||||
@ -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()
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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, "注销已撤销,欢迎回来")
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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())
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user