Added Basic API! Can now get into lobby (C1S2)

This commit is contained in:
eccentric 2023-11-03 23:48:50 +00:00
parent a857ce7e63
commit 113c68a38d
20 changed files with 817 additions and 15 deletions

View File

@ -15,6 +15,13 @@ type CS struct {
Output struct {
Level string
}
API struct {
Host string
Port string
}
JWT struct {
Secret string
}
}
var (
@ -52,4 +59,19 @@ func LoadConfig() {
if Config.Output.Level != "dev" && Config.Output.Level != "prod" && Config.Output.Level != "time" && Config.Output.Level != "info" {
panic("Output Level must be either dev or prod")
}
Config.API.Host = cfg.Section("api").Key("host").String()
if Config.API.Host == "" {
panic("API Host is empty")
}
Config.API.Port = cfg.Section("api").Key("port").String()
if Config.API.Port == "" {
panic("API Port is empty")
}
Config.JWT.Secret = cfg.Section("jwt").Key("secret").String()
if Config.JWT.Secret == "" {
panic("JWT Secret is empty")
}
}

29
aid/errors.go Normal file
View File

@ -0,0 +1,29 @@
package aid
func ErrorBadRequest(errorMessage string) JSON {
return JSON{
"errorCode": "errors.com.epicgames.bad_request",
"errorMessage": errorMessage,
"numericErrorCode": 1001,
"originatingService": "fortnite",
"intent": "prod-live",
}
}
var (
ErrorNotFound = JSON{
"errorCode": "errors.com.epicgames.common.not_found",
"errorMessage": "Resource Not found",
"numericErrorCode": 1004,
"originatingService": "fortnite",
"intent": "prod-live",
}
ErrorInternalServer = map[string]interface{}{
"errorCode": "errors.com.epicgames.common.server_error",
"errorMessage": "Internal Server Error",
"numericErrorCode": 1000,
"originatingService": "fortnite",
"intent": "prod-live",
}
)

31
aid/fiber.go Normal file
View File

@ -0,0 +1,31 @@
package aid
import (
"time"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/limiter"
"github.com/gofiber/fiber/v2/middleware/logger"
)
func FiberLogger() fiber.Handler {
return logger.New(logger.Config{
Format: "${status} ${path}\n",
})
}
func FiberLimiter() fiber.Handler {
return limiter.New(limiter.Config{
Max: 100,
Expiration: 1 * time.Minute,
})
}
func FiberCors() fiber.Handler {
return func(c *fiber.Ctx) error {
c.Set("Access-Control-Allow-Origin", "*")
c.Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
c.Set("Access-Control-Allow-Headers", "Content-Type, Authorization, Origin, Accept, X-Requested-With")
return c.Next()
}
}

44
aid/token.go Normal file
View File

@ -0,0 +1,44 @@
package aid
import (
"fmt"
"github.com/golang-jwt/jwt/v5"
)
func JWTSign(m JSON) (string, error) {
claims := jwt.MapClaims{}
for k, v := range m {
claims[k] = v
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
return token.SignedString([]byte(Config.JWT.Secret))
}
func JWTVerify(tokenString string) (JSON, error) {
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
return []byte(Config.JWT.Secret), nil
})
if err != nil {
return nil, err
}
if !token.Valid {
return nil, fmt.Errorf("invalid token")
}
claims, ok := token.Claims.(jwt.MapClaims)
if !ok {
return nil, fmt.Errorf("invalid claims")
}
json := JSON{}
for k, v := range claims {
json[k] = v
}
return json, nil
}

View File

@ -11,4 +11,15 @@ drop=false
; info = everything
; time = only time taken
; prod = only errors
level="info"
level="info"
[api]
; port to listen on
port=80
; host to listen on
; only change if you know what you're doing
host="0.0.0.0"
[jwt]
; secret for jwt signing
secret="secret"

17
go.mod
View File

