feat: 社区准则改为数据库存储

- AuthController 注入 SiteSettings 依赖,RegisterPage 优先从 DB 读取准则
- DB 中 site.guidelines 为空时 fallback 到静态 guidelines.html 文件
- 管理后台站点设置页新增「内容管理」分区,textarea 编辑社区准则
- 添加准则编辑区垂直布局样式
This commit is contained in:
2026-06-02 20:17:23 +08:00
parent c73b131b83
commit 72c08f3689
5 changed files with 76 additions and 7 deletions

View File

@ -1,6 +1,7 @@
package controller
import (
"html/template"
"net/http"
"metazone.cc/metalab/internal/common"
@ -17,11 +18,12 @@ type AuthController struct {
sessionManager *session.Manager
rateLimiter rateLimiter
cfg *config.Config
siteSettings *config.SiteSettings
}
// NewAuthController 构造函数
func NewAuthController(authService authUseCase, sm *session.Manager, limiter rateLimiter, cfg *config.Config) *AuthController {
return &AuthController{authService: authService, sessionManager: sm, rateLimiter: limiter, cfg: cfg}
func NewAuthController(authService authUseCase, sm *session.Manager, limiter rateLimiter, cfg *config.Config, siteSettings *config.SiteSettings) *AuthController {
return &AuthController{authService: authService, sessionManager: sm, rateLimiter: limiter, cfg: cfg, siteSettings: siteSettings}
}
// RegisterPage 注册页面(已登录用户重定向到首页)
@ -30,11 +32,19 @@ func (ac *AuthController) RegisterPage(c *gin.Context) {
c.Redirect(http.StatusFound, "/")
return
}
guidelines, err := theme.LoadContent("templates/MetaLab-2026/guidelines.html")
// 优先从 DB 读取社区准则,为空时 fallback 到静态文件
guidelinesHTML := ac.siteSettings.Get("site.guidelines", "")
var guidelines template.HTML
if guidelinesHTML != "" {
guidelines = template.HTML(guidelinesHTML)
} else {
content, err := theme.LoadContent("templates/MetaLab-2026/guidelines.html")
if err != nil {
c.String(http.StatusInternalServerError, "加载准则失败")
return
}
guidelines = content
}
c.HTML(http.StatusOK, "auth/register.html", common.BuildPageData(c, gin.H{
"Title": "注册",
"ExtraCSS": "/static/css/auth.css",

View File

@ -78,7 +78,7 @@ func buildDepsCore(db *gorm.DB, cfg *config.Config, siteSettings *config.SiteSet
sessionMgr := session.NewManager(sessionStore, userRepo, cfg)
authService := service.NewAuthService(userRepo, sessionMgr, cfg, siteSettings)
rateLimiter := middleware.NewRateLimiter()
authCtrl := controller.NewAuthController(authService, sessionMgr, rateLimiter, cfg)
authCtrl := controller.NewAuthController(authService, sessionMgr, rateLimiter, cfg, siteSettings)
avatarSvc := service.NewAvatarService(userRepo)
authMdw := middleware.NewAuthMiddleware(cfg, sessionMgr)
return userRepo, sessionMgr, authService, rateLimiter, authCtrl, avatarSvc, authMdw

View File

@ -115,6 +115,23 @@
</div>
</div>
<!-- 内容管理 -->
<div class="settings-section">
<div class="section-title">内容管理</div>
<div class="section-body">
<div class="setting-item setting-item-vertical">
<div class="setting-info">
<span class="setting-label">社区准则</span>
<span class="setting-desc">注册时需用户阅读并同意,支持 HTML 格式</span>
</div>
<div class="setting-input-block">
<textarea id="setting-site-guidelines" class="setting-textarea" rows="12" placeholder="留空则使用默认准则文件"></textarea>
<button class="btn btn-sm btn-primary btn-save-block" data-key="site.guidelines" data-input="setting-site-guidelines">保存准则</button>
</div>
</div>
</div>
</div>
<!-- 通用设置 -->
<div class="settings-section">
<div class="section-title">其他设置</div>

View File

@ -58,6 +58,35 @@
.btn-save { width: 56px; text-align: center; }
.btn-save:disabled { opacity: 0.5; cursor: not-allowed; }
/* --- 垂直布局设置项(用于准则等大文本) --- */
.setting-item-vertical {
flex-direction: column; align-items: stretch;
}
.setting-item-vertical .setting-info {
margin-bottom: 10px;
}
.setting-input-block {
display: flex; flex-direction: column; gap: 10px;
}
.setting-textarea {
padding: 10px 12px;
border: 1px solid var(--border);
border-radius: 4px;
font-size: 13px; line-height: 1.6;
font-family: 'Courier New', Consolas, monospace;
resize: vertical;
width: 100%;
box-sizing: border-box;
outline: none;
transition: border-color 0.2s;
}
.setting-textarea:focus { border-color: var(--primary); }
.btn-save-block {
align-self: flex-end;
min-width: 80px;
}
.btn-save-block:disabled { opacity: 0.5; cursor: not-allowed; }
/* --- 空状态 --- */
.settings-empty {
text-align: center; padding: 28px 20px;

View File

@ -18,6 +18,7 @@
// 已有专属 UI 的设置 key不在"其他设置"中重复显示
var DEDICATED_KEYS = {
'site.framework': true, 'site.copyright_start': true,
'site.guidelines': true,
'maintenance.enabled': true,
'registration.enabled': true,
'audit.enabled': true, 'audit.username_audit': true, 'audit.avatar_audit': true, 'audit.bio_audit': true
@ -51,6 +52,18 @@
// 文本输入保存按钮
bindSaveButtons();
// 社区准则文本域
var guidelinesEl = document.getElementById('setting-site-guidelines');
if (guidelinesEl) {
guidelinesEl.value = allSettings['site.guidelines'] || '';
var guidelinesBtn = document.querySelector('[data-key="site.guidelines"]');
if (guidelinesBtn) {
guidelinesBtn.addEventListener('click', function() {
updateTextSetting('site.guidelines', guidelinesEl.value, guidelinesBtn);
});
}
}
// 其他设置过滤已有专属UI的key
renderOtherSettings(allSettings);
} catch (e) {