删除 yggdrasil 中的 皮肤上传接口
因为几乎没有几个启动器实现了皮肤上传,所以删除了也几乎没有影响
This commit is contained in:
parent
6cba9ada31
commit
cad8e6e317
@ -42,6 +42,7 @@ texturePath: "skin"
|
||||
# 材质静态文件提供基础地址
|
||||
# 如果静态文件位于 oss 上,比如 https://s3.amazonaws.com/example/1.png
|
||||
# 则填写 https://s3.amazonaws.com/example
|
||||
# 若不需要可不填写
|
||||
textureBaseUrl: ""
|
||||
|
||||
# 用于在支持的启动器中展示本站的注册地址
|
||||
|
@ -1,53 +0,0 @@
|
||||
# 为 true 则 uuid 生成方式于离线模式相同,若从离线模式切换不会丢失数据。
|
||||
# 已有用户数据的情况下勿更改此项
|
||||
offlineUUID: true
|
||||
|
||||
port: "127.0.0.1:8080"
|
||||
|
||||
Log:
|
||||
Level: "debug"
|
||||
# json 格式输出
|
||||
Json: false
|
||||
|
||||
sql:
|
||||
dsn: "123"
|
||||
|
||||
# 输出每条执行的 sql 语句
|
||||
debug: false
|
||||
|
||||
cache:
|
||||
# 默认使用内存缓存,若需要集群部署,请更换 redis
|
||||
type: ""
|
||||
# 内存缓存使用大小,单位 b
|
||||
ram: 10000000
|
||||
|
||||
# 位于反向代理后启用,用于记录真实 ip
|
||||
raelIP: false
|
||||
|
||||
# ip 段最大注册用户,ipv4 为 /24 ipv6 为 /48
|
||||
maxIpUser: 10
|
||||
|
||||
# 运行后勿修改,若为集群需设置为一致
|
||||
rsaPriKey: ""
|
||||
|
||||
# 材质文件保存路径,如果需要对象存储可以把对象储存挂载到本地目录上
|
||||
texturePath: "skin"
|
||||
|
||||
# 材质静态文件提供基础地址
|
||||
# 如果静态文件位于 oss 上,比如 https://s3.amazonaws.com/example/1.png
|
||||
# 则填写 https://s3.amazonaws.com/example
|
||||
textureBaseUrl: ""
|
||||
|
||||
# 用于在支持的启动器中展示本站的注册地址
|
||||
# 填写类似 https://example.com
|
||||
webBaseUrl: ""
|
||||
|
||||
# 皮肤站名字,用于在多个地方展示
|
||||
serverName: ""
|
||||
|
||||
captcha:
|
||||
# 验证码类型,目前只支持 cloudflare turnstile
|
||||
# 填写 turnstile
|
||||
type: ""
|
||||
siteKey: ""
|
||||
secret: ""
|
@ -1,4 +1,4 @@
|
||||
import type { tokenData, ApiUser, ApiServerInfo, YggProfile, ApiConfig, List, UserInfo, EditUser } from '@/apis/model'
|
||||
import type { tokenData, ApiUser, YggProfile, ApiConfig, List, UserInfo, EditUser } from '@/apis/model'
|
||||
import { apiGet } from '@/apis/utils'
|
||||
import root from '@/utils/root'
|
||||
|
||||
@ -37,12 +37,6 @@ export async function userInfo(token: string) {
|
||||
return await apiGet<ApiUser>(v)
|
||||
}
|
||||
|
||||
|
||||
export async function serverInfo() {
|
||||
const v = await fetch(root() + "/api/yggdrasil")
|
||||
return await v.json() as ApiServerInfo
|
||||
}
|
||||
|
||||
export async function yggProfile(uuid: string) {
|
||||
if (uuid == "") return
|
||||
const v = await fetch(root() + "/api/yggdrasil/sessionserver/session/minecraft/profile/" + uuid)
|
||||
@ -53,21 +47,19 @@ export async function yggProfile(uuid: string) {
|
||||
return data as YggProfile
|
||||
}
|
||||
|
||||
export async function upTextures(uuid: string, token: string, textureType: 'skin' | 'cape', model: 'slim' | '', file: File) {
|
||||
export async function upTextures(token: string, textureType: 'skin' | 'cape', model: 'slim' | '', file: File) {
|
||||
const f = new FormData()
|
||||
f.set("file", file)
|
||||
f.set("model", model)
|
||||
|
||||
const r = await fetch(root() + "/api/yggdrasil/api/user/profile/" + uuid + "/" + textureType, {
|
||||
const r = await fetch(root() + "/api/v1/user/skin/" + textureType, {
|
||||
method: "PUT",
|
||||
body: f,
|
||||
headers: {
|
||||
"Authorization": "Bearer " + token
|
||||
}
|
||||
})
|
||||
if (r.status != 204) {
|
||||
throw new Error("上传失败 " + String(r.status))
|
||||
}
|
||||
return await apiGet<unknown>(r)
|
||||
}
|
||||
|
||||
export async function changePasswd(old: string, newpa: string, token: string) {
|
||||
|
@ -28,12 +28,6 @@ export interface ApiUser {
|
||||
is_admin: boolean
|
||||
}
|
||||
|
||||
export interface ApiServerInfo {
|
||||
meta: {
|
||||
serverName: string
|
||||
}
|
||||
}
|
||||
|
||||
export interface YggProfile {
|
||||
name: string
|
||||
properties: {
|
||||
@ -45,6 +39,7 @@ export interface YggProfile {
|
||||
export interface ApiConfig {
|
||||
captcha: captcha
|
||||
AllowChangeName: boolean
|
||||
serverName: string
|
||||
}
|
||||
|
||||
export interface UserInfo {
|
||||
|
@ -1,16 +1,16 @@
|
||||
import { serverInfo } from '@/apis/apis'
|
||||
import { getConfig } from '@/apis/apis'
|
||||
import { useTitle as auseTitle, useRequest } from 'ahooks'
|
||||
import { useEffect } from 'react'
|
||||
|
||||
export default function useTitle(title: string) {
|
||||
const { data, error } = useRequest(serverInfo, {
|
||||
cacheKey: "/api/yggdrasil",
|
||||
const { data, error } = useRequest(getConfig, {
|
||||
cacheKey: "/api/v1/config",
|
||||
staleTime: 60000,
|
||||
})
|
||||
useEffect(() => {
|
||||
error && console.warn(error)
|
||||
}, [error])
|
||||
auseTitle(title + " - " + data?.meta.serverName ?? "", {
|
||||
auseTitle(title + " - " + data?.serverName ?? "", {
|
||||
restoreOnUnmount: true
|
||||
})
|
||||
}
|
@ -23,7 +23,7 @@ import { atom, useAtom, useAtomValue, useSetAtom } from 'jotai';
|
||||
import Button from '@mui/material/Button';
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { useRequest, useMemoizedFn } from 'ahooks';
|
||||
import { serverInfo, userInfo } from '@/apis/apis'
|
||||
import { getConfig, userInfo } from '@/apis/apis'
|
||||
import Snackbar from '@mui/material/Snackbar';
|
||||
import Alert from '@mui/material/Alert';
|
||||
import { memo } from 'react';
|
||||
@ -94,8 +94,8 @@ const MyToolbar = memo(function MyToolbar() {
|
||||
const setErr = useSetAtom(LayoutAlertErr)
|
||||
const setOpen = useSetAtom(DrawerOpen)
|
||||
|
||||
const server = useRequest(serverInfo, {
|
||||
cacheKey: "/api/yggdrasil",
|
||||
const server = useRequest(getConfig, {
|
||||
cacheKey: "/api/v1/config",
|
||||
staleTime: 60000,
|
||||
onError: e => {
|
||||
console.warn(e)
|
||||
@ -131,7 +131,7 @@ const MyToolbar = memo(function MyToolbar() {
|
||||
}
|
||||
<Typography variant="h6" component="div" sx={{ flexGrow: 1 }}>
|
||||
<Link to="/" style={{ color: "unset", textDecoration: "unset" }}>
|
||||
{server.data?.meta.serverName ?? "皮肤站"}
|
||||
{server.data?.serverName ?? "皮肤站"}
|
||||
</Link>
|
||||
</Typography>
|
||||
{nowUser.name != "" && (
|
||||
|
@ -14,7 +14,7 @@ import Box from "@mui/material/Box";
|
||||
import ReactSkinview3d from '@/components/Skinview3d'
|
||||
import { useUnmount } from "ahooks";
|
||||
import { useAtomValue, useSetAtom } from "jotai";
|
||||
import { LayoutAlertErr, token, user } from "@/store/store";
|
||||
import { LayoutAlertErr, token } from "@/store/store";
|
||||
import { upTextures } from "@/apis/apis";
|
||||
import Loading from "@/components/Loading";
|
||||
import Snackbar from "@mui/material/Snackbar";
|
||||
@ -25,7 +25,6 @@ const Textures = function Textures() {
|
||||
const [file, setFile] = useState<File | null>(null)
|
||||
const setErr = useSetAtom(LayoutAlertErr)
|
||||
const [loading, setLoading] = useState(false)
|
||||
const userinfo = useAtomValue(user)
|
||||
const nowToken = useAtomValue(token)
|
||||
const [ok, setOk] = useState(false)
|
||||
const [skinInfo, setSkinInfo] = useState({
|
||||
@ -73,8 +72,8 @@ const Textures = function Textures() {
|
||||
setLoading(true)
|
||||
const textureType = redioValue == "cape" ? "cape" : "skin"
|
||||
const model = redioValue == "slim" ? "slim" : ""
|
||||
upTextures(userinfo.uuid, nowToken, textureType, model, file).catch(e => [setErr(String(e)), console.warn(e)]).
|
||||
finally(() => setLoading(false)).then(() => setOk(true))
|
||||
upTextures(nowToken, textureType, model, file).then(() => setOk(true)).catch(e => [setErr(String(e)), console.warn(e)]).
|
||||
finally(() => setLoading(false))
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,9 +1,14 @@
|
||||
package handle
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"image/png"
|
||||
"io"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/xmdhs/authlib-skin/model"
|
||||
"github.com/xmdhs/authlib-skin/utils"
|
||||
)
|
||||
@ -123,3 +128,64 @@ func (h *Handel) ChangeName() http.HandlerFunc {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Handel) PutTexture() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
t := ctx.Value(tokenKey).(*model.TokenClaims)
|
||||
models := r.FormValue("model")
|
||||
|
||||
textureType := chi.URLParamFromCtx(ctx, "textureType")
|
||||
if textureType != "skin" && textureType != "cape" {
|
||||
h.logger.DebugContext(ctx, "上传类型错误")
|
||||
h.handleError(ctx, w, "上传类型错误", model.ErrInput, 400, slog.LevelDebug)
|
||||
}
|
||||
|
||||
skin, err := func() ([]byte, error) {
|
||||
f, _, err := r.FormFile("file")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b, err := io.ReadAll(io.LimitReader(f, 50*1000))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pc, err := png.DecodeConfig(bytes.NewReader(b))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if pc.Height > 200 || pc.Width > 200 {
|
||||
return nil, fmt.Errorf("材质大小超过限制")
|
||||
}
|
||||
p, err := png.Decode(bytes.NewReader(b))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bw := bytes.NewBuffer(nil)
|
||||
err = png.Encode(bw, p)
|
||||
return bw.Bytes(), err
|
||||
}()
|
||||
if err != nil {
|
||||
h.handleError(ctx, w, err.Error(), model.ErrInput, 400, slog.LevelDebug)
|
||||
return
|
||||
}
|
||||
|
||||
switch models {
|
||||
case "slim":
|
||||
case "":
|
||||
default:
|
||||
h.logger.DebugContext(ctx, "错误的皮肤的材质模型")
|
||||
h.handleError(ctx, w, "错误的皮肤的材质模型", model.ErrInput, 400, slog.LevelDebug)
|
||||
return
|
||||
}
|
||||
|
||||
err = h.webService.PutTexture(ctx, t, skin, models, textureType)
|
||||
if err != nil {
|
||||
h.handleErrorService(ctx, w, err)
|
||||
return
|
||||
}
|
||||
encodeJson(w, model.API[any]{
|
||||
Code: 0,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,8 @@
|
||||
package yggdrasil
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"image/png"
|
||||
"io"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"strings"
|
||||
@ -46,74 +42,6 @@ func (y *Yggdrasil) validTextureType(ctx context.Context, w http.ResponseWriter,
|
||||
return true
|
||||
}
|
||||
|
||||
func (y *Yggdrasil) PutTexture() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
uuid, textureType, ok := getUUIDbyParams(ctx, y.logger, w)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
t := ctx.Value(tokenKey).(*model.TokenClaims)
|
||||
|
||||
if uuid != t.Subject {
|
||||
y.logger.DebugContext(ctx, "uuid 不相同")
|
||||
w.WriteHeader(401)
|
||||
return
|
||||
}
|
||||
|
||||
model := r.FormValue("model")
|
||||
|
||||
skin, err := func() ([]byte, error) {
|
||||
f, _, err := r.FormFile("file")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b, err := io.ReadAll(io.LimitReader(f, 50*1000))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pc, err := png.DecodeConfig(bytes.NewReader(b))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if pc.Height > 200 || pc.Width > 200 {
|
||||
return nil, fmt.Errorf("材质大小超过限制")
|
||||
}
|
||||
p, err := png.Decode(bytes.NewReader(b))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bw := bytes.NewBuffer(nil)
|
||||
err = png.Encode(bw, p)
|
||||
return bw.Bytes(), err
|
||||
}()
|
||||
if err != nil {
|
||||
y.logger.DebugContext(ctx, err.Error())
|
||||
handleYgError(ctx, w, yggdrasil.Error{ErrorMessage: err.Error()}, 400)
|
||||
return
|
||||
}
|
||||
if !y.validTextureType(ctx, w, textureType) {
|
||||
return
|
||||
}
|
||||
|
||||
switch model {
|
||||
case "slim":
|
||||
case "":
|
||||
default:
|
||||
y.logger.DebugContext(ctx, "错误的皮肤的材质模型")
|
||||
handleYgError(ctx, w, yggdrasil.Error{ErrorMessage: "错误的皮肤的材质模型"}, 400)
|
||||
return
|
||||
}
|
||||
|
||||
err = y.yggdrasilService.PutTexture(ctx, t, skin, model, textureType)
|
||||
if err != nil {
|
||||
y.handleYgError(ctx, w, err)
|
||||
return
|
||||
}
|
||||
w.WriteHeader(204)
|
||||
}
|
||||
}
|
||||
|
||||
func getUUIDbyParams(ctx context.Context, l *slog.Logger, w http.ResponseWriter) (string, string, bool) {
|
||||
uuid := chi.URLParamFromCtx(ctx, "uuid")
|
||||
textureType := chi.URLParamFromCtx(ctx, "textureType")
|
||||
|
@ -31,8 +31,9 @@ type TokenClaims struct {
|
||||
}
|
||||
|
||||
type Captcha struct {
|
||||
Type string `json:"type"`
|
||||
SiteKey string `json:"siteKey"`
|
||||
Type string `json:"type"`
|
||||
SiteKey string `json:"siteKey"`
|
||||
ServerName string `json:"serverName"`
|
||||
}
|
||||
|
||||
type UserInfo struct {
|
||||
|
@ -46,7 +46,6 @@ func newYggdrasil(handelY *yggdrasil.Yggdrasil) http.Handler {
|
||||
r.Post("/authserver/invalidate", handelY.Invalidate())
|
||||
r.Post("/authserver/refresh", handelY.Refresh())
|
||||
|
||||
r.Put("/api/user/profile/{uuid}/{textureType}", handelY.PutTexture())
|
||||
r.Delete("/api/user/profile/{uuid}/{textureType}", handelY.DelTexture())
|
||||
|
||||
r.Post("/sessionserver/session/minecraft/join", handelY.SessionJoin())
|
||||
@ -78,7 +77,7 @@ func newSkinApi(handel *handle.Handel) http.Handler {
|
||||
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.Group(func(r chi.Router) {
|
||||
|
@ -117,7 +117,7 @@ func (w *WebService) EditUser(ctx context.Context, u model.EditUser, uid int) er
|
||||
uuid = userProfile.UUID
|
||||
tl := []string{"skin", "cape"}
|
||||
for _, v := range tl {
|
||||
err := utilsService.DelTexture(ctx, userProfile.ID, v, w.client, w.config)
|
||||
err := utilsService.DelTexture(ctx, userProfile.ID, v, w.client, w.config.TexturePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -15,8 +15,9 @@ import (
|
||||
func (w *WebService) GetConfig(ctx context.Context) model.Config {
|
||||
return model.Config{
|
||||
Captcha: model.Captcha{
|
||||
Type: w.config.Captcha.Type,
|
||||
SiteKey: w.config.Captcha.SiteKey,
|
||||
Type: w.config.Captcha.Type,
|
||||
SiteKey: w.config.Captcha.SiteKey,
|
||||
ServerName: w.config.ServerName,
|
||||
},
|
||||
AllowChangeName: !w.config.OfflineUUID,
|
||||
}
|
||||
|
96
service/texture.go
Normal file
96
service/texture.go
Normal file
@ -0,0 +1,96 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"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"
|
||||
utilsService "github.com/xmdhs/authlib-skin/service/utils"
|
||||
"github.com/xmdhs/authlib-skin/utils"
|
||||
)
|
||||
|
||||
func (w *WebService) 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)
|
||||
}
|
||||
err = utilsService.DelTexture(ctx, up.ID, textureType, w.client, w.config.TexturePath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("PutTexture: %w", err)
|
||||
}
|
||||
|
||||
hashstr := getHash(texturebyte)
|
||||
if err != nil {
|
||||
return fmt.Errorf("PutTexture: %w", err)
|
||||
}
|
||||
u, err := w.client.User.Query().Where(user.HasProfileWith(userprofile.ID(up.ID))).Only(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("PutTexture: %w", err)
|
||||
}
|
||||
|
||||
err = utils.WithTx(ctx, w.client, func(tx *ent.Tx) error {
|
||||
t, err := tx.Texture.Query().Where(texture.TextureHash(hashstr)).Only(ctx)
|
||||
if err != nil {
|
||||
var ne *ent.NotFoundError
|
||||
if !errors.As(err, &ne) {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if t == nil {
|
||||
t, err = tx.Texture.Create().SetCreatedUser(u).SetTextureHash(hashstr).Save(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
err = tx.UserTexture.Create().SetTexture(t).SetType(textureType).SetUserProfile(up).SetVariant(model).Exec(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("PutTexture: %w", err)
|
||||
}
|
||||
err = createTextureFile(w.config.TexturePath, texturebyte, hashstr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("PutTexture: %w", err)
|
||||
}
|
||||
err = w.cache.Del([]byte("Profile" + t.Subject))
|
||||
if err != nil {
|
||||
return fmt.Errorf("PutTexture: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getHash(b []byte) string {
|
||||
hashed := sha256.Sum256(b)
|
||||
return hex.EncodeToString(hashed[:])
|
||||
}
|
||||
|
||||
func createTextureFile(path string, b []byte, hashstr string) error {
|
||||
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 nil
|
||||
}
|
@ -8,13 +8,12 @@ import (
|
||||
"path/filepath"
|
||||
|
||||
"github.com/samber/lo"
|
||||
"github.com/xmdhs/authlib-skin/config"
|
||||
"github.com/xmdhs/authlib-skin/db/ent"
|
||||
"github.com/xmdhs/authlib-skin/db/ent/texture"
|
||||
"github.com/xmdhs/authlib-skin/db/ent/usertexture"
|
||||
)
|
||||
|
||||
func DelTexture(ctx context.Context, userProfileID int, textureType string, client *ent.Client, config config.Config) error {
|
||||
func DelTexture(ctx context.Context, userProfileID int, textureType string, client *ent.Client, texturePath string) error {
|
||||
// 查找此用户该类型下是否已经存在皮肤
|
||||
tl, err := client.UserTexture.Query().Where(usertexture.And(
|
||||
usertexture.UserProfileID(userProfileID),
|
||||
@ -42,7 +41,7 @@ func DelTexture(ctx context.Context, userProfileID int, textureType string, clie
|
||||
}
|
||||
return fmt.Errorf("DelTexture: %w", err)
|
||||
}
|
||||
path := filepath.Join(config.TexturePath, t.TextureHash[:2], t.TextureHash[2:4], t.TextureHash)
|
||||
path := filepath.Join(texturePath, t.TextureHash[:2], t.TextureHash[2:4], t.TextureHash)
|
||||
err = os.Remove(path)
|
||||
if err != nil && !errors.Is(err, os.ErrNotExist) {
|
||||
return fmt.Errorf("DelTexture: %w", err)
|
||||
|
@ -2,20 +2,13 @@ package yggdrasil
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"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"
|
||||
utilsService "github.com/xmdhs/authlib-skin/service/utils"
|
||||
"github.com/xmdhs/authlib-skin/utils"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -23,7 +16,7 @@ var (
|
||||
)
|
||||
|
||||
func (y *Yggdrasil) delTexture(ctx context.Context, userProfileID int, textureType string) error {
|
||||
return utilsService.DelTexture(ctx, userProfileID, textureType, y.client, y.config)
|
||||
return utilsService.DelTexture(ctx, userProfileID, textureType, y.client, y.config.TexturePath)
|
||||
}
|
||||
|
||||
func (y *Yggdrasil) DelTexture(ctx context.Context, t *model.TokenClaims, textureType string) error {
|
||||
@ -41,81 +34,3 @@ func (y *Yggdrasil) DelTexture(ctx context.Context, t *model.TokenClaims, textur
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (y *Yggdrasil) PutTexture(ctx context.Context, t *model.TokenClaims, texturebyte []byte, model string, textureType string) error {
|
||||
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 := getHash(texturebyte)
|
||||
if err != nil {
|
||||
return fmt.Errorf("PutTexture: %w", err)
|
||||
}
|
||||
u, err := y.client.User.Query().Where(user.HasProfileWith(userprofile.ID(up.ID))).Only(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("PutTexture: %w", err)
|
||||
}
|
||||
|
||||
err = utils.WithTx(ctx, y.client, func(tx *ent.Tx) error {
|
||||
t, err := tx.Texture.Query().Where(texture.TextureHash(hashstr)).Only(ctx)
|
||||
if err != nil {
|
||||
var ne *ent.NotFoundError
|
||||
if !errors.As(err, &ne) {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if t == nil {
|
||||
t, err = tx.Texture.Create().SetCreatedUser(u).SetTextureHash(hashstr).Save(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
err = tx.UserTexture.Create().SetTexture(t).SetType(textureType).SetUserProfile(up).SetVariant(model).Exec(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("PutTexture: %w", err)
|
||||
}
|
||||
err = createTextureFile(y.config.TexturePath, texturebyte, hashstr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("PutTexture: %w", err)
|
||||
}
|
||||
err = y.cache.Del([]byte("Profile" + t.Subject))
|
||||
if err != nil {
|
||||
return fmt.Errorf("PutTexture: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getHash(b []byte) string {
|
||||
hashed := sha256.Sum256(b)
|
||||
return hex.EncodeToString(hashed[:])
|
||||
}
|
||||
|
||||
func createTextureFile(path string, b []byte, hashstr string) error {
|
||||
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 nil
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user