feat: 注册关闭时页面隐藏表单并显示友好提示,保留登录入口

- RegisterPage 传入 RegistrationEnabled 标志到模板
- 注册关闭时隐藏输入框和按钮,显示“当前暂不开放注册”提示
- 保留“已有账号?立即登录”链接
- API 层已有拦截(AuthService.Register 返回 403),无需修改
- authUseCase 接口新增 IsRegistrationEnabled 方法(ISP)
This commit is contained in:
2026-05-30 20:38:55 +08:00
parent 90d17ed4b1
commit fb2eff5f23
5 changed files with 38 additions and 5 deletions

View File

@ -23,7 +23,7 @@ func NewAuthController(authService authUseCase, tokenSvc tokenRefresher, limiter
return &AuthController{authService: authService, tokenService: tokenSvc, rateLimiter: limiter, cfg: cfg} return &AuthController{authService: authService, tokenService: tokenSvc, rateLimiter: limiter, cfg: cfg}
} }
// RegisterPage 注册页面(已登录用户重定向到首页,注册关闭时重定向到登录页 // RegisterPage 注册页面(已登录用户重定向到首页)
func (ac *AuthController) RegisterPage(c *gin.Context) { func (ac *AuthController) RegisterPage(c *gin.Context) {
if _, exists := c.Get("uid"); exists { if _, exists := c.Get("uid"); exists {
c.Redirect(http.StatusFound, "/") c.Redirect(http.StatusFound, "/")
@ -38,6 +38,7 @@ func (ac *AuthController) RegisterPage(c *gin.Context) {
"Title": "注册", "Title": "注册",
"ExtraCSS": "/static/css/auth.css", "ExtraCSS": "/static/css/auth.css",
"Guidelines": guidelines, "Guidelines": guidelines,
"RegistrationEnabled": ac.authService.IsRegistrationEnabled(),
})) }))
} }

View File

@ -6,12 +6,13 @@ import (
"metazone.cc/metalab/internal/service" "metazone.cc/metalab/internal/service"
) )
// authUseCase AuthController 对 AuthService 的最小依赖ISP4 个方法) // authUseCase AuthController 对 AuthService 的最小依赖ISP5 个方法)
type authUseCase interface { type authUseCase interface {
Register(req model.RegisterRequest, regIP string) (string, string, *model.User, error) Register(req model.RegisterRequest, regIP string) (string, string, *model.User, error)
Login(req model.LoginRequest, loginIP string) (string, string, *model.User, error) Login(req model.LoginRequest, loginIP string) (string, string, *model.User, error)
ConfirmRestore(req model.LoginRequest, loginIP string) (string, string, *model.User, error) ConfirmRestore(req model.LoginRequest, loginIP string) (string, string, *model.User, error)
CheckEmail(email string) (bool, error) CheckEmail(email string) (bool, error)
IsRegistrationEnabled() bool
} }
// tokenRefresher AuthController 对 TokenService 的最小依赖ISP1 个方法) // tokenRefresher AuthController 对 TokenService 的最小依赖ISP1 个方法)

View File

@ -52,6 +52,11 @@ func validatePassword(pw string) error {
return nil return nil
} }
// IsRegistrationEnabled 检查是否允许新用户注册
func (s *AuthService) IsRegistrationEnabled() bool {
return s.siteSettings.IsRegistrationEnabled()
}
// Register 注册 // Register 注册
// 流程:密码强度 → 邮箱查重 → 哈希 → 生成唯一用户名 → 创建 → access JWT + refresh JWT // 流程:密码强度 → 邮箱查重 → 哈希 → 生成唯一用户名 → 创建 → access JWT + refresh JWT
// rememberMe=true: refresh Cookie 持久化30天false: session cookie关浏览器即清除 // rememberMe=true: refresh Cookie 持久化30天false: session cookie关浏览器即清除

View File

@ -7,6 +7,7 @@
<div class="auth-card"> <div class="auth-card">
<h2>创建账号</h2> <h2>创建账号</h2>
<p class="auth-subtitle">加入 MetaLab 开发者社区</p> <p class="auth-subtitle">加入 MetaLab 开发者社区</p>
{{if .RegistrationEnabled}}
<form id="registerForm" novalidate> <form id="registerForm" novalidate>
<input type="hidden" name="csrf_token" value="{{.CSRFToken}}"> <input type="hidden" name="csrf_token" value="{{.CSRFToken}}">
<div class="form-group"> <div class="form-group">
@ -34,6 +35,12 @@
<p class="guidelines-link">点击注册即表示同意 <a href="javascript:void(0)" id="openGuidelines">《MetaLab 社区用户准则》</a></p> <p class="guidelines-link">点击注册即表示同意 <a href="javascript:void(0)" id="openGuidelines">《MetaLab 社区用户准则》</a></p>
<button type="submit" class="submit-btn" id="submitBtn">注 册</button> <button type="submit" class="submit-btn" id="submitBtn">注 册</button>
</form> </form>
{{else}}
<div class="registration-closed">
<p class="closed-message">当前暂不开放注册,敬请期待</p>
<p class="closed-subtitle">社区正在维护中,请稍后再来</p>
</div>
{{end}}
<div class="auth-footer"> <div class="auth-footer">
已有账号?<a href="/auth/login">立即登录</a> 已有账号?<a href="/auth/login">立即登录</a>
</div> </div>

View File

@ -36,6 +36,25 @@
margin-bottom: 2rem; margin-bottom: 2rem;
} }
/* ---------- Registration Closed Notice ---------- */
.registration-closed {
text-align: center;
padding: 2.5rem 1rem;
margin-bottom: .5rem;
}
.registration-closed .closed-message {
font-size: 1.05rem;
font-weight: 600;
color: var(--color-primary);
margin-bottom: .5rem;
}
.registration-closed .closed-subtitle {
font-size: .9rem;
color: var(--color-secondary);
}
/* ---------- Form ---------- */ /* ---------- Form ---------- */
.form-group { .form-group {
margin-bottom: 1.2rem; margin-bottom: 1.2rem;