package web import ( "context" "errors" "net/http" "strings" "git.arnef.de/arnef/nidus/internal/db" "git.arnef.de/arnef/nidus/internal/web/templates" ) // handleAccount serves GET /account: the current user's own profile and // password-change forms, reached by clicking the username in the nav. func (s *Server) handleAccount(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodGet { w.Header().Set("Allow", "GET") http.Error(w, "method not allowed", http.StatusMethodNotAllowed) return } username := userFromContext(r.Context()) data, err := s.accountData(username) if err != nil { s.logger.Error("loading account", "error", err) http.Error(w, "internal error", http.StatusInternalServerError) return } w.Header().Set("Content-Type", "text/html; charset=utf-8") _ = templates.Account(data).Render(context.Background(), w) } // accountData loads the current profile fields for username, with the // message fields left empty (used for the initial GET render). func (s *Server) accountData(username string) (templates.AccountData, error) { u, err := s.dbase.GetUser(username) if err != nil { return templates.AccountData{}, err } return templates.AccountData{ Username: u.Username, DisplayName: u.DisplayName, Email: u.Email, }, nil } // handleAccountProfile handles POST /account/profile: updates the // current user's display name and email, then re-renders the account // page with a success or error message. func (s *Server) handleAccountProfile(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { w.Header().Set("Allow", "POST") http.Error(w, "method not allowed", http.StatusMethodNotAllowed) return } username := userFromContext(r.Context()) data, err := s.accountData(username) if err != nil { s.logger.Error("loading account", "error", err) http.Error(w, "internal error", http.StatusInternalServerError) return } if err := r.ParseForm(); err != nil { data.ProfileMsg, data.ProfileErr = "invalid form submission", true s.renderAccount(w, data) return } displayName := strings.TrimSpace(r.PostForm.Get("display_name")) email := strings.TrimSpace(r.PostForm.Get("email")) if err := s.dbase.SetProfile(username, displayName, email); err != nil { s.logger.Error("updating profile", "error", err) data.DisplayName, data.Email = displayName, email data.ProfileMsg, data.ProfileErr = "could not update profile", true s.renderAccount(w, data) return } data.DisplayName, data.Email = displayName, email data.ProfileMsg = "Profile updated." s.renderAccount(w, data) } // handleAccountPassword handles POST /account/password: verifies the // current password, then updates it to the requested new password, and // re-renders the account page with a success or error message. func (s *Server) handleAccountPassword(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { w.Header().Set("Allow", "POST") http.Error(w, "method not allowed", http.StatusMethodNotAllowed) return } username := userFromContext(r.Context()) data, err := s.accountData(username) if err != nil { s.logger.Error("loading account", "error", err) http.Error(w, "internal error", http.StatusInternalServerError) return } if err := r.ParseForm(); err != nil { data.PasswordMsg, data.PasswordErr = "invalid form submission", true s.renderAccount(w, data) return } current := r.PostForm.Get("current_password") newPassword := r.PostForm.Get("new_password") confirm := r.PostForm.Get("confirm_password") if !s.dbase.VerifyPassword(username, current) { data.PasswordMsg, data.PasswordErr = "current password is incorrect", true s.renderAccount(w, data) return } if len(newPassword) < 8 { data.PasswordMsg, data.PasswordErr = "new password must be at least 8 characters", true s.renderAccount(w, data) return } if newPassword != confirm { data.PasswordMsg, data.PasswordErr = "new password and confirmation don't match", true s.renderAccount(w, data) return } if err := s.dbase.SetPassword(username, newPassword); err != nil && !errors.Is(err, db.ErrUserNotFound) { s.logger.Error("changing password", "error", err) data.PasswordMsg, data.PasswordErr = "could not change password", true s.renderAccount(w, data) return } data.PasswordMsg = "Password changed." s.renderAccount(w, data) } func (s *Server) renderAccount(w http.ResponseWriter, data templates.AccountData) { w.Header().Set("Content-Type", "text/html; charset=utf-8") _ = templates.Account(data).Render(context.Background(), w) }