获取皮肤接口

This commit is contained in:
xmdhs 2023-09-10 17:13:10 +08:00
parent 7ed8f72640
commit fc727c87a3
No known key found for this signature in database
GPG Key ID: E809D6D43DEFCC95
8 changed files with 151 additions and 8 deletions

View File

@ -19,4 +19,5 @@ type Config struct {
MaxIpUser int MaxIpUser int
RsaPriKey string RsaPriKey string
TexturePath string TexturePath string
TextureBaseUrl string
} }

View File

@ -36,5 +36,7 @@ func (UserProfile) Edges() []ent.Edge {
func (UserProfile) Indexes() []ent.Index { func (UserProfile) Indexes() []ent.Index {
return []ent.Index{ return []ent.Index{
index.Edges("user"), index.Edges("user"),
index.Fields("name"),
index.Fields("uuid"),
} }
} }

View File

@ -18,11 +18,11 @@ func (UserTexture) Fields() []ent.Field {
return []ent.Field{ return []ent.Field{
field.Int("user_profile_id"), field.Int("user_profile_id"),
field.Int("texture_id"), field.Int("texture_id"),
// 皮肤 or 披风 // skin or cape
field.String("type").SchemaType(map[string]string{ field.String("type").SchemaType(map[string]string{
dialect.MySQL: "VARCHAR(10)", dialect.MySQL: "VARCHAR(10)",
}), }),
// slim or // slim or ""
field.String("variant").SchemaType(map[string]string{ field.String("variant").SchemaType(map[string]string{
dialect.MySQL: "VARCHAR(10)", dialect.MySQL: "VARCHAR(10)",
}), }),

View File

@ -119,3 +119,37 @@ func (y *Yggdrasil) Refresh() httprouter.Handle {
w.Write(b) w.Write(b)
} }
} }
func (y *Yggdrasil) GetProfile() httprouter.Handle {
return func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
ctx := r.Context()
uuid := p.ByName("uuid")
unsigned := r.FormValue("unsigned")
unsignedBool := true
switch unsigned {
case "true":
case "false":
unsignedBool = false
default:
y.logger.DebugContext(ctx, "unsigned 参数类型错误")
handleYgError(ctx, w, yggdrasil.Error{ErrorMessage: "unsigned 参数类型错误"}, 400)
return
}
u, err := y.yggdrasilService.GetProfile(ctx, uuid, unsignedBool, r.Host)
if err != nil {
if errors.Is(err, yggdrasilS.ErrNotUser) {
y.logger.DebugContext(ctx, err.Error())
w.WriteHeader(204)
return
}
y.logger.WarnContext(ctx, err.Error())
handleYgError(ctx, w, yggdrasil.Error{ErrorMessage: err.Error()}, 500)
return
}
b, _ := json.Marshal(u)
w.Write(b)
}
}

View File

@ -52,3 +52,15 @@ type RefreshToken struct {
RequestUser bool `json:"requestUser"` RequestUser bool `json:"requestUser"`
SelectedProfile TokenProfile `json:"selectedProfile"` SelectedProfile TokenProfile `json:"selectedProfile"`
} }
type UserInfo struct {
ID string `json:"id"`
Name string `json:"name"`
Properties []UserProperties `json:"properties"`
}
type UserProperties struct {
Name string `json:"name"`
Value string `json:"value"`
Signature string `json:"signature,omitempty"`
}

View File

@ -0,0 +1,28 @@
package yggdrasil
import (
"encoding/base64"
"encoding/json"
)
type UserTextures struct {
// UUID
ProfileID string `json:"profileId"`
ProfileName string `json:"profileName"`
Textures map[string]Textures `json:"textures"`
// 时间戳 毫秒
Timestamp string `json:"timestamp"`
}
type Textures struct {
Url string `json:"url"`
Metadata map[string]string `json:"metadata"`
}
func (u UserTextures) Base64() string {
b, err := json.Marshal(u)
if err != nil {
panic(err)
}
return base64.StdEncoding.EncodeToString(b)
}

View File

