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), }) }