Files
olons_server/internal/httpapi/profile.go
T

161 lines
5.6 KiB
Go

package httpapi
import (
"errors"
"net/http"
accountsvc "git.118im.com/im_team/im_workbench/internal/accounts"
"git.118im.com/im_team/im_workbench/internal/auth"
"git.118im.com/im_team/im_workbench/internal/store"
)
type avatarRequest struct {
AttachmentID int64 `json:"attachment_id"`
}
type passwordRequest struct {
OldPassword string `json:"old_password"`
NewPassword string `json:"new_password"`
}
type tradePasswordRequest struct {
LoginPassword string `json:"login_password"`
OldTradePassword string `json:"old_trade_password,omitempty"`
NewTradePassword string `json:"new_trade_password"`
}
func (a *API) memberProfile(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet {
methodNotAllowed(w, r, http.MethodGet)
return
}
principal, ok := a.authenticateMember(w, r)
if !ok {
return
}
profile, err := a.accounts.Profile(r.Context(), principal.TenantID, principal.MemberID)
if err != nil {
writeProfileError(w, r, err)
return
}
writeSuccess(w, r, profile)
}
func (a *API) memberAvatar(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPatch {
methodNotAllowed(w, r, http.MethodPatch)
return
}
principal, ok := a.authenticateMember(w, r)
if !ok {
return
}
var body avatarRequest
if !decodeBody(w, r, &body) {
return
}
avatar, err := a.accounts.ChangeAvatar(r.Context(), principal.TenantID, principal.MemberID, body.AttachmentID)
if err != nil {
writeProfileError(w, r, err)
return
}
writeSuccess(w, r, avatar)
}
func (a *API) memberPassword(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPut {
methodNotAllowed(w, r, http.MethodPut)
return
}
principal, ok := a.authenticateMember(w, r)
if !ok {
return
}
var body passwordRequest
if !decodeBody(w, r, &body) {
return
}
err := a.accounts.ChangePassword(r.Context(), principal.TenantID, principal.MemberID, accountsvc.ChangePasswordInput{
OldPassword: body.OldPassword, NewPassword: body.NewPassword,
})
if err != nil {
writeProfileError(w, r, err)
return
}
if err := a.auth.RevokeMemberSessions(r.Context(), principal.TenantID, principal.MemberID); err != nil {
writeProfileError(w, r, err)
return
}
writeSuccess(w, r, nil)
}
func (a *API) memberTradePassword(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPut {
methodNotAllowed(w, r, http.MethodPut)
return
}
principal, ok := a.authenticateMember(w, r)
if !ok {
return
}
var body tradePasswordRequest
if !decodeBody(w, r, &body) {
return
}
err := a.accounts.ChangeTradePassword(r.Context(), principal.TenantID, principal.MemberID, accountsvc.ChangeTradePasswordInput{
LoginPassword: body.LoginPassword, OldTradePassword: body.OldTradePassword,
NewTradePassword: body.NewTradePassword,
})
if err != nil {
writeProfileError(w, r, err)
return
}
writeSuccess(w, r, nil)
}
func (a *API) authenticateMember(w http.ResponseWriter, r *http.Request) (*auth.Principal, bool) {
if a.auth == nil || a.accounts == nil {
writeError(w, r, http.StatusServiceUnavailable, 90001, "SERVICE_UNAVAILABLE", "service unavailable", nil)
return nil, false
}
principal, err := a.auth.Authenticate(r)
if err == nil {
return principal, true
}
if errors.Is(err, auth.ErrForbidden) {
writeError(w, r, http.StatusForbidden, 30003, reasonForbidden, "forbidden", nil)
} else if errors.Is(err, auth.ErrAuthRequired) {
writeError(w, r, http.StatusUnauthorized, 30041, "AUTH_REQUIRED", "authentication required", nil)
} else {
writeError(w, r, http.StatusInternalServerError, 90000, reasonInternal, "internal error", nil)
}
return nil, false
}
func writeProfileError(w http.ResponseWriter, r *http.Request, err error) {
switch {
case errors.Is(err, accountsvc.ErrAttachmentInvalid), errors.Is(err, store.ErrAttachmentInvalid):
writeError(w, r, http.StatusBadRequest, 30000, "AVATAR_ATTACHMENT_INVALID", "validation failed", map[string]any{"field": "attachment_id"})
case errors.Is(err, accountsvc.ErrCurrentPassword):
writeError(w, r, http.StatusBadRequest, 30000, "CURRENT_PASSWORD_INVALID", "current password is invalid", map[string]any{"field": "old_password"})
case errors.Is(err, accountsvc.ErrLoginPassword):
writeError(w, r, http.StatusBadRequest, 30000, "LOGIN_PASSWORD_INVALID", "login password is invalid", map[string]any{"field": "login_password"})
case errors.Is(err, accountsvc.ErrPasswordInvalid):
writeError(w, r, http.StatusBadRequest, 30000, "NEW_PASSWORD_INVALID", "validation failed", map[string]any{"field": "new_password"})
case errors.Is(err, accountsvc.ErrOldTradePasswordUnset):
writeError(w, r, http.StatusBadRequest, 30000, "OLD_TRADE_PASSWORD_REQUIRED", "old trade password is required", map[string]any{"field": "old_trade_password"})
case errors.Is(err, accountsvc.ErrOldTradePassword):
writeError(w, r, http.StatusBadRequest, 30000, "OLD_TRADE_PASSWORD_INVALID", "old trade password is invalid", map[string]any{"field": "old_trade_password"})
case errors.Is(err, accountsvc.ErrTradePassword):
writeError(w, r, http.StatusBadRequest, 30000, "TRADE_PASSWORD_INVALID", "trade password is invalid", map[string]any{"field": "new_trade_password"})
case errors.Is(err, store.ErrAccountNotFound):
writeError(w, r, http.StatusNotFound, 30044, "ACCOUNT_NOT_FOUND", "account not found", nil)
case errors.Is(err, store.ErrConcurrentUpdate):
writeError(w, r, http.StatusConflict, 30040, "ACCOUNT_CHANGED", "account changed, retry the operation", nil)
case errors.Is(err, accountsvc.ErrUnavailable):
writeError(w, r, http.StatusServiceUnavailable, 90001, "SERVICE_UNAVAILABLE", "service unavailable", nil)
default:
writeError(w, r, http.StatusInternalServerError, 90000, reasonInternal, "internal error", nil)
}
}