141 lines
3.2 KiB
Go
141 lines
3.2 KiB
Go
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))
|
||
|
|
}
|