Files
subtrack/internal/web/handlers_auth.go
T
chun_qiu f047662129
build / build-and-test (push) Failing after 1m24s
首次推送
2026-10-18 19:44:38 +08:00

120 lines
3.5 KiB
Go

package web
import (
"net/http"
"strings"
"daili/internal/store"
)
func (s *Server) handleSetupPage(w http.ResponseWriter, r *http.Request) {
n, err := s.db.CountUsers()
if err != nil {
http.Error(w, "数据库异常", http.StatusInternalServerError)
return
}
if n > 0 {
http.Redirect(w, r, "/login", http.StatusFound)
return
}
p := s.newPage(r, "初始化管理员", "")
s.render(w, r, "setup", p)
}
func (s *Server) handleSetupPost(w http.ResponseWriter, r *http.Request) {
n, _ := s.db.CountUsers()
if n > 0 {
http.Redirect(w, r, "/login", http.StatusFound)
return
}
if !sameOrigin(r) {
http.Error(w, "跨站请求被拒绝", http.StatusForbidden)
return
}
username := strings.TrimSpace(r.PostFormValue("username"))
display := strings.TrimSpace(r.PostFormValue("display_name"))
pw := r.PostFormValue("password")
pw2 := r.PostFormValue("password2")
p := s.newPage(r, "初始化管理员", "")
switch {
case len(username) < 3:
s.fail(w, r, "setup", "用户名至少 3 个字符", p)
return
case len(pw) < 6:
s.fail(w, r, "setup", "密码至少 6 位", p)
return
case pw != pw2:
s.fail(w, r, "setup", "两次输入的密码不一致", p)
return
}
if display == "" {
display = username
}
u, err := s.db.CreateUser(username, display, pw)
if err != nil {
s.fail(w, r, "setup", "创建失败:"+err.Error(), p)
return
}
_ = s.db.Log(store.OpLog{OperatorID: u.ID, OperatorName: u.DisplayName, Action: "创建管理员",
Entity: "user", EntityID: u.ID, Summary: "初始化管理员账号 " + username, IP: clientIP(r)})
s.startSession(w, r, u)
s.redirect(w, r, "/packages", "管理员已创建,先添加一个服务套餐吧")
}
func (s *Server) handleLoginPage(w http.ResponseWriter, r *http.Request) {
n, _ := s.db.CountUsers()
if n == 0 {
http.Redirect(w, r, "/setup", http.StatusFound)
return
}
if s.userFromRequest(r) != nil {
http.Redirect(w, r, "/calendar", http.StatusFound)
return
}
p := s.newPage(r, "登录", "")
s.render(w, r, "login", p)
}
func (s *Server) handleLoginPost(w http.ResponseWriter, r *http.Request) {
if !sameOrigin(r) {
http.Error(w, "跨站请求被拒绝", http.StatusForbidden)
return
}
username := strings.TrimSpace(r.PostFormValue("username"))
pw := r.PostFormValue("password")
next := r.PostFormValue("next")
if !strings.HasPrefix(next, "/") || strings.HasPrefix(next, "//") {
next = "/calendar"
}
u, err := s.db.Authenticate(username, pw)
if err != nil {
p := s.newPage(r, "登录", "")
s.fail(w, r, "login", "用户名或密码不正确", p)
return
}
_ = s.db.Log(store.OpLog{OperatorID: u.ID, OperatorName: u.DisplayName, Action: "登录",
Entity: "session", Summary: "登录成功", IP: clientIP(r)})
s.startSession(w, r, u)
s.redirect(w, r, next, "欢迎回来,"+u.DisplayName)
}
func (s *Server) handleLogout(w http.ResponseWriter, r *http.Request) {
if c, err := r.Cookie(cookieSession); err == nil && c.Value != "" {
_ = s.db.DeleteSession(c.Value)
}
http.SetCookie(w, &http.Cookie{Name: cookieSession, Value: "", Path: "/", MaxAge: -1, HttpOnly: true})
http.Redirect(w, r, "/login", http.StatusSeeOther)
}
// logOp 是写操作日志的便捷方法。
func (s *Server) logOp(r *http.Request, action, entity string, entityID int64, summary string) {
u := s.currentUser(r)
if u == nil {
return
}
_ = s.db.Log(store.OpLog{
OperatorID: u.ID, OperatorName: u.DisplayName, Action: action,
Entity: entity, EntityID: entityID, Summary: summary, IP: clientIP(r),
})
}