获取皮肤接口
This commit is contained in:
parent
7ed8f72640
commit
fc727c87a3
@ -19,4 +19,5 @@ type Config struct {
|
|||||||
MaxIpUser int
|
MaxIpUser int
|
||||||
RsaPriKey string
|
RsaPriKey string
|
||||||
TexturePath string
|
TexturePath string
|
||||||
|
TextureBaseUrl string
|
||||||
}
|
}
|
||||||
|
@ -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"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)",
|
||||||
}),
|
}),
|
||||||
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -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"`
|
||||||
|
}
|
||||||
|
28
model/yggdrasil/textures.go
Normal file
28
model/yggdrasil/textures.go
Normal 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)
|
||||||
|
}
|
@ -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": {
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user