原始提交

This commit is contained in:
2022-02-22 18:04:33 +08:00
commit 014dbe4982
32 changed files with 4057 additions and 0 deletions

View File

@@ -0,0 +1,40 @@
/*
* Copyright (C) 2022. Gardel <sunxinao@hotmail.com> and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package model
import "time"
type AuthenticationSession struct {
ServerId string
Token Token
Ip string
createAt int64
}
func NewAuthenticationSession(serverId string, token *Token, ip string) (session AuthenticationSession) {
session.ServerId = serverId
session.Token = *token
session.Ip = ip
session.createAt = time.Now().UnixMilli()
return session
}
func (s *AuthenticationSession) HasExpired() bool {
d := time.Now().Sub(time.UnixMilli(s.createAt))
return d > 30*time.Second
}

130
model/profile.go Normal file
View File

@@ -0,0 +1,130 @@
/*
* Copyright (C) 2022. Gardel <sunxinao@hotmail.com> and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package model
import (
"encoding/json"
"github.com/google/uuid"
"time"
"yggdrasil-go/util"
)
type Profile struct {
Id uuid.UUID
Name string
ModelType ModelType
Textures map[string]string
}
type ModelType string
const (
STEVE ModelType = "default"
ALEX = "slim"
)
type MetadataType map[string]interface{}
type SkinTexture struct {
Url string `json:"url"`
Metadata *MetadataType `json:"metadata,omitempty"`
}
type CapeTexture struct {
Url string `json:"url"`
}
type TexturesType struct {
SKIN *SkinTexture `json:"SKIN,omitempty"`
CAPE *CapeTexture `json:"CAPE,omitempty"`
}
func NewProfile(id uuid.UUID, name string, modelType ModelType, serializedTextures string) (this Profile) {
this.Id = id
this.Name = name
this.ModelType = modelType
if len(serializedTextures) < 2 {
serializedTextures = "{}"
}
err := json.Unmarshal([]byte(serializedTextures), &this.Textures)
if err != nil {
panic(err)
}
return this
}
type ProfileResponse struct {
Name string `json:"name" binding:"required"`
Id string `json:"id" binding:"required"`
}
func (p *Profile) ToSimpleResponse() ProfileResponse {
return ProfileResponse{
Id: util.UnsignedString(p.Id),
Name: p.Name,
}
}
func (p *Profile) ToCompleteResponse(signed bool, textureBaseUrl string) (map[string]interface{}, error) {
textures := TexturesType{}
if hash, ok := p.Textures["SKIN"]; ok {
skin := SkinTexture{
Url: textureBaseUrl + "/" + hash,
}
if p.ModelType == ALEX {
m := MetadataType{
"model": ALEX,
}
skin.Metadata = &m
}
textures.SKIN = &skin
}
if hash, ok := p.Textures["CAPE"]; ok {
cape := CapeTexture{
Url: textureBaseUrl + "/" + hash,
}
textures.CAPE = &cape
}
texturesStr, err := util.EncodeBase64(util.Property{
Name: "timestamp", Value: time.Now().UnixMilli(),
}, util.Property{
Name: "profileId", Value: util.UnsignedString(p.Id),
}, util.Property{
Name: "profileName", Value: p.Name,
}, util.Property{
Name: "textures", Value: textures,
}, util.Property{
Name: "signatureRequired", Value: signed,
})
if err != nil {
return nil, err
}
properties := util.Properties(signed,
util.StringProperty{Name: "textures", Value: texturesStr},
util.StringProperty{Name: "uploadableTextures", Value: "skin,cape"},
)
return map[string]interface{}{
"id": util.UnsignedString(p.Id),
"name": p.Name,
"properties": properties,
}, nil
}
func (p *Profile) Equals(another *Profile) bool {
return p == another || p.Id == another.Id
}

72
model/texture.go Normal file
View File

@@ -0,0 +1,72 @@
/*
* Copyright (C) 2022. Gardel <sunxinao@hotmail.com> and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package model
import (
"crypto/sha256"
"encoding/hex"
"image"
"image/color"
"time"
)
type Texture struct {
Hash string `gorm:"size:64;primaryKey"`
CreatedAt time.Time
UpdatedAt time.Time
Data []byte `gorm:"not null"`
Used uint `gorm:"not null"`
}
func ComputeTextureId(img image.Image) string {
digest := sha256.New()
bound := img.Bounds()
width := bound.Dx()
height := bound.Dy()
var buf [4096]byte
putInt(buf[:], int32(width))
putInt(buf[4:], int32(height))
var pos = 8
for x := 0; x < width; x++ {
for y := 0; y < height; y++ {
rgba := color.NRGBAModel.Convert(img.At(x, y)).(color.NRGBA)
if rgba.A == 0 {
copy(buf[pos:], []byte{0, 0, 0, 0})
} else {
copy(buf[pos:], []byte{rgba.A, rgba.R, rgba.G, rgba.B})
}
pos += 4
if pos == len(buf) {
pos = 0
digest.Write(buf[:])
}
}
}
if pos > 0 {
digest.Write(buf[:pos])
}
return hex.EncodeToString(digest.Sum(nil))
}
func putInt(buf []byte, n int32) {
buf[0] = byte(n >> 24 & 0xff)
buf[1] = byte(n >> 16 & 0xff)
buf[2] = byte(n >> 8 & 0xff)
buf[3] = byte(n & 0xff)
}

62
model/token.go Normal file
View File

@@ -0,0 +1,62 @@
/*
* Copyright (C) 2022. Gardel <sunxinao@hotmail.com> and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package model
import (
"time"
"yggdrasil-go/util"
)
type Token struct {
createAt int64
ClientToken string
AccessToken string
SelectedProfile Profile
}
type AvailableLevel uint
const (
Valid AvailableLevel = iota
NeedRefresh
Invalid
)
func NewToken(accessToken string, clientToken *string, selectedProfile *Profile) (this Token) {
this.createAt = time.Now().UnixMilli()
if clientToken == nil || (len(*clientToken) == 0) {
this.ClientToken = util.RandomUUID()
} else {
this.ClientToken = *clientToken
}
this.AccessToken = accessToken
this.SelectedProfile = *selectedProfile
return this
}
func (t *Token) GetAvailableLevel() AvailableLevel {
d := time.Now().Sub(time.UnixMilli(t.createAt))
if d > time.Hour*24*30 {
return Invalid
} else if d > time.Hour*24*15 {
return NeedRefresh
} else {
return Valid
}
}

87
model/user.go Normal file
View File

@@ -0,0 +1,87 @@
/*
* Copyright (C) 2022. Gardel <sunxinao@hotmail.com> and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package model
import (
"encoding/json"
"github.com/google/uuid"
"time"
"yggdrasil-go/util"
)
type User struct {
ID uuid.UUID `gorm:"column:id;type:bytes;size:36;primaryKey"`
CreatedAt time.Time
UpdatedAt time.Time
Email string `gorm:"size:64;uniqueIndex:email_idx"`
Password string `gorm:"size:255"`
ProfileName string `gorm:"size:64;uniqueIndex:profile_name_idx"`
ProfileModelType string `gorm:"size:8;default:STEVE"`
SerializedTextures string `gorm:"type:TEXT NULL"`
profile *Profile `gorm:"-"`
}
func (u *User) Profile() (*Profile, error) {
if len(u.ProfileName) == 0 {
return nil, util.NewIllegalArgumentError("Do not have profile")
}
if u.profile != nil {
return u.profile, nil
} else {
var modelType ModelType
if u.ProfileModelType == "ALEX" {
modelType = ALEX
} else {
modelType = STEVE
}
profile := NewProfile(u.ID, u.ProfileName, modelType, u.SerializedTextures)
return &profile, nil
}
}
func (u *User) SetProfile(p *Profile) {
u.profile = p
u.ProfileName = p.Name
switch p.ModelType {
case ALEX:
u.ProfileModelType = "ALEX"
break
case STEVE:
u.ProfileModelType = "STEVE"
break
}
serialized, err := json.Marshal(p.Textures)
if err != nil {
panic("Can not serialize texture")
}
u.SerializedTextures = string(serialized)
}
type UserResponse struct {
Username string `json:"username,omitempty"`
Properties []util.StringProperty `json:"properties"`
Id string `json:"id,omitempty"`
}
func (u *User) ToResponse() UserResponse {
return UserResponse{
Id: util.UnsignedString(u.ID),
Username: u.ProfileName,
Properties: make([]util.StringProperty, 0),
}
}