121 lines
3.4 KiB
Go
121 lines
3.4 KiB
Go
package yggdrasil
|
|
|
|
import (
|
|
"context"
|
|
"encoding/hex"
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"github.com/samber/lo"
|
|
"github.com/xmdhs/authlib-skin/db/ent"
|
|
"github.com/xmdhs/authlib-skin/db/ent/texture"
|
|
"github.com/xmdhs/authlib-skin/db/ent/user"
|
|
"github.com/xmdhs/authlib-skin/db/ent/userprofile"
|
|
"github.com/xmdhs/authlib-skin/model/yggdrasil"
|
|
utilsService "github.com/xmdhs/authlib-skin/service/utils"
|
|
"github.com/xmdhs/authlib-skin/utils"
|
|
"lukechampine.com/blake3"
|
|
)
|
|
|
|
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))).WithUser().First(ctx)
|
|
|
|
err = utils.WithTx(ctx, y.client, func(tx *ent.Tx) error {
|
|
// 查找此用户该类型下是否已经存在皮肤
|
|
tl, err := tx.UserProfile.QueryTexture(up).Where(texture.TypeEQ(textureType)).ForUpdate().All(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if len(tl) == 0 {
|
|
return nil
|
|
}
|
|
// 若存在,查找是否被引用
|
|
for _, v := range tl {
|
|
c, err := tx.UserProfile.Query().Where(userprofile.HasTextureWith(texture.IDEQ(v.ID))).Count(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if c == 1 {
|
|
// 若没有其他用户使用该皮肤,删除文件和记录
|
|
path := filepath.Join(y.TexturePath, v.TextureHash[:2], v.TextureHash[2:4], v.TextureHash)
|
|
err = os.Remove(path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = tx.Texture.DeleteOneID(v.ID).Exec(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
ids := lo.Map[*ent.Texture, int](tl, func(item *ent.Texture, index int) int {
|
|
return item.ID
|
|
})
|
|
return tx.UserProfile.UpdateOne(up).RemoveTextureIDs(ids...).Exec(ctx)
|
|
})
|
|
|
|
hashstr, err := createTextureFile(y.config.TexturePath, texturebyte)
|
|
if err != nil {
|
|
return fmt.Errorf("PutTexture: %w", err)
|
|
}
|
|
|
|
var textureEnt *ent.Texture
|
|
err = utils.WithTx(ctx, y.client, func(tx *ent.Tx) error {
|
|
textureEnt, err = tx.Texture.Query().Where(texture.TextureHashEQ(hashstr)).ForUpdate().First(ctx)
|
|
var nr *ent.NotFoundError
|
|
if err != nil && !errors.As(err, &nr) {
|
|
return err
|
|
}
|
|
if textureEnt == nil {
|
|
textureEnt, err = tx.Texture.Create().SetCreatedUser(up.Edges.User).
|
|
SetTextureHash(hashstr).
|
|
SetType(textureType).
|
|
SetVariant(model).
|
|
Save(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return tx.UserProfile.UpdateOne(up).AddTexture(textureEnt).Exec(ctx)
|
|
})
|
|
if err != nil {
|
|
return fmt.Errorf("PutTexture: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func createTextureFile(path string, b []byte) (string, error) {
|
|
hashed := blake3.Sum256(b)
|
|
hashstr := hex.EncodeToString(hashed[:])
|
|
p := filepath.Join(path, hashstr[:2], hashstr[2:4], hashstr)
|
|
err := os.MkdirAll(filepath.Dir(p), 0755)
|
|
if err != nil {
|
|
return "", fmt.Errorf("createTextureFile: %w", err)
|
|
}
|
|
f, err := os.Stat(p)
|
|
if err != nil && !errors.Is(err, os.ErrNotExist) {
|
|
return "", fmt.Errorf("createTextureFile: %w", err)
|
|
}
|
|
if f == nil {
|
|
err := os.WriteFile(p, b, 0644)
|
|
if err != nil {
|
|
return "", fmt.Errorf("createTextureFile: %w", err)
|
|
}
|
|
}
|
|
return hashstr, nil
|
|
}
|