diff --git a/handle/yggdrasil/texture.go b/handle/yggdrasil/texture.go index 2650927..4b364e4 100644 --- a/handle/yggdrasil/texture.go +++ b/handle/yggdrasil/texture.go @@ -7,6 +7,7 @@ import ( "fmt" "image/png" "io" + "log/slog" "net/http" "strings" @@ -46,14 +47,14 @@ func (y *Yggdrasil) validTextureType(ctx context.Context, w http.ResponseWriter, func (y *Yggdrasil) PutTexture() httprouter.Handle { return func(w http.ResponseWriter, r *http.Request, p httprouter.Params) { ctx := r.Context() - uuid := p.ByName("uuid") - textureType := p.ByName("textureType") - if uuid == "" || textureType == "" { - y.logger.DebugContext(ctx, "路径中缺少参数 uuid / textureType") - handleYgError(ctx, w, yggdrasil.Error{ErrorMessage: "路径中缺少参数 uuid / textureType"}, 400) + uuid, textureType, ok := getUUIDbyParams(ctx, p, y.logger, w) + if !ok { return } token := y.getTokenbyAuthorization(ctx, w, r) + if token == "" { + return + } model := r.FormValue("model") @@ -114,3 +115,40 @@ func (y *Yggdrasil) PutTexture() httprouter.Handle { w.WriteHeader(204) } } + +func getUUIDbyParams(ctx context.Context, p httprouter.Params, l *slog.Logger, w http.ResponseWriter) (string, string, bool) { + uuid := p.ByName("uuid") + textureType := p.ByName("textureType") + if uuid == "" || textureType == "" { + l.DebugContext(ctx, "路径中缺少参数 uuid / textureType") + handleYgError(ctx, w, yggdrasil.Error{ErrorMessage: "路径中缺少参数 uuid / textureType"}, 400) + return "", "", false + } + return uuid, textureType, true +} + +func (y *Yggdrasil) DelTexture() httprouter.Handle { + return func(w http.ResponseWriter, r *http.Request, p httprouter.Params) { + ctx := r.Context() + uuid, textureType, ok := getUUIDbyParams(ctx, p, y.logger, w) + if !ok { + return + } + token := y.getTokenbyAuthorization(ctx, w, r) + if token == "" { + return + } + err := y.yggdrasilService.DelTexture(ctx, uuid, token, textureType) + if err != nil { + if errors.Is(err, utils.ErrTokenInvalid) { + y.logger.DebugContext(ctx, err.Error()) + w.WriteHeader(401) + return + } + y.logger.WarnContext(ctx, err.Error()) + handleYgError(ctx, w, yggdrasil.Error{ErrorMessage: err.Error()}, 500) + return + } + w.WriteHeader(204) + } +} diff --git a/server/route/route.go b/server/route/route.go index 9efe515..c63b9d0 100644 --- a/server/route/route.go +++ b/server/route/route.go @@ -29,6 +29,7 @@ func newYggdrasil(r *httprouter.Router, handelY yggdrasil.Yggdrasil) error { r.POST("/api/yggdrasil/authserver/invalidate", handelY.Invalidate()) r.POST("/api/yggdrasil/authserver/refresh", handelY.Refresh()) r.PUT("/api/yggdrasil/api/user/profile/:uuid/:textureType", handelY.PutTexture()) + r.DELETE("/api/yggdrasil/api/user/profile/:uuid/:textureType", handelY.DelTexture()) r.GET("/api/yggdrasil", func(w http.ResponseWriter, r *http.Request, p httprouter.Params) { w.Write([]byte(`{ "meta": { diff --git a/service/yggdrasil/texture.go b/service/yggdrasil/texture.go index 34445a9..734e8cb 100644 --- a/service/yggdrasil/texture.go +++ b/service/yggdrasil/texture.go @@ -24,21 +24,11 @@ var ( ErrUUIDNotEq = errors.New("uuid 不相同") ) -func (y *Yggdrasil) PutTexture(ctx context.Context, token string, texturebyte []byte, model string, uuid string, textureType string) error { - t, err := utilsService.Auth(ctx, yggdrasil.ValidateToken{AccessToken: token}, y.client, &y.prikey.PublicKey, true) - if err != nil { - return fmt.Errorf("PutTexture: %w", err) - } - if uuid != t.Subject { - return fmt.Errorf("PutTexture: %w", ErrUUIDNotEq) - } - - up, err := y.client.UserProfile.Query().Where(userprofile.HasUserWith(user.ID(t.UID))).First(ctx) - - err = utils.WithTx(ctx, y.client, func(tx *ent.Tx) error { +func (y *Yggdrasil) delTexture(ctx context.Context, userProfileID int, textureType string) error { + err := utils.WithTx(ctx, y.client, func(tx *ent.Tx) error { // 查找此用户该类型下是否已经存在皮肤 tl, err := tx.UserTexture.Query().Where(usertexture.And( - usertexture.UserProfileID(up.ID), + usertexture.UserProfileID(userProfileID), usertexture.Type(textureType), )).ForUpdate().All(ctx) if err != nil { @@ -75,11 +65,55 @@ func (y *Yggdrasil) PutTexture(ctx context.Context, token string, texturebyte [] return item.UserProfileID }) // 中间表删除记录 + // UserProfile 上没有于此相关的字段,所以无需操作 _, err = tx.UserTexture.Delete().Where(usertexture.UserProfileIDIn(ids...)).Exec(ctx) return err // 小概率皮肤上传后,高并发时被此处清理。问题不大重新上传一遍就行。 // 条件为使用一个独一无二的皮肤的用户,更换皮肤时,另一个用户同时更换自己的皮肤到这个独一无二的皮肤上。 }) + if err != nil { + return fmt.Errorf("delTexture: %w", err) + } + return nil +} + +func (y *Yggdrasil) DelTexture(ctx context.Context, uuid string, token string, textureType string) error { + t, err := utilsService.Auth(ctx, yggdrasil.ValidateToken{AccessToken: token}, y.client, &y.prikey.PublicKey, true) + if err != nil { + return fmt.Errorf("DelTexture: %w", err) + } + if uuid != t.Subject { + return fmt.Errorf("PutTexture: %w", errors.Join(ErrUUIDNotEq, utilsService.ErrTokenInvalid)) + } + up, err := y.client.UserProfile.Query().Where(userprofile.HasUserWith(user.ID(t.UID))).First(ctx) + if err != nil { + return fmt.Errorf("DelTexture: %w", err) + } + err = y.delTexture(ctx, up.ID, textureType) + if err != nil { + return fmt.Errorf("DelTexture: %w", err) + } + return nil +} + +func (y *Yggdrasil) PutTexture(ctx context.Context, token string, texturebyte []byte, model string, uuid string, textureType string) error { + t, err := utilsService.Auth(ctx, yggdrasil.ValidateToken{AccessToken: token}, y.client, &y.prikey.PublicKey, true) + if err != nil { + return fmt.Errorf("PutTexture: %w", err) + } + if uuid != t.Subject { + return fmt.Errorf("PutTexture: %w", errors.Join(ErrUUIDNotEq, utilsService.ErrTokenInvalid)) + } + + up, err := y.client.UserProfile.Query().Where(userprofile.HasUserWith(user.ID(t.UID))).First(ctx) + if err != nil { + return fmt.Errorf("PutTexture: %w", err) + } + + err = y.delTexture(ctx, up.ID, textureType) + if err != nil { + return fmt.Errorf("PutTexture: %w", err) + } hashstr, err := createTextureFile(y.config.TexturePath, texturebyte) if err != nil {