106 lines
3.5 KiB
Go
106 lines
3.5 KiB
Go
package response
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"net/http"
|
|
|
|
"apps/backend/internal/domain"
|
|
memberDomain "apps/backend/internal/module/member/domain"
|
|
tokenDomain "apps/backend/internal/module/token/domain"
|
|
|
|
"github.com/zeromicro/go-zero/rest/httpx"
|
|
)
|
|
|
|
// Envelope is the standard JSON body (AGENTS.md).
|
|
type Envelope struct {
|
|
Code int64 `json:"code"`
|
|
Message string `json:"message"`
|
|
Data interface{} `json:"data"`
|
|
Error interface{} `json:"error"`
|
|
}
|
|
|
|
// Write is used by goctl-generated handlers.
|
|
func Write(ctx context.Context, w http.ResponseWriter, data interface{}, err error) {
|
|
if err != nil {
|
|
status, env := mapError(err)
|
|
httpx.WriteJsonCtx(ctx, w, status, env)
|
|
return
|
|
}
|
|
httpx.WriteJsonCtx(ctx, w, http.StatusOK, Envelope{
|
|
Code: domain.SuccessCode,
|
|
Message: domain.SuccessMessage,
|
|
Data: data,
|
|
Error: nil,
|
|
})
|
|
}
|
|
|
|
func WrapRequestError(err error) error {
|
|
if err == nil {
|
|
return nil
|
|
}
|
|
return &bizError{http: http.StatusBadRequest, code: 400001, msg: err.Error()}
|
|
}
|
|
|
|
type bizError struct {
|
|
http int
|
|
code int64
|
|
msg string
|
|
}
|
|
|
|
func (e *bizError) Error() string { return e.msg }
|
|
|
|
func Biz(httpStatus int, code int64, msg string) error {
|
|
return &bizError{http: httpStatus, code: code, msg: msg}
|
|
}
|
|
|
|
func mapError(err error) (int, Envelope) {
|
|
var be *bizError
|
|
if errors.As(err, &be) {
|
|
return be.http, Envelope{Code: be.code, Message: be.msg}
|
|
}
|
|
switch {
|
|
case errors.Is(err, memberDomain.ErrBadPassword):
|
|
return http.StatusUnauthorized, Envelope{Code: 401010, Message: "invalid email or password"}
|
|
case errors.Is(err, memberDomain.ErrSuspended):
|
|
return http.StatusForbidden, Envelope{Code: 403001, Message: "account suspended"}
|
|
case errors.Is(err, memberDomain.ErrEmailTaken):
|
|
return http.StatusConflict, Envelope{Code: 409001, Message: "email already registered"}
|
|
case errors.Is(err, memberDomain.ErrWeakPassword):
|
|
// 與 domain.PasswordPolicyEN、FE api.err.400003 / password.policy.hint 同一句
|
|
return http.StatusBadRequest, Envelope{Code: 400003, Message: memberDomain.PasswordPolicyEN}
|
|
|
|
case errors.Is(err, memberDomain.ErrInvalidCode):
|
|
return http.StatusBadRequest, Envelope{Code: 400004, Message: "invalid or expired code"}
|
|
case errors.Is(err, memberDomain.ErrEmailNotRegistered):
|
|
return http.StatusNotFound, Envelope{Code: 404002, Message: "email not registered"}
|
|
case errors.Is(err, memberDomain.ErrNotFound):
|
|
return http.StatusNotFound, Envelope{Code: 404001, Message: "not found"}
|
|
case errors.Is(err, memberDomain.ErrLastAdmin):
|
|
return http.StatusBadRequest, Envelope{Code: 400021, Message: err.Error()}
|
|
case errors.Is(err, memberDomain.ErrSelfSuspend):
|
|
return http.StatusBadRequest, Envelope{Code: 400020, Message: err.Error()}
|
|
case errors.Is(err, tokenDomain.ErrInvalidToken):
|
|
return http.StatusUnauthorized, Envelope{Code: 401002, Message: "invalid token"}
|
|
default:
|
|
return http.StatusInternalServerError, Envelope{Code: 500000, Message: err.Error()}
|
|
}
|
|
}
|
|
|
|
func OK(w http.ResponseWriter, data interface{}) {
|
|
Write(context.Background(), w, data, nil)
|
|
}
|
|
|
|
func Fail(w http.ResponseWriter, httpStatus int, code int64, message string, errDetail interface{}) {
|
|
if code == domain.SuccessCode {
|
|
code = 400000
|
|
}
|
|
httpx.WriteJson(w, httpStatus, Envelope{Code: code, Message: message, Data: nil, Error: errDetail})
|
|
}
|
|
|
|
func NotImplemented(w http.ResponseWriter, feature string) {
|
|
Fail(w, http.StatusNotImplemented, 501000, "not implemented: "+feature, map[string]string{"feature": feature})
|
|
}
|
|
|
|
func IsSuccessCode(code int64) bool { return code == domain.SuccessCode }
|