@ -3,21 +3,28 @@ module github.com/ectrc/snow
go 1.21.3
require (
github.com/andybalholm/brotli v1.0.6 // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/google/uuid v1.3.1 // indirect
github.com/gofiber/fiber/v2 v2.50.0 // indirect
github.com/golang-jwt/jwt/v5 v5.0.0 // indirect
github.com/google/uuid v1.4.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
github.com/jackc/pgx/v5 v5.4.3 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/klauspost/compress v1.17.2 // indirect
github.com/lib/pq v1.10.9 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.17 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.15 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/pelletier/go-toml/v2 v2.1.0 // indirect
github.com/philhofer/fwd v1.1.2 // indirect
github.com/r3labs/diff/v3 v3.0.1 // indirect
github.com/rivo/uniseg v0.4.4 // indirect
github.com/rs/zerolog v1.26.0 // indirect
github.com/sagikazarmark/locafero v0.3.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
@ -29,6 +36,10 @@ require (
github.com/spf13/pflag v1.0.5 // indirect
github.com/spf13/viper v1.6.3 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
github.com/tinylib/msgp v1.1.8 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasthttp v1.50.0 // indirect
github.com/valyala/tcplisten v1.0.0 // indirect
github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
github.com/x448/float16 v0.8.4 // indirect
@ -36,7 +47,7 @@ require (
go.uber.org/multierr v1.9.0 // indirect
golang.org/x/crypto v0.9.0 // indirect
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
golang.org/x/sys v0.12.0 // indirect
golang.org/x/sys v0.13.0 // indirect
golang.org/x/text v0.13.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect

46
go.sum
View File

@ -43,6 +43,8 @@ github.com/Vilsol/ue4pak v0.1.5 h1:XE17rYgieBh67vpB2lZI6ARv3KxMzAelWh/RDA9O7Ms=
github.com/Vilsol/ue4pak v0.1.5/go.mod h1:e/4WiPI80PbsP2+IOWYoqwiZT+lKAcoEr0wmVY3ROLQ=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/andybalholm/brotli v1.0.6 h1:Yf9fFpf49Zrxb9NlQaluyE92/+X7UVHlhMNJN2sxfOI=
github.com/andybalholm/brotli v1.0.6/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
@ -85,8 +87,12 @@ github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gofiber/fiber/v2 v2.50.0 h1:ia0JaB+uw3GpNSCR5nvC5dsaxXjRU5OEu36aytx+zGw=
github.com/gofiber/fiber/v2 v2.50.0/go.mod h1:21eytvay9Is7S6z+OgPi7c7n4++tnClWmhpimVHMimw=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/golang-jwt/jwt/v5 v5.0.0 h1:1n1XNM9hk7O9mnQoNBGolZvzebBQ7p93ULHRc28XJUE=
github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
@ -142,6 +148,8 @@ github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm4
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4=
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4=
github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
@ -174,6 +182,8 @@ github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfV
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.17.2 h1:RlWWUY/Dr4fL8qk9YG7DTZ7PDgME2V4csBXA8L/ixi4=
github.com/klauspost/compress v1.17.2/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
@ -190,6 +200,10 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
@ -202,6 +216,8 @@ github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4=
github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
github.com/philhofer/fwd v1.1.2 h1:bnDivRJ1EWPjUIRXV5KfORO897HTbpFAQddBdE8t7Gw=
github.com/philhofer/fwd v1.1.2/go.mod h1:qkPdfjR2SIEbspLqpe1tO4n5yICnr2DY7mqEx2tUTP0=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg=
@ -218,6 +234,9 @@ github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7z
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/r3labs/diff/v3 v3.0.1 h1:CBKqf3XmNRHXKmdU7mZP1w7TV0pDyVCis1AUHtA4Xtg=
github.com/r3labs/diff/v3 v3.0.1/go.mod h1:f1S9bourRbiM66NskseyUdo0fTmEE0qKrikYJX63dgo=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
@ -268,7 +287,15 @@ github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXl
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/tinylib/msgp v1.1.8 h1:FCXC1xanKO4I8plpHGH2P7koL/RzZs12l/+r7vakfm0=
github.com/tinylib/msgp v1.1.8/go.mod h1:qkpG+2ldGg4xRFmx+jfTvZPxfGFhi64BcnL9vkCm/Tw=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasthttp v1.50.0 h1:H7fweIlBm0rXLs2q0XbalvJ6r0CUPFWK3/bB4N13e9M=
github.com/valyala/fasthttp v1.50.0/go.mod h1:k2zXd82h/7UZc3VOdJ2WaUqt1uZ/XpXAfE9i+HBC3lA=
github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
github.com/vmihailenco/msgpack/v5 v5.3.5 h1:5gO0H1iULLWGhs2H5tbAHIZTV8/cYafcFOr9znI5mJU=
github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc=
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
@ -282,6 +309,7 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
@ -303,6 +331,7 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g=
golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
@ -342,6 +371,8 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@ -378,6 +409,8 @@ golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@ -398,6 +431,8 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@ -437,12 +472,20 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@ -451,6 +494,7 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@ -506,6 +550,8 @@ golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4f
golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

164
handlers/auth.go Normal file
View File

@ -0,0 +1,164 @@
package handlers
import (
"encoding/base64"
"math/rand"
"strings"
"time"
"github.com/ectrc/snow/aid"
p "github.com/ectrc/snow/person"
"github.com/gofiber/fiber/v2"
)
var (
oatuhTokenGrantTypes = map[string]func(c *fiber.Ctx, body *OAuthTokenBody) error{
"client_credentials": PostOAuthTokenClientCredentials,
"password": PostOAuthTokenPassword,
}
)
type OAuthTokenBody struct {
GrantType string `form:"grant_type" binding:"required"`
Username string `form:"username"`
Password string `form:"password"`
}
func PostOAuthToken(c *fiber.Ctx) error {
var body OAuthTokenBody
if err := c.BodyParser(&body); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(aid.ErrorBadRequest("Invalid Request Body"))
}
if action, ok := oatuhTokenGrantTypes[body.GrantType]; ok {
return action(c, &body)
}
return c.Status(fiber.StatusBadRequest).JSON(aid.ErrorBadRequest("Invalid Grant Type"))
}
func PostOAuthTokenClientCredentials(c *fiber.Ctx, body *OAuthTokenBody) error {
credentials, err := aid.JWTSign(aid.JSON{
"snow_id": 0, // custom
"t": "s",
"am": "client_credentials", // authorization method
"ic": true, // internal client
"mver": false, // mobile version
"clsvc": "snow", // client service
"clid": c.IP(), // client id
"jti": rand.Int63(), // jwt id
"p": base64.StdEncoding.EncodeToString([]byte(c.IP())), // payload
"hours_expire": 1,
"creation_date": time.Now().Format("2006-01-02T15:04:05.999Z"),
})
if err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(aid.ErrorInternalServer)
}
return c.Status(fiber.StatusOK).JSON(aid.JSON{
"access_token": "eg1~"+credentials,
"token_type": "bearer",
"client_id": c.IP(),
"client_service": "snow",
"internal_client": true,
"expires_in": 3600,
"expires_at": time.Now().Add(time.Hour).Format("2006-01-02T15:04:05.999Z"),
})
}
func PostOAuthTokenPassword(c *fiber.Ctx, body *OAuthTokenBody) error {
if body.Username == "" || body.Password == "" {
return c.Status(fiber.StatusBadRequest).JSON(aid.ErrorBadRequest("Username/Password is empty"))
}
person := p.FindByDisplay(strings.Split(body.Username, "@")[0])
if person == nil {
return c.Status(fiber.StatusBadRequest).JSON(aid.ErrorBadRequest("No Account Found"))
}
if person.AccessKey == "" {
return c.Status(fiber.StatusBadRequest).JSON(aid.ErrorBadRequest("Activation Required"))
}
if person.AccessKey != body.Password {
return c.Status(fiber.StatusBadRequest).JSON(aid.ErrorBadRequest("Invalid Access Key"))
}
access, err := aid.JWTSign(aid.JSON{
"snow_id": person.ID, // custom
"iai": person.ID, // account id
"dn": person.DisplayName, // display name
"t": "s",
"am": "password", // authorization method
"ic": true, // internal client
"mver": false, // mobile version
"clsvc": "snow", // client service
"app": "com.epicgames.fortnite", // app name
"clid": c.IP(), // client id
"dvid": "default", // device id
"jti": rand.Int63(), // jwt id
"p": base64.StdEncoding.EncodeToString([]byte(c.IP())), // payload
"sec": 1, // security level
"hours_expire": 24,
"creation_date": time.Now().Format("2006-01-02T15:04:05.999Z"),
})
if err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(aid.ErrorInternalServer)
}
refresh, err := aid.JWTSign(aid.JSON{
"snow_id": person.ID, // custom
"sub": person.ID, // account id
"clid": c.IP(), // client id
"jti": rand.Int63(), // jwt id
"t": "s",
"am": "refresh_token", // authorization method
"hours_expire": 24,
"creation_date": time.Now().Format("2006-01-02T15:04:05.999Z"),
})
if err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(aid.ErrorInternalServer)
}
return c.Status(fiber.StatusOK).JSON(aid.JSON{
"access_token": "eg1~"+access,
"account_id": person.ID,
"client_id": c.IP(),
"client_service": "snow",
"device_id": "default",
"display_name": person.DisplayName,
"expires_at": time.Now().Add(time.Hour * 24).Format("2006-01-02T15:04:05.999Z"),
"expires_in": 86400,
"internal_client": true,
"refresh_expires": 86400,
"refresh_expires_at": time.Now().Add(time.Hour * 24).Format("2006-01-02T15:04:05.999Z"),
"refresh_token": "eg1~"+refresh,
"token_type": "bearer",
})
}
func DeleteOAuthSessions(c *fiber.Ctx) error {
return c.Status(fiber.StatusOK).JSON(aid.JSON{})
}
func GetPublicAccount(c *fiber.Ctx) error {
person := p.Find(c.Params("accountId"))
if person == nil {
return c.Status(fiber.StatusBadRequest).JSON(aid.ErrorBadRequest("No Account Found"))
}
return c.Status(fiber.StatusOK).JSON(aid.JSON{
"id": person.ID,
"displayName": person.DisplayName,
})
}
func GetPublicAccountExternalAuths(c *fiber.Ctx) error {
person := p.Find(c.Params("accountId"))
if person == nil {
return c.Status(fiber.StatusBadRequest).JSON(aid.ErrorBadRequest("No Account Found"))
}
return c.Status(fiber.StatusOK).JSON([]aid.JSON{})
}