@ -27,9 +27,13 @@ func newYggdrasil(r *httprouter.Router, handelY yggdrasil.Yggdrasil) error {
r.POST("/api/yggdrasil/authserver/validate", warpHJSON(handelY.Validate())) r.POST("/api/yggdrasil/authserver/validate", warpHJSON(handelY.Validate()))
r.POST("/api/yggdrasil/authserver/signout", warpHJSON(handelY.Signout())) r.POST("/api/yggdrasil/authserver/signout", warpHJSON(handelY.Signout()))
r.POST("/api/yggdrasil/authserver/invalidate", handelY.Invalidate()) r.POST("/api/yggdrasil/authserver/invalidate", handelY.Invalidate())
r.POST("/api/yggdrasil/authserver/refresh", handelY.Refresh()) r.POST("/api/yggdrasil/authserver/refresh", warpHJSON(handelY.Refresh()))
r.PUT("/api/yggdrasil/api/user/profile/:uuid/:textureType", handelY.PutTexture()) r.PUT("/api/yggdrasil/api/user/profile/:uuid/:textureType", handelY.PutTexture())
r.DELETE("/api/yggdrasil/api/user/profile/:uuid/:textureType", handelY.DelTexture()) r.DELETE("/api/yggdrasil/api/user/profile/:uuid/:textureType", warpHJSON(handelY.DelTexture()))
r.GET("/sessionserver/session/minecraft/profile/:uuid", handelY.GetProfile())
r.GET("/api/yggdrasil", func(w http.ResponseWriter, r *http.Request, p httprouter.Params) { r.GET("/api/yggdrasil", func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
w.Write([]byte(`{ w.Write([]byte(`{
"meta": { "meta": {

View File

@ -4,6 +4,7 @@ import (
"context" "context"
"errors" "errors"
"fmt" "fmt"
"path"
"strconv" "strconv"
"strings" "strings"
"time" "time"
@ -16,11 +17,13 @@ import (
"github.com/xmdhs/authlib-skin/model/yggdrasil" "github.com/xmdhs/authlib-skin/model/yggdrasil"
sutils "github.com/xmdhs/authlib-skin/service/utils" sutils "github.com/xmdhs/authlib-skin/service/utils"
"github.com/xmdhs/authlib-skin/utils" "github.com/xmdhs/authlib-skin/utils"
"github.com/xmdhs/authlib-skin/utils/sign"
) )
var ( var (
ErrRate = errors.New("频率限制") ErrRate = errors.New("频率限制")
ErrPassWord = errors.New("错误的密码或邮箱") ErrPassWord = errors.New("错误的密码或邮箱")
ErrNotUser = errors.New("没有这个用户")
) )
func (y *Yggdrasil) validatePass(cxt context.Context, email, pass string) (*ent.User, error) { func (y *Yggdrasil) validatePass(cxt context.Context, email, pass string) (*ent.User, error) {
@ -164,3 +167,62 @@ func (y *Yggdrasil) Refresh(ctx context.Context, token yggdrasil.RefreshToken) (
}, },
}, nil }, nil
} }
func (y *Yggdrasil) GetProfile(ctx context.Context, uuid string, unsigned bool, host string) (yggdrasil.UserInfo, error) {
up, err := y.client.UserProfile.Query().Where(userprofile.UUID(uuid)).WithUsertexture().WithTexture().Only(ctx)
if err != nil {
var nf *ent.NotFoundError
if errors.As(err, &nf) {
return yggdrasil.UserInfo{}, fmt.Errorf("GetProfile: %w", ErrNotUser)
}
return yggdrasil.UserInfo{}, fmt.Errorf("GetProfile: %w", err)
}
var baseURl string
if y.config.TextureBaseUrl == "" {
baseURl = path.Join(host, "textures")
}
ut := yggdrasil.UserTextures{
ProfileID: up.UUID,
ProfileName: up.Name,
Textures: map[string]yggdrasil.Textures{},
Timestamp: strconv.FormatInt(time.Now().UnixMilli(), 10),
}
for _, v := range up.Edges.Usertexture {
hashstr := v.Edges.Texture.TextureHash
t := yggdrasil.Textures{
Url: path.Join(baseURl, hashstr[:2], hashstr[2:4], hashstr),
Metadata: map[string]string{},
}
if v.Variant == "slim" {
t.Metadata["model"] = "slim"
}
ut.Textures[strings.ToTitle(v.Type)] = t
}
texturesBase64 := ut.Base64()
var signStr string
if !unsigned {
s := sign.NewAuthlibSignWithKey(y.prikey)
signStr, err = s.Sign([]byte(texturesBase64))
if err != nil {
return yggdrasil.UserInfo{}, fmt.Errorf("GetProfile: %w", err)
}
}
uinfo := yggdrasil.UserInfo{
ID: up.UUID,
Name: up.Name,
Properties: []yggdrasil.UserProperties{
{
Name: "textures",
Value: texturesBase64,
Signature: signStr,
},
},
}
return uinfo, nil
}