feat: 实现 Admin 首次初始化与网页登录 (#50)

This commit is contained in:
chengma
2026-08-09 13:36:41 +08:00
parent 1064ffed59
commit 0a7db404a1
17 changed files with 1140 additions and 12 deletions
+14 -5
View File
@@ -33,9 +33,17 @@ type Handler struct {
func Register(r *gin.Engine, db *sql.DB, onlineThreshold time.Duration) {
h := &Handler{db: db, onlineThreshold: onlineThreshold}
// CSRF 只挂在页面路由上。
// CSRF 只挂在页面路由上。初始化和登录是公开页面,但 POST 仍要 CSRF。
// 给 Client 的 /api/v1/client/* 绝不能加——它不是浏览器、没有 Cookie。
pages := r.Group("/", CSRFMiddleware())
public := r.Group("/", CSRFMiddleware())
public.GET("/setup", h.SetupPage)
public.POST("/setup", h.SetupSubmit)
public.GET("/login", h.LoginPage)
public.POST("/login", h.LoginSubmit)
// 登录中间件只挂业务网页组,绝不能挂在整个 Engine。
pages := r.Group("/", CSRFMiddleware(), AuthRequired(db))
pages.POST("/logout", h.Logout)
// 打开根路径直接进第一个模块
pages.GET("/", func(c *gin.Context) {
@@ -80,9 +88,10 @@ func Register(r *gin.Engine, db *sql.DB, onlineThreshold time.Duration) {
// page 组装每个页面都要的公共数据(导航高亮、标题、CSRF token)。
func page(c *gin.Context, active, title string, extra gin.H) gin.H {
data := gin.H{
"Active": active,
"Title": title,
"CSRFToken": csrfToken(c),
"Active": active,
"Title": title,
"CSRFToken": csrfToken(c),
"CurrentUser": currentUser(c),
}
for k, v := range extra {
data[k] = v