36
handlers/common.go Normal file
View File

@ -0,0 +1,36 @@
package handlers
import (
"github.com/ectrc/snow/aid"
"github.com/gofiber/fiber/v2"
)
func AnyNoContent(c *fiber.Ctx) error {
return c.SendStatus(fiber.StatusNoContent)
}
func PostTryPlayOnPlatform(c *fiber.Ctx) error {
return c.Status(fiber.StatusOK).SendString("true")
}
func GetEnabledFeatures(c *fiber.Ctx) error {
return c.Status(fiber.StatusOK).JSON([]string{})
}
func PostGrantAccess(c *fiber.Ctx) error {
return c.Status(fiber.StatusOK).SendString("true")
}
func GetAccountReceipts(c *fiber.Ctx) error {
return c.Status(fiber.StatusOK).JSON([]string{})
}
func GetSessionFindPlayer(c *fiber.Ctx) error {
return c.Status(fiber.StatusOK).JSON([]string{})
}
func GetVersionCheck(c *fiber.Ctx) error {
return c.Status(fiber.StatusOK).JSON(aid.JSON{
"type": "NO_UPDATE",
})
}

14
handlers/lightswitch.go Normal file
View File

@ -0,0 +1,14 @@
package handlers
import (
"github.com/ectrc/snow/aid"
"github.com/gofiber/fiber/v2"
)
func GetLightswitchBulkStatus(c *fiber.Ctx) error {
return c.Status(fiber.StatusOK).JSON([]aid.JSON{{
"serviceInstanceId": "fortnite",
"status": "UP",
"banned": false,
}})
}

