package rest import ( "errors" "io" "net/http" "goAuth/internal/user" ) type updateProfileRequest struct { FullName *string `json:"fullName"` Email *string `json:"email"` } type updatePasswordRequest struct { OldPassword string `json:"oldPassword"` NewPassword string `json:"newPassword"` } func (h *Handler) getProfile(w http.ResponseWriter, r *http.Request) { u := userFromRequest(r) writeJson(w, http.StatusOK, newUserResponse(u)) } func (h *Handler) updateProfile(w http.ResponseWriter, r *http.Request) { u := userFromRequest(r) var req updateProfileRequest if err := decodeJson(r, &req); err != nil { writeError(w, http.StatusBadRequest, "invalid request body") return } fullname := "" if req.FullName != nil { fullname = *req.FullName } else if u.FullName != nil { fullname = *u.FullName } email := "" if req.Email != nil { // An empty email is allowed so the profile can be updated (e.g. the // full name) without also providing an email address. if *req.Email != "" && !verifyEmail(*req.Email) { writeError(w, http.StatusBadRequest, "invalid email") return } email = *req.Email } else if u.Email != nil { email = *u.Email } updated, err := h.users.UpdateProfile(r.Context(), u, fullname, email) if err != nil { h.fail(w, err) return } writeJson(w, http.StatusOK, newUserResponse(updated)) } func (h *Handler) updatePassword(w http.ResponseWriter, r *http.Request) { u := userFromRequest(r) var req updatePasswordRequest if err := decodeJson(r, &req); err != nil { writeError(w, http.StatusBadRequest, "invalid request body") return } if !verifyPassword(req.NewPassword) { writeError(w, http.StatusBadRequest, "invalid new password") return } _, err := h.users.UpdatePassword(r.Context(), u, req.OldPassword, req.NewPassword) if err != nil { if errors.Is(err, user.ErrInvalidPassword) { writeError(w, http.StatusBadRequest, "old password is incorrect") return } h.fail(w, err) return } writeNoContent(w) } func (h *Handler) uploadAvatar(w http.ResponseWriter, r *http.Request) { u := userFromRequest(r) data, err := readAvatarUpload(r) if err != nil { writeError(w, http.StatusBadRequest, "invalid avatar upload") return } if err := verifyImage(data); err != nil { h.fail(w, err) return } if err := h.users.UpdateAvatar(r.Context(), u, data); err != nil { h.fail(w, err) return } writeNoContent(w) } func (h *Handler) getAvatar(w http.ResponseWriter, r *http.Request) { u := userFromRequest(r) data, err := h.users.Avatar(r.Context(), u) if err != nil { h.fail(w, err) return } w.Header().Set("Content-Type", http.DetectContentType(data)) w.Header().Set("Cache-Control", "private, max-age=300") w.WriteHeader(http.StatusOK) w.Write(data) } func readAvatarUpload(r *http.Request) ([]byte, error) { contentType := r.Header.Get("Content-Type") const multipart = "multipart/form-data" if len(contentType) >= len(multipart) && contentType[:len(multipart)] == multipart { file, _, err := r.FormFile("avatar") if err != nil { return nil, err } defer file.Close() return io.ReadAll(io.LimitReader(file, maxRequestBody)) } return io.ReadAll(io.LimitReader(r.Body, maxRequestBody)) }