From 67eb16c0f1535814a57af8244199b1371818e51e Mon Sep 17 00:00:00 2001 From: xmdhs Date: Fri, 24 Nov 2023 15:51:55 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8B=86=E5=88=86=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- handle/admin.go | 64 ++++++++++++++++++++++-------- handle/config.go | 11 ++++++ handle/email.go | 9 ----- handle/error.go | 61 ----------------------------- handle/handelerror/error.go | 71 +++++++++++++++++++++++++++++++++ handle/handle.go | 42 +------------------- handle/user.go | 78 ++++++++++++++++++++++++------------- server/route/route.go | 29 +++++++------- server/wire.go | 8 ++-- server/wire_gen.go | 28 ++++++++----- service/admin.go | 27 +++++++++++-- service/admin_test.go | 12 +++--- service/service.go | 4 ++ service/texture.go | 18 ++++++++- service/user.go | 38 ++++++++++++++---- service/user_test.go | 33 +++++++++------- service/web.go | 26 ++----------- 17 files changed, 325 insertions(+), 234 deletions(-) delete mode 100644 handle/email.go delete mode 100644 handle/error.go create mode 100644 handle/handelerror/error.go diff --git a/handle/admin.go b/handle/admin.go index d3befea..2a1a31b 100644 --- a/handle/admin.go +++ b/handle/admin.go @@ -5,27 +5,45 @@ import ( "log/slog" "net/http" "strconv" + "strings" "github.com/go-chi/chi/v5" + "github.com/go-playground/validator/v10" + "github.com/xmdhs/authlib-skin/handle/handelerror" "github.com/xmdhs/authlib-skin/model" + "github.com/xmdhs/authlib-skin/service" U "github.com/xmdhs/authlib-skin/utils" ) +type AdminHandel struct { + handleError *handelerror.HandleError + adminService *service.AdminService + validate *validator.Validate +} + +func NewAdminHandel(handleError *handelerror.HandleError, adminService *service.AdminService, validate *validator.Validate) *AdminHandel { + return &AdminHandel{ + handleError: handleError, + adminService: adminService, + validate: validate, + } +} + type tokenValue string const tokenKey = tokenValue("token") -func (h *Handel) NeedAuth(handle http.Handler) http.Handler { +func (h *AdminHandel) NeedAuth(handle http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { ctx := r.Context() token := h.getTokenbyAuthorization(ctx, w, r) if token == "" { return } - t, err := h.webService.Auth(ctx, token) + t, err := h.adminService.Auth(ctx, token) if err != nil { - h.handleErrorService(ctx, w, err) + h.handleError.Service(ctx, w, err) return } r = r.WithContext(context.WithValue(ctx, tokenKey, t)) @@ -33,20 +51,20 @@ func (h *Handel) NeedAuth(handle http.Handler) http.Handler { }) } -func (h *Handel) NeedAdmin(handle http.Handler) http.Handler { +func (h *AdminHandel) NeedAdmin(handle http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { ctx := r.Context() t := ctx.Value(tokenKey).(*model.TokenClaims) - err := h.webService.IsAdmin(ctx, t) + err := h.adminService.IsAdmin(ctx, t) if err != nil { - h.handleErrorService(ctx, w, err) + h.handleError.Service(ctx, w, err) return } handle.ServeHTTP(w, r) }) } -func (h *Handel) ListUser() http.HandlerFunc { +func (h *AdminHandel) ListUser() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { ctx := r.Context() page := r.FormValue("page") @@ -54,7 +72,7 @@ func (h *Handel) ListUser() http.HandlerFunc { if page != "" { p, err := strconv.Atoi(page) if err != nil { - h.handleError(ctx, w, "page 必须为数字", model.ErrInput, 400, slog.LevelDebug) + h.handleError.Error(ctx, w, "page 必须为数字", model.ErrInput, 400, slog.LevelDebug) return } if p == 0 { @@ -65,37 +83,37 @@ func (h *Handel) ListUser() http.HandlerFunc { email := r.FormValue("email") name := r.FormValue("name") - ul, uc, err := h.webService.ListUser(ctx, pagei, email, name) + ul, uc, err := h.adminService.ListUser(ctx, pagei, email, name) if err != nil { - h.handleErrorService(ctx, w, err) + h.handleError.Service(ctx, w, err) return } encodeJson(w, model.API[model.List[model.UserList]]{Data: model.List[model.UserList]{List: ul, Total: uc}}) } } -func (h *Handel) EditUser() http.HandlerFunc { +func (h *AdminHandel) EditUser() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { ctx := r.Context() uid := chi.URLParamFromCtx(ctx, "uid") if uid == "" { - h.handleError(ctx, w, "uid 为空", model.ErrInput, 400, slog.LevelDebug) + h.handleError.Error(ctx, w, "uid 为空", model.ErrInput, 400, slog.LevelDebug) return } uidi, err := strconv.Atoi(uid) if err != nil { - h.handleError(ctx, w, err.Error(), model.ErrInput, 400, slog.LevelDebug) + h.handleError.Error(ctx, w, err.Error(), model.ErrInput, 400, slog.LevelDebug) return } a, err := U.DeCodeBody[model.EditUser](r.Body, h.validate) if err != nil { - h.handleError(ctx, w, err.Error(), model.ErrInput, 400, slog.LevelDebug) + h.handleError.Error(ctx, w, err.Error(), model.ErrInput, 400, slog.LevelDebug) return } - err = h.webService.EditUser(ctx, a, uidi) + err = h.adminService.EditUser(ctx, a, uidi) if err != nil { - h.handleErrorService(ctx, w, err) + h.handleError.Service(ctx, w, err) return } encodeJson[any](w, model.API[any]{ @@ -103,3 +121,17 @@ func (h *Handel) EditUser() http.HandlerFunc { }) } } + +func (h *AdminHandel) getTokenbyAuthorization(ctx context.Context, w http.ResponseWriter, r *http.Request) string { + auth := r.Header.Get("Authorization") + if auth == "" { + h.handleError.Error(ctx, w, "缺少 Authorization", model.ErrAuth, 401, slog.LevelDebug) + return "" + } + al := strings.Split(auth, " ") + if len(al) != 2 || al[0] != "Bearer" { + h.handleError.Error(ctx, w, "Authorization 格式错误", model.ErrAuth, 401, slog.LevelDebug) + return "" + } + return al[1] +} diff --git a/handle/config.go b/handle/config.go index b3f2c49..b87a32b 100644 --- a/handle/config.go +++ b/handle/config.go @@ -5,8 +5,19 @@ import ( "net/http" "github.com/xmdhs/authlib-skin/model" + "github.com/xmdhs/authlib-skin/service" ) +type Handel struct { + webService *service.WebService +} + +func NewHandel(webService *service.WebService) *Handel { + return &Handel{ + webService: webService, + } +} + func (h *Handel) GetConfig() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { ctx := r.Context() diff --git a/handle/email.go b/handle/email.go deleted file mode 100644 index bc23572..0000000 --- a/handle/email.go +++ /dev/null @@ -1,9 +0,0 @@ -package handle - -import "net/http" - -func (h *Handel) SendVerifyCode() http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - - } -} diff --git a/handle/error.go b/handle/error.go deleted file mode 100644 index 834e30c..0000000 --- a/handle/error.go +++ /dev/null @@ -1,61 +0,0 @@ -package handle - -import ( - "context" - "encoding/json" - "errors" - "log/slog" - "net/http" - - "github.com/xmdhs/authlib-skin/model" - "github.com/xmdhs/authlib-skin/service" - "github.com/xmdhs/authlib-skin/service/auth" - "github.com/xmdhs/authlib-skin/service/captcha" -) - -func (h *Handel) handleErrorService(ctx context.Context, w http.ResponseWriter, err error) { - if errors.Is(err, service.ErrExistUser) { - h.handleError(ctx, w, err.Error(), model.ErrExistUser, 400, slog.LevelDebug) - return - } - if errors.Is(err, service.ErrExitsName) { - h.handleError(ctx, w, err.Error(), model.ErrExitsName, 400, slog.LevelDebug) - return - } - if errors.Is(err, service.ErrRegLimit) { - h.handleError(ctx, w, err.Error(), model.ErrRegLimit, 400, slog.LevelDebug) - return - } - if errors.Is(err, captcha.ErrCaptcha) { - h.handleError(ctx, w, err.Error(), model.ErrCaptcha, 400, slog.LevelDebug) - return - } - if errors.Is(err, service.ErrPassWord) { - h.handleError(ctx, w, err.Error(), model.ErrPassWord, 401, slog.LevelDebug) - return - } - if errors.Is(err, auth.ErrUserDisable) { - h.handleError(ctx, w, err.Error(), model.ErrUserDisable, 401, slog.LevelDebug) - return - } - if errors.Is(err, service.ErrNotAdmin) { - h.handleError(ctx, w, err.Error(), model.ErrNotAdmin, 401, slog.LevelDebug) - return - } - if errors.Is(err, auth.ErrTokenInvalid) { - h.handleError(ctx, w, err.Error(), model.ErrAuth, 401, slog.LevelDebug) - return - } - - h.handleError(ctx, w, err.Error(), model.ErrService, 500, slog.LevelWarn) -} - -func (h *Handel) handleError(ctx context.Context, w http.ResponseWriter, msg string, code model.APIStatus, httpcode int, level slog.Level) { - h.logger.Log(ctx, level, msg) - w.WriteHeader(httpcode) - b, err := json.Marshal(model.API[any]{Code: code, Msg: msg, Data: nil}) - if err != nil { - panic(err) - } - w.Write(b) -} diff --git a/handle/handelerror/error.go b/handle/handelerror/error.go new file mode 100644 index 0000000..85dcdd6 --- /dev/null +++ b/handle/handelerror/error.go @@ -0,0 +1,71 @@ +package handelerror + +import ( + "context" + "encoding/json" + "errors" + "log/slog" + "net/http" + + "github.com/xmdhs/authlib-skin/model" + "github.com/xmdhs/authlib-skin/service" + "github.com/xmdhs/authlib-skin/service/auth" + "github.com/xmdhs/authlib-skin/service/captcha" +) + +type HandleError struct { + logger *slog.Logger +} + +func NewHandleError(logger *slog.Logger) *HandleError { + return &HandleError{ + logger: logger, + } +} + +func (h *HandleError) Service(ctx context.Context, w http.ResponseWriter, err error) { + if errors.Is(err, service.ErrExistUser) { + h.Error(ctx, w, err.Error(), model.ErrExistUser, 400, slog.LevelDebug) + return + } + if errors.Is(err, service.ErrExitsName) { + h.Error(ctx, w, err.Error(), model.ErrExitsName, 400, slog.LevelDebug) + return + } + if errors.Is(err, service.ErrRegLimit) { + h.Error(ctx, w, err.Error(), model.ErrRegLimit, 400, slog.LevelDebug) + return + } + if errors.Is(err, captcha.ErrCaptcha) { + h.Error(ctx, w, err.Error(), model.ErrCaptcha, 400, slog.LevelDebug) + return + } + if errors.Is(err, service.ErrPassWord) { + h.Error(ctx, w, err.Error(), model.ErrPassWord, 401, slog.LevelDebug) + return + } + if errors.Is(err, auth.ErrUserDisable) { + h.Error(ctx, w, err.Error(), model.ErrUserDisable, 401, slog.LevelDebug) + return + } + if errors.Is(err, service.ErrNotAdmin) { + h.Error(ctx, w, err.Error(), model.ErrNotAdmin, 401, slog.LevelDebug) + return + } + if errors.Is(err, auth.ErrTokenInvalid) { + h.Error(ctx, w, err.Error(), model.ErrAuth, 401, slog.LevelDebug) + return + } + + h.Error(ctx, w, err.Error(), model.ErrService, 500, slog.LevelWarn) +} + +func (h *HandleError) Error(ctx context.Context, w http.ResponseWriter, msg string, code model.APIStatus, httpcode int, level slog.Level) { + h.logger.Log(ctx, level, msg) + w.WriteHeader(httpcode) + b, err := json.Marshal(model.API[any]{Code: code, Msg: msg, Data: nil}) + if err != nil { + panic(err) + } + w.Write(b) +} diff --git a/handle/handle.go b/handle/handle.go index 08f33da..339bd88 100644 --- a/handle/handle.go +++ b/handle/handle.go @@ -1,60 +1,22 @@ package handle import ( - "context" "encoding/json" "fmt" "io" - "log/slog" - "net/http" "net/netip" - "strings" - "github.com/go-playground/validator/v10" + "github.com/google/wire" "github.com/samber/lo" - "github.com/xmdhs/authlib-skin/config" "github.com/xmdhs/authlib-skin/model" - "github.com/xmdhs/authlib-skin/service" - "github.com/xmdhs/authlib-skin/service/email" ) -type Handel struct { - webService *service.WebService - validate *validator.Validate - emailService *email.Email - config config.Config - logger *slog.Logger -} - -func NewHandel(webService *service.WebService, validate *validator.Validate, - config config.Config, logger *slog.Logger, email *email.Email) *Handel { - return &Handel{ - webService: webService, - validate: validate, - config: config, - logger: logger, - emailService: email, - } -} +var HandelSet = wire.NewSet(NewUserHandel, NewAdminHandel, NewHandel) func encodeJson[T any](w io.Writer, m model.API[T]) { json.NewEncoder(w).Encode(m) } -func (h *Handel) getTokenbyAuthorization(ctx context.Context, w http.ResponseWriter, r *http.Request) string { - auth := r.Header.Get("Authorization") - if auth == "" { - h.handleError(ctx, w, "缺少 Authorization", model.ErrAuth, 401, slog.LevelDebug) - return "" - } - al := strings.Split(auth, " ") - if len(al) != 2 || al[0] != "Bearer" { - h.handleError(ctx, w, "Authorization 格式错误", model.ErrAuth, 401, slog.LevelDebug) - return "" - } - return al[1] -} - func getPrefix(ip string) (string, error) { ipa, err := netip.ParseAddr(ip) if err != nil { diff --git a/handle/user.go b/handle/user.go index 245e015..975decb 100644 --- a/handle/user.go +++ b/handle/user.go @@ -9,33 +9,55 @@ import ( "net/http" "github.com/go-chi/chi/v5" + "github.com/go-playground/validator/v10" + "github.com/xmdhs/authlib-skin/handle/handelerror" "github.com/xmdhs/authlib-skin/model" + "github.com/xmdhs/authlib-skin/service" "github.com/xmdhs/authlib-skin/utils" ) -func (h *Handel) Reg() http.HandlerFunc { +type UserHandel struct { + handleError *handelerror.HandleError + validate *validator.Validate + userService *service.UserSerice + logger *slog.Logger + textureService *service.TextureService +} + +func NewUserHandel(handleError *handelerror.HandleError, validate *validator.Validate, + userService *service.UserSerice, logger *slog.Logger, textureService *service.TextureService) *UserHandel { + return &UserHandel{ + handleError: handleError, + validate: validate, + userService: userService, + logger: logger, + textureService: textureService, + } +} + +func (h *UserHandel) Reg() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { ctx := r.Context() ip, err := utils.GetIP(r) if err != nil { - h.handleError(ctx, w, err.Error(), model.ErrInput, 400, slog.LevelDebug) + h.handleError.Error(ctx, w, err.Error(), model.ErrInput, 400, slog.LevelDebug) return } u, err := utils.DeCodeBody[model.UserReg](r.Body, h.validate) if err != nil { - h.handleError(ctx, w, err.Error(), model.ErrInput, 400, slog.LevelDebug) + h.handleError.Error(ctx, w, err.Error(), model.ErrInput, 400, slog.LevelDebug) return } rip, err := getPrefix(ip) if err != nil { - h.handleError(ctx, w, err.Error(), model.ErrUnknown, 500, slog.LevelWarn) + h.handleError.Error(ctx, w, err.Error(), model.ErrUnknown, 500, slog.LevelWarn) return } - lr, err := h.webService.Reg(ctx, u, rip, ip) + lr, err := h.userService.Reg(ctx, u, rip, ip) if err != nil { - h.handleErrorService(ctx, w, err) + h.handleError.Service(ctx, w, err) return } encodeJson(w, model.API[model.LoginRep]{ @@ -45,24 +67,24 @@ func (h *Handel) Reg() http.HandlerFunc { } } -func (h *Handel) Login() http.HandlerFunc { +func (h *UserHandel) Login() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { ctx := r.Context() ip, err := utils.GetIP(r) if err != nil { - h.handleError(ctx, w, err.Error(), model.ErrInput, 400, slog.LevelDebug) + h.handleError.Error(ctx, w, err.Error(), model.ErrInput, 400, slog.LevelDebug) return } l, err := utils.DeCodeBody[model.Login](r.Body, h.validate) if err != nil { - h.handleError(ctx, w, err.Error(), model.ErrInput, 400, slog.LevelDebug) + h.handleError.Error(ctx, w, err.Error(), model.ErrInput, 400, slog.LevelDebug) return } - lr, err := h.webService.Login(ctx, l, ip) + lr, err := h.userService.Login(ctx, l, ip) if err != nil { - h.handleErrorService(ctx, w, err) + h.handleError.Service(ctx, w, err) return } encodeJson(w, model.API[model.LoginRep]{ @@ -72,13 +94,13 @@ func (h *Handel) Login() http.HandlerFunc { } } -func (h *Handel) UserInfo() http.HandlerFunc { +func (h *UserHandel) UserInfo() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { ctx := r.Context() t := ctx.Value(tokenKey).(*model.TokenClaims) - u, err := h.webService.Info(ctx, t) + u, err := h.userService.Info(ctx, t) if err != nil { - h.handleErrorService(ctx, w, err) + h.handleError.Service(ctx, w, err) return } encodeJson(w, model.API[model.UserInfo]{ @@ -88,19 +110,19 @@ func (h *Handel) UserInfo() http.HandlerFunc { } } -func (h *Handel) ChangePasswd() http.HandlerFunc { +func (h *UserHandel) ChangePasswd() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { ctx := r.Context() t := ctx.Value(tokenKey).(*model.TokenClaims) c, err := utils.DeCodeBody[model.ChangePasswd](r.Body, h.validate) if err != nil { - h.handleError(ctx, w, err.Error(), model.ErrInput, 400, slog.LevelDebug) + h.handleError.Error(ctx, w, err.Error(), model.ErrInput, 400, slog.LevelDebug) return } - err = h.webService.ChangePasswd(ctx, c, t) + err = h.userService.ChangePasswd(ctx, c, t) if err != nil { - h.handleErrorService(ctx, w, err) + h.handleError.Service(ctx, w, err) return } encodeJson(w, model.API[any]{ @@ -110,18 +132,18 @@ func (h *Handel) ChangePasswd() http.HandlerFunc { } } -func (h *Handel) ChangeName() http.HandlerFunc { +func (h *UserHandel) ChangeName() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { ctx := r.Context() t := ctx.Value(tokenKey).(*model.TokenClaims) c, err := utils.DeCodeBody[model.ChangeName](r.Body, h.validate) if err != nil { - h.handleError(ctx, w, err.Error(), model.ErrInput, 400, slog.LevelDebug) + h.handleError.Error(ctx, w, err.Error(), model.ErrInput, 400, slog.LevelDebug) return } - err = h.webService.ChangeName(ctx, c.Name, t) + err = h.userService.ChangeName(ctx, c.Name, t) if err != nil { - h.handleErrorService(ctx, w, err) + h.handleError.Service(ctx, w, err) return } encodeJson(w, model.API[any]{ @@ -130,7 +152,7 @@ func (h *Handel) ChangeName() http.HandlerFunc { } } -func (h *Handel) PutTexture() http.HandlerFunc { +func (h *UserHandel) PutTexture() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { ctx := r.Context() t := ctx.Value(tokenKey).(*model.TokenClaims) @@ -139,7 +161,7 @@ func (h *Handel) PutTexture() http.HandlerFunc { textureType := chi.URLParamFromCtx(ctx, "textureType") if textureType != "skin" && textureType != "cape" { h.logger.DebugContext(ctx, "上传类型错误") - h.handleError(ctx, w, "上传类型错误", model.ErrInput, 400, slog.LevelDebug) + h.handleError.Error(ctx, w, "上传类型错误", model.ErrInput, 400, slog.LevelDebug) } skin, err := func() ([]byte, error) { @@ -167,7 +189,7 @@ func (h *Handel) PutTexture() http.HandlerFunc { return bw.Bytes(), err }() if err != nil { - h.handleError(ctx, w, err.Error(), model.ErrInput, 400, slog.LevelDebug) + h.handleError.Error(ctx, w, err.Error(), model.ErrInput, 400, slog.LevelDebug) return } @@ -176,13 +198,13 @@ func (h *Handel) PutTexture() http.HandlerFunc { case "": default: h.logger.DebugContext(ctx, "错误的皮肤的材质模型") - h.handleError(ctx, w, "错误的皮肤的材质模型", model.ErrInput, 400, slog.LevelDebug) + h.handleError.Error(ctx, w, "错误的皮肤的材质模型", model.ErrInput, 400, slog.LevelDebug) return } - err = h.webService.PutTexture(ctx, t, skin, models, textureType) + err = h.textureService.PutTexture(ctx, t, skin, models, textureType) if err != nil { - h.handleErrorService(ctx, w, err) + h.handleError.Service(ctx, w, err) return } encodeJson(w, model.API[any]{ diff --git a/server/route/route.go b/server/route/route.go index 2967f11..adf35ad 100644 --- a/server/route/route.go +++ b/server/route/route.go @@ -14,7 +14,8 @@ import ( "github.com/xmdhs/authlib-skin/server/static" ) -func NewRoute(handelY *yggdrasil.Yggdrasil, handel *handle.Handel, c config.Config, sl slog.Handler) http.Handler { +func NewRoute(handelY *yggdrasil.Yggdrasil, handel *handle.Handel, c config.Config, sl slog.Handler, + userHandel *handle.UserHandel, adminHandel *handle.AdminHandel) http.Handler { r := chi.NewRouter() r.Use(middleware.RequestID) if c.RaelIP { @@ -28,7 +29,7 @@ func NewRoute(handelY *yggdrasil.Yggdrasil, handel *handle.Handel, c config.Conf r.Use(APILocationIndication) r.Mount("/", static.StaticServer()) - r.Mount("/api/v1", newSkinApi(handel)) + r.Mount("/api/v1", newSkinApi(handel, userHandel, adminHandel)) r.Mount("/api/yggdrasil", newYggdrasil(handelY)) if c.Debug { @@ -74,26 +75,26 @@ func newYggdrasil(handelY *yggdrasil.Yggdrasil) http.Handler { return r } -func newSkinApi(handel *handle.Handel) http.Handler { +func newSkinApi(handel *handle.Handel, userHandel *handle.UserHandel, adminHandel *handle.AdminHandel) http.Handler { r := chi.NewRouter() - r.Post("/user/reg", handel.Reg()) - r.Post("/user/login", handel.Login()) + r.Post("/user/reg", userHandel.Reg()) + r.Post("/user/login", userHandel.Login()) r.Get("/config", handel.GetConfig()) r.Group(func(r chi.Router) { - r.Use(handel.NeedAuth) - r.Get("/user", handel.UserInfo()) - r.Post("/user/password", handel.ChangePasswd()) - r.Post("/user/name", handel.ChangeName()) - r.Put("/user/skin/{textureType}", handel.PutTexture()) + r.Use(adminHandel.NeedAuth) + r.Get("/user", userHandel.UserInfo()) + r.Post("/user/password", userHandel.ChangePasswd()) + r.Post("/user/name", userHandel.ChangeName()) + r.Put("/user/skin/{textureType}", userHandel.PutTexture()) }) r.Group(func(r chi.Router) { - r.Use(handel.NeedAuth) - r.Use(handel.NeedAdmin) - r.Get("/admin/users", handel.ListUser()) - r.Patch("/admin/user/{uid}", handel.EditUser()) + r.Use(adminHandel.NeedAuth) + r.Use(adminHandel.NeedAdmin) + r.Get("/admin/users", adminHandel.ListUser()) + r.Patch("/admin/user/{uid}", adminHandel.EditUser()) }) return r diff --git a/server/wire.go b/server/wire.go index a819990..f183ab6 100644 --- a/server/wire.go +++ b/server/wire.go @@ -9,6 +9,7 @@ import ( "github.com/google/wire" "github.com/xmdhs/authlib-skin/config" "github.com/xmdhs/authlib-skin/handle" + "github.com/xmdhs/authlib-skin/handle/handelerror" "github.com/xmdhs/authlib-skin/handle/yggdrasil" "github.com/xmdhs/authlib-skin/server/route" "github.com/xmdhs/authlib-skin/service" @@ -18,13 +19,14 @@ import ( yggdrasilS "github.com/xmdhs/authlib-skin/service/yggdrasil" ) -var serviceSet = wire.NewSet(service.NewWebService, yggdrasilS.NewYggdrasil, email.NewEmail, auth.NewAuthService, +var serviceSet = wire.NewSet(service.Service, yggdrasilS.NewYggdrasil, email.NewEmail, auth.NewAuthService, captcha.NewCaptchaService, ) +var handleSet = wire.NewSet(handelerror.NewHandleError, handle.HandelSet, yggdrasil.NewYggdrasil) + func InitializeRoute(ctx context.Context, c config.Config) (*http.Server, func(), error) { panic(wire.Build(Set, route.NewRoute, NewSlog, - NewServer, handle.NewHandel, yggdrasil.NewYggdrasil, - serviceSet, + NewServer, handleSet, serviceSet, )) } diff --git a/server/wire_gen.go b/server/wire_gen.go index b45be66..1d55ab1 100644 --- a/server/wire_gen.go +++ b/server/wire_gen.go @@ -8,12 +8,15 @@ package server import ( "context" + "github.com/google/wire" "github.com/xmdhs/authlib-skin/config" "github.com/xmdhs/authlib-skin/handle" + "github.com/xmdhs/authlib-skin/handle/handelerror" yggdrasil2 "github.com/xmdhs/authlib-skin/handle/yggdrasil" "github.com/xmdhs/authlib-skin/server/route" "github.com/xmdhs/authlib-skin/service" "github.com/xmdhs/authlib-skin/service/auth" + "github.com/xmdhs/authlib-skin/service/captcha" "github.com/xmdhs/authlib-skin/service/email" "github.com/xmdhs/authlib-skin/service/yggdrasil" "net/http" @@ -55,16 +58,17 @@ func InitializeRoute(ctx context.Context, c config.Config) (*http.Server, func() return nil, nil, err } yggdrasil3 := yggdrasil2.NewYggdrasil(logger, validate, yggdrasilYggdrasil, c, pubRsaKey) + webService := service.NewWebService(c) + handel := handle.NewHandel(webService) + handleError := handelerror.NewHandleError(logger) httpClient := ProvideHttpClient() - webService := service.NewWebService(c, client, httpClient, cache, privateKey, authService) - emailEmail, err := email.NewEmail(privateKey, c, cache) - if err != nil { - cleanup2() - cleanup() - return nil, nil, err - } - handel := handle.NewHandel(webService, validate, c, logger, emailEmail) - httpHandler := route.NewRoute(yggdrasil3, handel, c, handler) + captchaService := captcha.NewCaptchaService(c, httpClient) + userSerice := service.NewUserSerice(c, client, captchaService, authService, cache) + textureService := service.NewTextureService(client, c, cache) + userHandel := handle.NewUserHandel(handleError, validate, userSerice, logger, textureService) + adminService := service.NewAdminService(authService, client, c, cache) + adminHandel := handle.NewAdminHandel(handleError, adminService, validate) + httpHandler := route.NewRoute(yggdrasil3, handel, c, handler, userHandel, adminHandel) server, cleanup3 := NewServer(c, httpHandler) return server, func() { cleanup3() @@ -72,3 +76,9 @@ func InitializeRoute(ctx context.Context, c config.Config) (*http.Server, func() cleanup() }, nil } + +// wire.go: + +var serviceSet = wire.NewSet(service.Service, yggdrasil.NewYggdrasil, email.NewEmail, auth.NewAuthService, captcha.NewCaptchaService) + +var handleSet = wire.NewSet(handelerror.NewHandleError, handle.HandelSet, yggdrasil2.NewYggdrasil) diff --git a/service/admin.go b/service/admin.go index f140b3f..8d98a67 100644 --- a/service/admin.go +++ b/service/admin.go @@ -6,6 +6,8 @@ import ( "fmt" "strconv" + "github.com/xmdhs/authlib-skin/config" + "github.com/xmdhs/authlib-skin/db/cache" "github.com/xmdhs/authlib-skin/db/ent" "github.com/xmdhs/authlib-skin/db/ent/predicate" "github.com/xmdhs/authlib-skin/db/ent/user" @@ -18,9 +20,26 @@ import ( "github.com/xmdhs/authlib-skin/utils" ) +type AdminService struct { + authService *auth.AuthService + client *ent.Client + config config.Config + cache cache.Cache +} + +func NewAdminService(authService *auth.AuthService, client *ent.Client, + config config.Config, cache cache.Cache) *AdminService { + return &AdminService{ + authService: authService, + client: client, + config: config, + cache: cache, + } +} + var ErrNotAdmin = errors.New("无权限") -func (w *WebService) Auth(ctx context.Context, token string) (*model.TokenClaims, error) { +func (w *AdminService) Auth(ctx context.Context, token string) (*model.TokenClaims, error) { t, err := w.authService.Auth(ctx, yggdrasil.ValidateToken{AccessToken: token}, false) if err != nil { return nil, fmt.Errorf("WebService.Auth: %w", err) @@ -28,7 +47,7 @@ func (w *WebService) Auth(ctx context.Context, token string) (*model.TokenClaims return t, nil } -func (w *WebService) IsAdmin(ctx context.Context, t *model.TokenClaims) error { +func (w *AdminService) IsAdmin(ctx context.Context, t *model.TokenClaims) error { u, err := w.client.User.Query().Where(user.ID(t.UID)).First(ctx) if err != nil { return fmt.Errorf("IsAdmin: %w", err) @@ -39,7 +58,7 @@ func (w *WebService) IsAdmin(ctx context.Context, t *model.TokenClaims) error { return nil } -func (w *WebService) ListUser(ctx context.Context, page int, email, name string) ([]model.UserList, int, error) { +func (w *AdminService) ListUser(ctx context.Context, page int, email, name string) ([]model.UserList, int, error) { whereL := []predicate.User{} if email != "" { whereL = append(whereL, user.EmailHasPrefix(email)) @@ -79,7 +98,7 @@ func (w *WebService) ListUser(ctx context.Context, page int, email, name string) return ul, uc, nil } -func (w *WebService) EditUser(ctx context.Context, u model.EditUser, uid int) error { +func (w *AdminService) EditUser(ctx context.Context, u model.EditUser, uid int) error { uuid := "" changePasswd := false err := utils.WithTx(ctx, w.client, func(tx *ent.Tx) error { diff --git a/service/admin_test.go b/service/admin_test.go index 02e3e5b..144f987 100644 --- a/service/admin_test.go +++ b/service/admin_test.go @@ -9,9 +9,9 @@ import ( "github.com/xmdhs/authlib-skin/model" ) -func TestWebService_Auth(t *testing.T) { +func TestAdminSerice_Auth(t *testing.T) { ctx := context.Background() - lr, err := webService.Reg(ctx, model.UserReg{ + lr, err := userSerice.Reg(ctx, model.UserReg{ Email: "TestWebService_Auth@xmdhs.com", Password: "TestWebService_Auth", Name: "TestWebService_Auth", @@ -20,7 +20,7 @@ func TestWebService_Auth(t *testing.T) { require.Nil(t, err) require.Equal(t, lr.Name, "TestWebService_Auth") - token, err := webService.Auth(ctx, lr.Token) + token, err := adminSerice.Auth(ctx, lr.Token) require.Nil(t, err) assert.Equal(t, token.Subject, lr.UUID) @@ -32,13 +32,13 @@ func TestWebService_Auth(t *testing.T) { } tests := []struct { name string - w *WebService + w *AdminService args args wantErr bool }{ { name: "some string", - w: webService, + w: adminSerice, args: args{ ctx: ctx, token: "123213", @@ -47,7 +47,7 @@ func TestWebService_Auth(t *testing.T) { }, { name: "valid jwt", - w: webService, + w: adminSerice, args: args{ ctx: ctx, token: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkYXRhIjpbeyJ0b29sdHQiOiJodHRwczovL3Rvb2x0dC5jb20ifV0sImlhdCI6MTY5NzEwMjMzOCwiZXhwIjoxNjk3MTI2Mzk5LCJhdWQiOiIiLCJpc3MiOiIiLCJzdWIiOiIifQ.JTQWl1PEX8u7PhVc4dTtv1DRS6e1PbMDZNWOAFJmVqE", diff --git a/service/service.go b/service/service.go index 6d43c33..996c576 100644 --- a/service/service.go +++ b/service/service.go @@ -1 +1,5 @@ package service + +import "github.com/google/wire" + +var Service = wire.NewSet(NewUserSerice, NewTextureService, NewAdminService, NewWebService) diff --git a/service/texture.go b/service/texture.go index b4784e6..07a23ac 100644 --- a/service/texture.go +++ b/service/texture.go @@ -9,6 +9,8 @@ import ( "os" "path/filepath" + "github.com/xmdhs/authlib-skin/config" + "github.com/xmdhs/authlib-skin/db/cache" "github.com/xmdhs/authlib-skin/db/ent" "github.com/xmdhs/authlib-skin/db/ent/texture" "github.com/xmdhs/authlib-skin/db/ent/user" @@ -18,7 +20,21 @@ import ( "github.com/xmdhs/authlib-skin/utils" ) -func (w *WebService) PutTexture(ctx context.Context, t *model.TokenClaims, texturebyte []byte, model string, textureType string) error { +type TextureService struct { + client *ent.Client + config config.Config + cache cache.Cache +} + +func NewTextureService(client *ent.Client, config config.Config, cache cache.Cache) *TextureService { + return &TextureService{ + client: client, + config: config, + cache: cache, + } +} + +func (w *TextureService) PutTexture(ctx context.Context, t *model.TokenClaims, texturebyte []byte, model string, textureType string) error { up, err := w.client.UserProfile.Query().Where(userprofile.HasUserWith(user.ID(t.UID))).First(ctx) if err != nil { return fmt.Errorf("PutTexture: %w", err) diff --git a/service/user.go b/service/user.go index 56d4317..f242af3 100644 --- a/service/user.go +++ b/service/user.go @@ -9,11 +9,14 @@ import ( "time" "github.com/google/uuid" + "github.com/xmdhs/authlib-skin/config" + "github.com/xmdhs/authlib-skin/db/cache" "github.com/xmdhs/authlib-skin/db/ent" "github.com/xmdhs/authlib-skin/db/ent/user" "github.com/xmdhs/authlib-skin/db/ent/userprofile" "github.com/xmdhs/authlib-skin/model" "github.com/xmdhs/authlib-skin/service/auth" + "github.com/xmdhs/authlib-skin/service/captcha" "github.com/xmdhs/authlib-skin/utils" ) @@ -25,7 +28,26 @@ var ( ErrChangeName = errors.New("离线模式 uuid 不允许修改用户名") ) -func (w *WebService) Reg(ctx context.Context, u model.UserReg, ipPrefix, ip string) (model.LoginRep, error) { +type UserSerice struct { + config config.Config + client *ent.Client + captchaService *captcha.CaptchaService + authService *auth.AuthService + cache cache.Cache +} + +func NewUserSerice(config config.Config, client *ent.Client, captchaService *captcha.CaptchaService, + authService *auth.AuthService, cache cache.Cache) *UserSerice { + return &UserSerice{ + config: config, + client: client, + captchaService: captchaService, + authService: authService, + cache: cache, + } +} + +func (w *UserSerice) Reg(ctx context.Context, u model.UserReg, ipPrefix, ip string) (model.LoginRep, error) { var userUuid string if w.config.OfflineUUID { userUuid = utils.UUIDGen(u.Name) @@ -108,7 +130,7 @@ func (w *WebService) Reg(ctx context.Context, u model.UserReg, ipPrefix, ip stri }, nil } -func (w *WebService) Login(ctx context.Context, l model.Login, ip string) (model.LoginRep, error) { +func (w *UserSerice) Login(ctx context.Context, l model.Login, ip string) (model.LoginRep, error) { err := w.captchaService.VerifyCaptcha(ctx, l.CaptchaToken, ip) if err != nil { return model.LoginRep{}, fmt.Errorf("Login: %w", err) @@ -121,7 +143,7 @@ func (w *WebService) Login(ctx context.Context, l model.Login, ip string) (model } return model.LoginRep{}, fmt.Errorf("Login: %w", err) } - err = w.validatePass(ctx, u, l.Password) + err = validatePass(ctx, u, l.Password) if err != nil { return model.LoginRep{}, fmt.Errorf("Login: %w", err) } @@ -136,7 +158,7 @@ func (w *WebService) Login(ctx context.Context, l model.Login, ip string) (model }, nil } -func (w *WebService) Info(ctx context.Context, t *model.TokenClaims) (model.UserInfo, error) { +func (w *UserSerice) Info(ctx context.Context, t *model.TokenClaims) (model.UserInfo, error) { u, err := w.client.User.Query().Where(user.ID(t.UID)).First(ctx) if err != nil { return model.UserInfo{}, fmt.Errorf("Info: %w", err) @@ -149,12 +171,12 @@ func (w *WebService) Info(ctx context.Context, t *model.TokenClaims) (model.User }, nil } -func (w *WebService) ChangePasswd(ctx context.Context, p model.ChangePasswd, t *model.TokenClaims) error { +func (w *UserSerice) ChangePasswd(ctx context.Context, p model.ChangePasswd, t *model.TokenClaims) error { u, err := w.client.User.Query().Where(user.IDEQ(t.UID)).WithToken().First(ctx) if err != nil { return fmt.Errorf("ChangePasswd: %w", err) } - err = w.validatePass(ctx, u, p.Old) + err = validatePass(ctx, u, p.Old) if err != nil { return fmt.Errorf("ChangePasswd: %w", err) } @@ -176,7 +198,7 @@ func (w *WebService) ChangePasswd(ctx context.Context, p model.ChangePasswd, t * return nil } -func (w *WebService) changeName(ctx context.Context, newName string, uid int, uuid string) error { +func (w *UserSerice) changeName(ctx context.Context, newName string, uid int, uuid string) error { if w.config.OfflineUUID { return fmt.Errorf("changeName: %w", ErrChangeName) } @@ -195,7 +217,7 @@ func (w *WebService) changeName(ctx context.Context, newName string, uid int, uu return err } -func (w *WebService) ChangeName(ctx context.Context, newName string, t *model.TokenClaims) error { +func (w *UserSerice) ChangeName(ctx context.Context, newName string, t *model.TokenClaims) error { err := w.changeName(ctx, newName, t.UID, t.Subject) if err != nil { return fmt.Errorf("ChangeName: %w", err) diff --git a/service/user_test.go b/service/user_test.go index 1615baf..97f470f 100644 --- a/service/user_test.go +++ b/service/user_test.go @@ -19,12 +19,15 @@ import ( "github.com/xmdhs/authlib-skin/service/captcha" ) -var webService *WebService +var ( + userSerice *UserSerice + adminSerice *AdminService +) func TestMain(m *testing.M) { ctx := context.Background() - clean := initWebService(ctx) + clean := initSerice(ctx) code := m.Run() clean() @@ -32,12 +35,16 @@ func TestMain(m *testing.M) { os.Exit(code) } -func initWebService(ctx context.Context) func() { +func initSerice(ctx context.Context) func() { c := lo.Must(ent.Open("mysql", "root:root@tcp(127.0.0.1)/test")) lo.Must0(c.Schema.Create(context.Background(), migrate.WithForeignKeys(false), migrate.WithDropIndex(true), migrate.WithDropColumn(true))) rsa4 := lo.Must(rsa.GenerateKey(rand.Reader, 4096)) cache := cache.NewFastCache(100000) - webService = NewWebService(config.Default(), c, &http.Client{}, cache, rsa4, auth.NewAuthService(c, cache, &rsa4.PublicKey, rsa4), captcha.NewCaptchaService(config.Default(), &http.Client{})) + config := config.Default() + authService := auth.NewAuthService(c, cache, &rsa4.PublicKey, rsa4) + + userSerice = NewUserSerice(config, c, captcha.NewCaptchaService(config, &http.Client{}), authService, cache) + adminSerice = NewAdminService(authService, c, config, cache) return func() { c.User.Delete().Exec(ctx) @@ -48,9 +55,9 @@ func initWebService(ctx context.Context) func() { } } -func TestWebService_Reg(t *testing.T) { +func TestUserSerice_Reg(t *testing.T) { ctx := context.Background() - webService.config.MaxIpUser = 1 + userSerice.config.MaxIpUser = 1 type args struct { ctx context.Context u model.UserReg @@ -59,13 +66,13 @@ func TestWebService_Reg(t *testing.T) { } tests := []struct { name string - w *WebService + w *UserSerice args args wantErr bool }{ { name: "1", - w: webService, + w: userSerice, args: args{ ctx: ctx, u: model.UserReg{ @@ -81,7 +88,7 @@ func TestWebService_Reg(t *testing.T) { }, { name: "email duplicate", - w: webService, + w: userSerice, args: args{ ctx: ctx, u: model.UserReg{ @@ -97,7 +104,7 @@ func TestWebService_Reg(t *testing.T) { }, { name: "name duplicate", - w: webService, + w: userSerice, args: args{ ctx: ctx, u: model.UserReg{ @@ -113,7 +120,7 @@ func TestWebService_Reg(t *testing.T) { }, { name: "MaxIpUser", - w: webService, + w: userSerice, args: args{ ctx: ctx, u: model.UserReg{ @@ -129,7 +136,7 @@ func TestWebService_Reg(t *testing.T) { }, { name: "MaxIpUser", - w: webService, + w: userSerice, args: args{ ctx: ctx, u: model.UserReg{ @@ -151,5 +158,5 @@ func TestWebService_Reg(t *testing.T) { } }) } - webService.config.MaxIpUser = 0 + userSerice.config.MaxIpUser = 0 } diff --git a/service/web.go b/service/web.go index 0da6387..2b08f4b 100644 --- a/service/web.go +++ b/service/web.go @@ -2,43 +2,25 @@ package service import ( "context" - "crypto/rsa" "fmt" - "net/http" "github.com/xmdhs/authlib-skin/config" - "github.com/xmdhs/authlib-skin/db/cache" "github.com/xmdhs/authlib-skin/db/ent" "github.com/xmdhs/authlib-skin/model" - "github.com/xmdhs/authlib-skin/service/auth" - "github.com/xmdhs/authlib-skin/service/captcha" "github.com/xmdhs/authlib-skin/utils" ) type WebService struct { - config config.Config - client *ent.Client - httpClient *http.Client - cache cache.Cache - prikey *rsa.PrivateKey - authService *auth.AuthService - captchaService *captcha.CaptchaService + config config.Config } -func NewWebService(c config.Config, e *ent.Client, hc *http.Client, - cache cache.Cache, prikey *rsa.PrivateKey, authService *auth.AuthService, captchaService *captcha.CaptchaService) *WebService { +func NewWebService(c config.Config) *WebService { return &WebService{ - config: c, - client: e, - httpClient: hc, - cache: cache, - prikey: prikey, - authService: authService, - captchaService: captchaService, + config: c, } } -func (w *WebService) validatePass(ctx context.Context, u *ent.User, password string) error { +func validatePass(ctx context.Context, u *ent.User, password string) error { if !utils.Argon2Compare(password, u.Password, u.Salt) { return fmt.Errorf("validatePass: %w", ErrPassWord) }