57
handlers/profile.go Normal file
View File

@ -0,0 +1,57 @@
package handlers
import (
"time"
"github.com/ectrc/snow/aid"
p "github.com/ectrc/snow/person"
"github.com/gofiber/fiber/v2"
)
var (
profileActions = map[string]func(c *fiber.Ctx, person *p.Person, profile *p.Profile) error {
"QueryProfile": PostQueryProfileAction,
"ClientQuestLogin": PostQueryProfileAction,
}
)
func PostProfileAction(c *fiber.Ctx) error {
person := p.Find(c.Params("accountId"))
if person == nil {
return c.Status(404).JSON(aid.ErrorBadRequest("No Account Found"))
}
profile := person.GetProfileFromType(c.Query("profileId"))
if profile == nil {
return c.Status(404).JSON(aid.ErrorBadRequest("No Profile Found"))
}
snapshot := profile.Snapshot()
if action, ok := profileActions[c.Params("action")]; ok {
err := action(c, person, profile)
if err != nil {
return err
}
}
profile.Diff(snapshot)
profile.Revision++
return c.Status(200).JSON(aid.JSON{
"profileId": profile.Type,
"profileRevision": profile.Revision,
"profileCommandRevision": profile.Revision,
"profileChangesBaseRevision": profile.Revision - 1,
"profileChanges": profile.Changes,
"multiUpdate": []aid.JSON{},
"notifications": []aid.JSON{},
"responseVersion": 1,
"serverTime": time.Now().Format("2006-01-02T15:04:05.999Z"),
})
}
func PostQueryProfileAction(c *fiber.Ctx, person *p.Person, profile *p.Profile) error {
profile.Changes = []interface{}{}
profile.CreateFullProfileUpdateChange()
return nil
}

32
handlers/storage.go Normal file
View File

@ -0,0 +1,32 @@
package handlers
import (
"github.com/ectrc/snow/aid"
"github.com/gofiber/fiber/v2"
)
func GetCloudStorageFiles(c *fiber.Ctx) error {
return c.Status(fiber.StatusOK).JSON([]aid.JSON{})
}
func GetCloudStorageConfig(c *fiber.Ctx) error {
return c.Status(fiber.StatusOK).JSON(aid.JSON{
"enumerateFilesPath": "/api/cloudstorage/system",
"enableMigration": true,
"enableWrites": true,
"epicAppName": "Live",
"isAuthenticated": true,
"disableV2": true,
"lastUpdated": "2021-01-01T00:00:00Z",
"transports": []string{},
})
}
func GetCloudStorageFile(c *fiber.Ctx) error {
fileName := c.Params("fileName")
if fileName == "" {
return c.Status(fiber.StatusBadRequest).JSON(aid.ErrorBadRequest)
}
return c.Status(fiber.StatusOK).JSON(aid.JSON{})
}

