package web import ( "net/http" "strconv" "uPortal/internal/auth" "uPortal/internal/user" ) // HandleProfile shows the current user's own profile. func (s *WebService) HandleProfile(w http.ResponseWriter, r *http.Request) { data, ok := s.basicViewData(w, r, "Profile") if !ok { http.Redirect(w, r, "/signin", http.StatusSeeOther) return } full, err := s.users.GetUserById(r.Context(), data.User.Id) if err != nil { http.Error(w, "Internal Server Error", http.StatusInternalServerError) return } data.User = full s.render(w, r, "profile/view.html", data) } // HandleProfileEdit renders the edit form on GET and applies changes on POST. func (s *WebService) HandleProfileEdit(w http.ResponseWriter, r *http.Request) { data, ok := s.basicViewData(w, r, "Edit Profile") if !ok { http.Redirect(w, r, "/signin", http.StatusSeeOther) return } switch r.Method { case http.MethodGet: s.render(w, r, "profile/edit.html", data) case http.MethodPost: if err := r.ParseMultipartForm(32 << 20); err != nil { data.Error = "Could not parse form" s.render(w, r, "profile/edit.html", data) return } if err := auth.ValidateCsrf(r, CsrfCookieName, CsrfFieldName); err != nil { data.Error = "Invalid form, please try again" s.render(w, r, "profile/edit.html", data) return } // Display name: UpdateUser writes the displayname column via the user row. displayName := r.FormValue("display_name") if displayName != "" { data.User.DisplayName = &displayName if err := s.users.UpdateUser(r.Context(), data.User); err != nil { data.Error = "Could not save display name" s.render(w, r, "profile/edit.html", data) return } } // Avatar: upload a JPG/PNG <= 256x256 (validated by the user package). if _, _, err := r.FormFile("avatar"); err == nil { bytes, _, err := user.ExtractUploadedImageBytes(r, "avatar") if err != nil { data.Error = "Invalid avatar image: " + err.Error() s.render(w, r, "profile/edit.html", data) return } if err := s.users.CreateAvatar(r.Context(), data.User.Id, bytes); err != nil { data.Error = "Could not save avatar" s.render(w, r, "profile/edit.html", data) return } } http.Redirect(w, r, "/profile", http.StatusSeeOther) } } // HandleProfilePassword renders the password change form on GET and // applies the new password on POST. func (s *WebService) HandleProfilePassword(w http.ResponseWriter, r *http.Request) { data, ok := s.basicViewData(w, r, "Change Password") if !ok { http.Redirect(w, r, "/signin", http.StatusSeeOther) return } switch r.Method { case http.MethodGet: s.render(w, r, "profile/password.html", data) case http.MethodPost: if err := r.ParseForm(); err != nil { data.Error = "Could not parse form" s.render(w, r, "profile/password.html", data) return } if err := auth.ValidateCsrf(r, CsrfCookieName, CsrfFieldName); err != nil { data.Error = "Invalid form token" s.render(w, r, "profile/password.html", data) return } oldPassword := r.FormValue("old_password") newPassword := r.FormValue("new_password") confirmPassword := r.FormValue("confirm_password") if !auth.CheckPasswordHash(oldPassword, data.User.Password) { data.Error = "Current password is incorrect" s.render(w, r, "profile/password.html", data) return } if len(newPassword) < 6 { data.Error = "New password must be at least 6 characters" s.render(w, r, "profile/password.html", data) return } if newPassword != confirmPassword { data.Error = "New passwords do not match" s.render(w, r, "profile/password.html", data) return } hashedPassword, err := auth.HashPassword(newPassword) if err != nil { data.Error = "Could not hash password" s.render(w, r, "profile/password.html", data) return } data.User.Password = hashedPassword if err := s.users.UpdateUser(r.Context(), data.User); err != nil { data.Error = "Could not update password" s.render(w, r, "profile/password.html", data) return } http.Redirect(w, r, "/profile", http.StatusSeeOther) } } // HandleAvatar serves a stored avatar image by user id. func (s *WebService) HandleAvatar(w http.ResponseWriter, r *http.Request) { id, err := strconv.ParseInt(r.PathValue("id"), 10, 64) if err != nil { http.NotFound(w, r) return } data, err := s.users.GetAvatar(r.Context(), id) if err != nil || len(data) == 0 { http.NotFound(w, r) return } w.Header().Set("Content-Type", http.DetectContentType(data)) w.Write(data) }