package httpapi import ( "errors" "net/http" accountsvc "git.118im.com/im_team/olons_server/internal/accounts" "git.118im.com/im_team/olons_server/internal/auth" "git.118im.com/im_team/olons_server/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) } }