57
main.go
View File

@ -2,8 +2,11 @@ package main
import (
"github.com/ectrc/snow/aid"
"github.com/ectrc/snow/handlers"
"github.com/ectrc/snow/person"
"github.com/ectrc/snow/storage"
"github.com/gofiber/fiber/v2"
)
func init() {
aid.LoadConfig()
@ -36,17 +39,55 @@ func init() {
}
func init() {
if aid.Config.Database.DropAllTables {
person.NewFortnitePerson("ac", "ket")
}
aid.PrintTime("Loading all persons from database", func() {
for _, person := range person.AllFromDatabase() {
aid.Print("Loaded person: " + person.DisplayName)
}
})
go storage.Cache.CacheKiller()
}
func main() {
aid.PrintTime("Fetching Persons", func() {
users := person.AllFromDatabase()
aid.Print("Found", len(users), "users")
for _, user := range users {
aid.Print(user.ID)
}
})
r := fiber.New()
// aid.WaitForExit()
r.Use(aid.FiberLogger())
r.Use(aid.FiberLimiter())
r.Use(aid.FiberCors())
account := r.Group("/account/api")
account.Get("/public/account/:accountId", handlers.GetPublicAccount)
account.Get("/public/account/:accountId/externalAuths", handlers.GetPublicAccountExternalAuths)
account.Post("/oauth/token", handlers.PostOAuthToken)
account.Delete("/oauth/sessions/kill", handlers.DeleteOAuthSessions)
fortnite := r.Group("/fortnite/api")
fortnite.Get("/receipts/v1/account/:accountId/receipts", handlers.GetAccountReceipts)
fortnite.Get("/versioncheck/*", handlers.GetVersionCheck)
matchmaking := fortnite.Group("/matchmaking")
matchmaking.Get("/session/findPlayer/:accountId", handlers.GetSessionFindPlayer)
storage := fortnite.Group("/cloudstorage")
storage.Get("/system", handlers.GetCloudStorageFiles)
storage.Get("/system/config", handlers.GetCloudStorageConfig)
storage.Get("/system/:fileName", handlers.GetCloudStorageFile)
game := fortnite.Group("/game/v2")
game.Post("/tryPlayOnPlatform/account/:accountId", handlers.PostTryPlayOnPlatform)
game.Post("/grant_access/:accountId", handlers.PostGrantAccess)
game.Get("/enabled_features", handlers.GetEnabledFeatures)
profile := game.Group("/profile/:accountId")
profile.Post("/client/:action", handlers.PostProfileAction)
lightswitch := r.Group("/lightswitch/api")
lightswitch.Get("/service/bulk/status", handlers.GetLightswitchBulkStatus)
r.All("*", func(c *fiber.Ctx) error { return c.Status(fiber.StatusNotFound).JSON(aid.ErrorNotFound) })
r.Listen(aid.Config.API.Host + aid.Config.API.Port)
}

79
person/fortnite.go Normal file
View File

@ -0,0 +1,79 @@
package person
import "github.com/ectrc/snow/aid"
func NewFortnitePerson(displayName string, key string) {
person := NewPerson()
person.DisplayName = displayName
person.AccessKey = key
character := NewItem("AthenaCharacter:CID_001_Athena_Commando_F_Default", 1)
pickaxe := NewItem("AthenaPickaxe:DefaultPickaxe", 1)
glider := NewItem("AthenaGlider:DefaultGlider", 1)
default_dance := NewItem("AthenaDance:EID_DanceMoves", 1)
person.AthenaProfile.Items.AddItem(character)
person.AthenaProfile.Items.AddItem(pickaxe)
person.AthenaProfile.Items.AddItem(glider)
person.AthenaProfile.Items.AddItem(default_dance)
person.CommonCoreProfile.Items.AddItem(NewItem("Currency:MtxPurchased", 0))
person.Loadout.Character = character.ID
person.Loadout.Pickaxe = pickaxe.ID
person.Loadout.Glider = glider.ID
person.Loadout.Dances[0] = default_dance.ID
person.AthenaProfile.Attributes.AddAttribute(NewAttribute("mfa_reward_claimed", true))
person.AthenaProfile.Attributes.AddAttribute(NewAttribute("rested_xp_overflow", 0))
person.AthenaProfile.Attributes.AddAttribute(NewAttribute("lifetime_wins", 0))
person.AthenaProfile.Attributes.AddAttribute(NewAttribute("party_assist_quest", ""))
person.AthenaProfile.Attributes.AddAttribute(NewAttribute("quest_manager", aid.JSON{}))
person.AthenaProfile.Attributes.AddAttribute(NewAttribute("inventory_limit_bonus", 0))
person.AthenaProfile.Attributes.AddAttribute(NewAttribute("daily_rewards", []aid.JSON{}))
person.AthenaProfile.Attributes.AddAttribute(NewAttribute("competitive_identity", aid.JSON{}))
person.AthenaProfile.Attributes.AddAttribute(NewAttribute("season_update", 0))
person.AthenaProfile.Attributes.AddAttribute(NewAttribute("season_num", 2))
person.AthenaProfile.Attributes.AddAttribute(NewAttribute("permissions", []aid.JSON{}))
person.AthenaProfile.Attributes.AddAttribute(NewAttribute("loadouts", []aid.JSON{}))
person.AthenaProfile.Attributes.AddAttribute(NewAttribute("last_applied_loadout", ""))
person.AthenaProfile.Attributes.AddAttribute(NewAttribute("active_loadout_index", 0))
person.AthenaProfile.Attributes.AddAttribute(NewAttribute("accountLevel", 1))
person.AthenaProfile.Attributes.AddAttribute(NewAttribute("level", 1))
person.AthenaProfile.Attributes.AddAttribute(NewAttribute("xp", 0))
person.AthenaProfile.Attributes.AddAttribute(NewAttribute("xp_overflow", 0))
person.AthenaProfile.Attributes.AddAttribute(NewAttribute("rested_xp", 0))
person.AthenaProfile.Attributes.AddAttribute(NewAttribute("rested_xp_mult", 0))
person.AthenaProfile.Attributes.AddAttribute(NewAttribute("rested_xp_exchange", 0))
person.AthenaProfile.Attributes.AddAttribute(NewAttribute("book_purchased", false))
person.AthenaProfile.Attributes.AddAttribute(NewAttribute("book_level", 1))
person.AthenaProfile.Attributes.AddAttribute(NewAttribute("book_xp", 0))
person.AthenaProfile.Attributes.AddAttribute(NewAttribute("favorite_character", person.Loadout.Character))
person.AthenaProfile.Attributes.AddAttribute(NewAttribute("favorite_backpack", person.Loadout.Backpack))
person.AthenaProfile.Attributes.AddAttribute(NewAttribute("favorite_pickaxe", person.Loadout.Pickaxe))
person.AthenaProfile.Attributes.AddAttribute(NewAttribute("favorite_glider", person.Loadout.Glider))
person.AthenaProfile.Attributes.AddAttribute(NewAttribute("favorite_skydivecontrail", person.Loadout.SkyDiveContrail))
person.AthenaProfile.Attributes.AddAttribute(NewAttribute("favorite_dance", person.Loadout.Dances))
person.AthenaProfile.Attributes.AddAttribute(NewAttribute("favorite_itemwraps", person.Loadout.ItemWraps))
person.AthenaProfile.Attributes.AddAttribute(NewAttribute("favorite_loadingscreen", person.Loadout.LoadingScreen))
person.AthenaProfile.Attributes.AddAttribute(NewAttribute("favorite_musicpack", person.Loadout.MusicPack))
person.AthenaProfile.Attributes.AddAttribute(NewAttribute("banner_icon", person.Loadout.BannerIcon))
person.AthenaProfile.Attributes.AddAttribute(NewAttribute("banner_color", person.Loadout.BannerColor))
person.CommonCoreProfile.Attributes.AddAttribute(NewAttribute("mfa_enabled", true))
person.CommonCoreProfile.Attributes.AddAttribute(NewAttribute("mtx_affiliate", ""))
person.CommonCoreProfile.Attributes.AddAttribute(NewAttribute("mtx_purchase_history", aid.JSON{
"refundsUsed": 0,
"refundCredits": 3,
"purchases": []any{},
}))
person.CommonCoreProfile.Attributes.AddAttribute(NewAttribute("current_mtx_platform", "EpicPC"))
person.CommonCoreProfile.Attributes.AddAttribute(NewAttribute("allowed_to_receive_gifts", true))
person.CommonCoreProfile.Attributes.AddAttribute(NewAttribute("allowed_to_send_gifts", true))
person.CommonCoreProfile.Attributes.AddAttribute(NewAttribute("gift_history", aid.JSON{}))
person.Save()
}

View File

@ -8,8 +8,11 @@ import (
type Person struct {
ID string
DisplayName string
AccessKey string
AthenaProfile *Profile
CommonCoreProfile *Profile
CommonPublicProfile *Profile
Profile0 *Profile
Loadout *Loadout
}
@ -22,13 +25,16 @@ func NewPerson() *Person {
return &Person{
ID: uuid.New().String(),
DisplayName: uuid.New().String(),
AccessKey: "",
AthenaProfile: NewProfile("athena"),
CommonCoreProfile: NewProfile("common_core"),
CommonPublicProfile: NewProfile("common_public"),
Profile0: NewProfile("profile0"),
Loadout: NewLoadout(),
}
}
func FromDatabase(personId string) *Person {
func Find(personId string) *Person {
person := storage.Repo.GetPerson(personId)
if person == nil {
return nil
@ -37,6 +43,8 @@ func FromDatabase(personId string) *Person {
loadout := FromDatabaseLoadout(&person.Loadout)
athenaProfile := NewProfile("athena")
commonCoreProfile := NewProfile("common_core")
commonPublicProfile := NewProfile("common_public")
profile0 := NewProfile("profile0")
for _, profile := range person.Profiles {
if profile.Type == "athena" {
@ -48,13 +56,72 @@ func FromDatabase(personId string) *Person {
commonCoreProfile.ID = profile.ID
commonCoreProfile = FromDatabaseProfile(&profile)
}
if profile.Type == "common_public" {
commonPublicProfile.ID = profile.ID
commonPublicProfile = FromDatabaseProfile(&profile)
}
if profile.Type == "profile0" {
profile0.ID = profile.ID
profile0 = FromDatabaseProfile(&profile)
}
}
return &Person{
ID: person.ID,
DisplayName: person.DisplayName,
AccessKey: person.AccessKey,
AthenaProfile: athenaProfile,
CommonCoreProfile: commonCoreProfile,
CommonPublicProfile: commonPublicProfile,
Profile0: profile0,
Loadout: loadout,
}
}
func FindByDisplay(displayName string) *Person {
person := storage.Repo.GetPersonByDisplay(displayName)
if person == nil {
return nil
}
loadout := FromDatabaseLoadout(&person.Loadout)
athenaProfile := NewProfile("athena")
commonCoreProfile := NewProfile("common_core")
commonPublicProfile := NewProfile("common_public")
profile0 := NewProfile("profile0")
for _, profile := range person.Profiles {
if profile.Type == "athena" {
athenaProfile.ID = profile.ID
athenaProfile = FromDatabaseProfile(&profile)
}
if profile.Type == "common_core" {
commonCoreProfile.ID = profile.ID
commonCoreProfile = FromDatabaseProfile(&profile)
}
if profile.Type == "common_public" {
commonPublicProfile.ID = profile.ID
commonPublicProfile = FromDatabaseProfile(&profile)
}
if profile.Type == "profile0" {
profile0.ID = profile.ID
profile0 = FromDatabaseProfile(&profile)
}
}
return &Person{
ID: person.ID,
DisplayName: person.DisplayName,
AccessKey: person.AccessKey,
AthenaProfile: athenaProfile,
CommonCoreProfile: commonCoreProfile,
CommonPublicProfile: commonPublicProfile,
Profile0: profile0,
Loadout: loadout,
}
}
@ -63,12 +130,27 @@ func AllFromDatabase() []*Person {
var persons []*Person
for _, person := range storage.Repo.GetAllPersons() {
persons = append(persons, FromDatabase(person.ID))
persons = append(persons, Find(person.ID))
}
return persons
}
func (p *Person) GetProfileFromType(profileType string) *Profile {
switch profileType {
case "athena":
return p.AthenaProfile
case "common_core":
return p.CommonCoreProfile
case "common_public":
return p.CommonPublicProfile
case "profile0":
return p.Profile0
}
return nil
}
func (p *Person) Save() {
storage.Repo.SavePerson(p.ToDatabase())
}
@ -79,11 +161,14 @@ func (p *Person) ToDatabase() *storage.DB_Person {
DisplayName: p.DisplayName,
Profiles: []storage.DB_Profile{},
Loadout: *p.Loadout.ToDatabase(),
AccessKey: p.AccessKey,
}
profilesToConvert := map[string]*Profile{
"common_core": p.CommonCoreProfile,
"athena": p.AthenaProfile,
"athena": p.AthenaProfile,
"common_public": p.CommonPublicProfile,
"profile0": p.Profile0,
}
for profileType, profile := range profilesToConvert {

View File

@ -3,6 +3,7 @@ package person
import (
"fmt"
"github.com/ectrc/snow/aid"
"github.com/ectrc/snow/storage"
"github.com/google/uuid"
"github.com/r3labs/diff/v3"
@ -10,6 +11,7 @@ import (
type Profile struct {
ID string
PersonID string
Items *ItemMutex
Gifts *GiftMutex
Quests *QuestMutex
@ -22,6 +24,7 @@ type Profile struct {
func NewProfile(profile string) *Profile {
return &Profile{
ID: uuid.New().String(),
PersonID: "",
Items: NewItemMutex(profile),
Gifts: NewGiftMutex(),
Quests: NewQuestMutex(),
@ -62,6 +65,7 @@ func FromDatabaseProfile(profile *storage.DB_Profile) *Profile {
return &Profile{
ID: profile.ID,
PersonID: profile.PersonID,
Items: items,
Gifts: gifts,
Quests: quests,
@ -71,6 +75,44 @@ func FromDatabaseProfile(profile *storage.DB_Profile) *Profile {
}
}
func (p *Profile) GenerateFortniteProfileEntry() aid.JSON {
items := aid.JSON{}
attributes := aid.JSON{}
p.Items.RangeItems(func(id string, item *Item) bool {
items[id] = item.GenerateFortniteItemEntry()
return true
})
p.Quests.RangeQuests(func(id string, quest *Quest) bool {
items[id] = quest.GenerateFortniteQuestEntry()
return true
})
p.Gifts.RangeGifts(func(id string, gift *Gift) bool {
items[id] = gift.GenerateFortniteGiftEntry()
return true
})
p.Attributes.RangeAttributes(func(id string, attribute *Attribute) bool {
attributes[attribute.Key] = attribute.Value
return true
})
return aid.JSON{
"profileId": p.Type,
"accountId": p.PersonID,
"rvn": p.Revision,
"commandRevision": p.Revision,
"wipeNumber": 0,
"version": "",
"items": items,
"stats": aid.JSON{
"attributes": attributes,
},
}
}
func (p *Profile) Save() {
//storage.Repo.SaveProfile(p.ToDatabase())
}
@ -259,6 +301,13 @@ func (p *Profile) CreateItemAttributeChangedChange(item *Item, attribute string)
})
}
func (p *Profile) CreateFullProfileUpdateChange() {
p.Changes = append(p.Changes, FullProfileUpdate{
ChangeType: "fullProfileUpdate",
Profile: p.GenerateFortniteProfileEntry(),
})
}
type Loadout struct {
ID string
Character string

View File

@ -47,6 +47,20 @@ func (m *PersonsCache) GetPerson(id string) *DB_Person {
return nil
}
func (m *PersonsCache) GetPersonByDisplay(displayName string) *DB_Person {
var person *DB_Person
m.Range(func(key, value interface{}) bool {
if value.(*CacheEntry).Entry.(*DB_Person).DisplayName == displayName {
person = value.(*CacheEntry).Entry.(*DB_Person)
return false
}
return true
})
return person
}
func (m *PersonsCache) SavePerson(p *DB_Person) {
m.Store(p.ID, &CacheEntry{
Entry: p,

View File

@ -48,6 +48,26 @@ func (s *PostgresStorage) GetPerson(personId string) *DB_Person {
return &dbPerson
}
func (s *PostgresStorage) GetPersonByDisplay(displayName string) *DB_Person {
var dbPerson DB_Person
s.Postgres.
Preload("Profiles").
Preload("Profiles.Items.Variants").
Preload("Profiles.Gifts.Loot").
Preload("Profiles.Attributes").
Preload("Profiles.Items").
Preload("Profiles.Gifts").
Preload("Profiles.Quests").
Where("display_name = ?", displayName).
Find(&dbPerson)
if dbPerson.ID == "" {
return nil
}
return &dbPerson
}
func (s *PostgresStorage) GetAllPersons() []*DB_Person {
var dbPersons []*DB_Person

View File

@ -9,6 +9,7 @@ type Storage interface {
Migrate(table interface{}, tableName string)
GetPerson(personId string) *DB_Person
GetPersonByDisplay(displayName string) *DB_Person
GetAllPersons() []*DB_Person
SavePerson(person *DB_Person)
@ -45,6 +46,21 @@ func (r *Repository) GetPerson(personId string) *DB_Person {
return nil
}
func (r *Repository) GetPersonByDisplay(displayName string) *DB_Person {
cachePerson := Cache.GetPersonByDisplay(displayName)
if cachePerson != nil {
return cachePerson
}
storagePerson := r.Storage.GetPersonByDisplay(displayName)
if storagePerson != nil {
Cache.SavePerson(storagePerson)
return storagePerson
}
return nil
}
func (r *Repository) GetAllPersons() []*DB_Person {
return r.Storage.GetAllPersons()
}

View File

@ -9,6 +9,7 @@ type Tabler interface {
type DB_Person struct {
ID string
DisplayName string
AccessKey string
Profiles []DB_Profile `gorm:"foreignkey:PersonID"`
Loadout DB_Loadout `gorm:"foreignkey:PersonID"`
}