Major code cleanup and switch to Fiber
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
package conf
|
||||
package main
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
@@ -17,6 +17,8 @@ type RouteConfig struct {
|
||||
}
|
||||
|
||||
type Configuration struct {
|
||||
Host string `yaml:"host"`
|
||||
Port uint16 `yaml:"port"`
|
||||
Redis struct {
|
||||
URI string `yaml:"uri"`
|
||||
Database int `yaml:"database"`
|
||||
@@ -39,7 +41,7 @@ type Configuration struct {
|
||||
}
|
||||
|
||||
func (c *Configuration) ReadFile(file string) error {
|
||||
data, err := ioutil.ReadFile(file)
|
||||
data, err := os.ReadFile(file)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
88
src/main.go
88
src/main.go
@@ -3,24 +3,25 @@ package main
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
"net/http"
|
||||
|
||||
"github.com/buaazp/fasthttprouter"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/gofiber/fiber/v2/middleware/cors"
|
||||
"github.com/gofiber/fiber/v2/middleware/recover"
|
||||
"github.com/joho/godotenv"
|
||||
"github.com/mineatar-io/api-server/src/conf"
|
||||
"github.com/mineatar-io/api-server/src/redis"
|
||||
"github.com/mineatar-io/api-server/src/routes"
|
||||
"github.com/mineatar-io/api-server/src/util"
|
||||
"github.com/valyala/fasthttp"
|
||||
)
|
||||
|
||||
var (
|
||||
host string = "127.0.0.1"
|
||||
port uint16 = 3000
|
||||
config *conf.Configuration = &conf.Configuration{}
|
||||
r *redis.Redis = &redis.Redis{}
|
||||
app *fiber.App = fiber.New(fiber.Config{
|
||||
DisableStartupMessage: true,
|
||||
ErrorHandler: func(ctx *fiber.Ctx, err error) error {
|
||||
log.Println(ctx.Request().URI(), err)
|
||||
|
||||
return ctx.SendStatus(http.StatusInternalServerError)
|
||||
},
|
||||
})
|
||||
r *Redis = &Redis{}
|
||||
config *Configuration = &Configuration{}
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -32,64 +33,29 @@ func init() {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
start := time.Now()
|
||||
|
||||
if err = r.Connect(config.Redis.URI, config.Redis.Database); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
log.Printf("Successfully connected to Redis (%s)\n", time.Since(start))
|
||||
log.Println("Successfully connected to Redis")
|
||||
|
||||
if value, ok := os.LookupEnv("HOST"); ok {
|
||||
host = value
|
||||
}
|
||||
|
||||
if value, ok := os.LookupEnv("PORT"); ok {
|
||||
parsedValue, err := strconv.ParseUint(value, 10, 16)
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
port = uint16(parsedValue)
|
||||
}
|
||||
|
||||
routes.Init(r, config)
|
||||
util.Init(r, config)
|
||||
}
|
||||
|
||||
func middleware(next fasthttp.RequestHandler) fasthttp.RequestHandler {
|
||||
return func(ctx *fasthttp.RequestCtx) {
|
||||
ctx.Response.Header.Set("Access-Control-Allow-Origin", "*")
|
||||
ctx.Response.Header.Set("Access-Control-Allow-Headers", "*")
|
||||
ctx.Response.Header.Set("Access-Control-Allow-Methods", "GET,POST,HEAD,OPTIONS")
|
||||
ctx.Response.Header.Set("Access-Control-Expose-Headers", "X-Cache-Hit")
|
||||
|
||||
if util.Debug {
|
||||
log.Printf("%s %s (%s) - %s\n", ctx.Method(), ctx.URI(), ctx.RemoteAddr(), ctx.UserAgent())
|
||||
}
|
||||
|
||||
next(ctx)
|
||||
}
|
||||
app.Use(recover.New())
|
||||
app.Use(cors.New(cors.Config{
|
||||
AllowOrigins: "*",
|
||||
AllowMethods: "HEAD,OPTIONS,GET",
|
||||
ExposeHeaders: "Content-Type,Content-Disposition,X-Cache-Hit",
|
||||
}))
|
||||
}
|
||||
|
||||
func main() {
|
||||
defer r.Close()
|
||||
|
||||
router := fasthttprouter.New()
|
||||
instanceID, err := GetInstanceID()
|
||||
|
||||
router.GET("/ping", routes.PingHandler)
|
||||
router.GET("/uuid/:user", routes.UUIDHandler)
|
||||
router.GET("/skin/:user", routes.SkinHandler)
|
||||
router.GET("/face/:user", routes.FaceHandler)
|
||||
router.GET("/head/:user", routes.HeadHandler)
|
||||
router.GET("/body/full/:user", routes.FullBodyHandler)
|
||||
router.GET("/body/front/:user", routes.FrontBodyHandler)
|
||||
router.GET("/body/back/:user", routes.BackBodyHandler)
|
||||
router.GET("/body/left/:user", routes.LeftBodyHandler)
|
||||
router.GET("/body/right/:user", routes.RightBodyHandler)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
log.Printf("Listening on %s:%d\n", host, port)
|
||||
|
||||
log.Fatal(fasthttp.ListenAndServe(fmt.Sprintf("%s:%d", host, port), middleware(router.Handler)))
|
||||
log.Printf("Listening on %s:%d\n", config.Host, config.Port+instanceID)
|
||||
log.Fatal(app.Listen(fmt.Sprintf("%s:%d", config.Host, config.Port+instanceID)))
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package redis
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@@ -8,27 +8,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/go-redis/redis/v8"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
)
|
||||
|
||||
var (
|
||||
redisGetMetric = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "redis_get_count",
|
||||
Help: "The amount of Redis GET requests",
|
||||
})
|
||||
redisSetMetric = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "redis_set_count",
|
||||
Help: "The amount of Redis SET requests",
|
||||
})
|
||||
redisExistsMetric = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "redis_exists_count",
|
||||
Help: "The amount of Redis EXIST requests",
|
||||
})
|
||||
redisDeleteMetric = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "redis_delete_count",
|
||||
Help: "The amount of Redis DELETE requests",
|
||||
})
|
||||
)
|
||||
|
||||
type Redis struct {
|
||||
@@ -51,8 +30,6 @@ func (r *Redis) Connect(uri string, database int) error {
|
||||
}
|
||||
|
||||
func (r *Redis) GetString(key string) (string, bool, error) {
|
||||
redisGetMetric.Inc()
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
|
||||
|
||||
defer cancel()
|
||||
@@ -73,8 +50,6 @@ func (r *Redis) GetString(key string) (string, bool, error) {
|
||||
}
|
||||
|
||||
func (r *Redis) GetBytes(key string) ([]byte, bool, error) {
|
||||
redisGetMetric.Inc()
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
|
||||
|
||||
defer cancel()
|
||||
@@ -101,8 +76,6 @@ func (r *Redis) GetBytes(key string) ([]byte, bool, error) {
|
||||
}
|
||||
|
||||
func (r *Redis) GetNRGBA(key string) (*image.NRGBA, bool, error) {
|
||||
redisGetMetric.Inc()
|
||||
|
||||
value, ok, err := r.GetBytes(key)
|
||||
|
||||
if err != nil {
|
||||
@@ -131,8 +104,6 @@ func (r *Redis) GetNRGBA(key string) (*image.NRGBA, bool, error) {
|
||||
}
|
||||
|
||||
func (r *Redis) Exists(key string) (bool, error) {
|
||||
redisExistsMetric.Inc()
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
|
||||
|
||||
defer cancel()
|
||||
@@ -143,8 +114,6 @@ func (r *Redis) Exists(key string) (bool, error) {
|
||||
}
|
||||
|
||||
func (r *Redis) Delete(key string) error {
|
||||
redisDeleteMetric.Inc()
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
|
||||
|
||||
defer cancel()
|
||||
@@ -153,8 +122,6 @@ func (r *Redis) Delete(key string) error {
|
||||
}
|
||||
|
||||
func (r *Redis) Set(key string, value interface{}, ttl time.Duration) error {
|
||||
redisSetMetric.Inc()
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
|
||||
|
||||
defer cancel()
|
||||
543
src/routes.go
Normal file
543
src/routes.go
Normal file
@@ -0,0 +1,543 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/mineatar-io/skin-render"
|
||||
)
|
||||
|
||||
func init() {
|
||||
app.Get("/ping", PingHandler)
|
||||
app.Get("/uuid/:user", UUIDHandler)
|
||||
app.Get("/skin/:user", SkinHandler)
|
||||
app.Get("/face/:user", FaceHandler)
|
||||
app.Get("/head/:user", HeadHandler)
|
||||
app.Get("/body/full/:user", FullBodyHandler)
|
||||
app.Get("/body/front/:user", FrontBodyHandler)
|
||||
app.Get("/body/back/:user", BackBodyHandler)
|
||||
app.Get("/body/left/:user", LeftBodyHandler)
|
||||
app.Get("/body/right/:user", RightBodyHandler)
|
||||
app.Use(NotFoundHandler)
|
||||
}
|
||||
|
||||
func PingHandler(ctx *fiber.Ctx) error {
|
||||
return ctx.SendString("Pong!")
|
||||
}
|
||||
|
||||
func FullBodyHandler(ctx *fiber.Ctx) error {
|
||||
user := ctx.Params("user")
|
||||
|
||||
opts := ParseQueryParams(ctx, config.Routes.FullBody)
|
||||
|
||||
uuid, ok, err := LookupUUID(user)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !ok && !opts.Fallback {
|
||||
return ctx.SendStatus(http.StatusNotFound)
|
||||
}
|
||||
|
||||
cacheKey := fmt.Sprintf("result:fullbody-%d-%t-%s", opts.Scale, opts.Overlay, uuid)
|
||||
|
||||
{
|
||||
cache, ok, err := r.GetBytes(cacheKey)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if ok {
|
||||
if opts.Download {
|
||||
ctx.Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s.png"`, user))
|
||||
}
|
||||
|
||||
ctx.Set("X-Cache-Hit", "TRUE")
|
||||
|
||||
return ctx.Type("png").Send(cache)
|
||||
}
|
||||
}
|
||||
|
||||
rawSkin, slim, err := GetPlayerSkin(uuid)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
render := skin.RenderBody(rawSkin, skin.Options{
|
||||
Overlay: opts.Overlay,
|
||||
Slim: slim,
|
||||
Scale: opts.Scale,
|
||||
})
|
||||
|
||||
data, err := EncodePNG(render)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = r.Set(cacheKey, data, config.Cache.RenderCacheDuration); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if opts.Download {
|
||||
ctx.Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s.png"`, user))
|
||||
}
|
||||
|
||||
ctx.Set("X-Cache-Hit", "FALSE")
|
||||
|
||||
return ctx.Type("png").Send(data)
|
||||
}
|
||||
|
||||
func FrontBodyHandler(ctx *fiber.Ctx) error {
|
||||
user := ctx.Params("user")
|
||||
|
||||
opts := ParseQueryParams(ctx, config.Routes.FrontBody)
|
||||
|
||||
uuid, ok, err := LookupUUID(user)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !ok && !opts.Fallback {
|
||||
return ctx.SendStatus(http.StatusNotFound)
|
||||
}
|
||||
|
||||
cacheKey := fmt.Sprintf("result:frontbody-%d-%t-%s", opts.Scale, opts.Overlay, uuid)
|
||||
|
||||
{
|
||||
cache, ok, err := r.GetBytes(cacheKey)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if ok {
|
||||
if opts.Download {
|
||||
ctx.Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s.png"`, user))
|
||||
}
|
||||
|
||||
ctx.Set("X-Cache-Hit", "TRUE")
|
||||
|
||||
return ctx.Type("png").Send(cache)
|
||||
}
|
||||
}
|
||||
|
||||
rawSkin, slim, err := GetPlayerSkin(uuid)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
render := skin.RenderFrontBody(rawSkin, skin.Options{
|
||||
Overlay: opts.Overlay,
|
||||
Slim: slim,
|
||||
Scale: opts.Scale,
|
||||
})
|
||||
|
||||
data, err := EncodePNG(render)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = r.Set(cacheKey, data, config.Cache.RenderCacheDuration); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if opts.Download {
|
||||
ctx.Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s.png"`, user))
|
||||
}
|
||||
|
||||
ctx.Set("X-Cache-Hit", "FALSE")
|
||||
|
||||
return ctx.Type("png").Send(data)
|
||||
}
|
||||
|
||||
func BackBodyHandler(ctx *fiber.Ctx) error {
|
||||
user := ctx.Params("user")
|
||||
|
||||
opts := ParseQueryParams(ctx, config.Routes.BackBody)
|
||||
|
||||
uuid, ok, err := LookupUUID(user)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !ok && !opts.Fallback {
|
||||
return ctx.SendStatus(http.StatusNotFound)
|
||||
}
|
||||
|
||||
cacheKey := fmt.Sprintf("result:backbody-%d-%t-%s", opts.Scale, opts.Overlay, uuid)
|
||||
|
||||
{
|
||||
cache, ok, err := r.GetBytes(cacheKey)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if ok {
|
||||
if opts.Download {
|
||||
ctx.Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s.png"`, user))
|
||||
}
|
||||
|
||||
ctx.Set("X-Cache-Hit", "TRUE")
|
||||
|
||||
return ctx.Type("png").Send(cache)
|
||||
}
|
||||
}
|
||||
|
||||
rawSkin, slim, err := GetPlayerSkin(uuid)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
render := skin.RenderBackBody(rawSkin, skin.Options{
|
||||
Overlay: opts.Overlay,
|
||||
Slim: slim,
|
||||
Scale: opts.Scale,
|
||||
})
|
||||
|
||||
data, err := EncodePNG(render)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = r.Set(cacheKey, data, config.Cache.RenderCacheDuration); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if opts.Download {
|
||||
ctx.Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s.png"`, user))
|
||||
}
|
||||
|
||||
ctx.Set("X-Cache-Hit", "FALSE")
|
||||
|
||||
return ctx.Type("png").Send(data)
|
||||
}
|
||||
|
||||
func LeftBodyHandler(ctx *fiber.Ctx) error {
|
||||
user := ctx.Params("user")
|
||||
|
||||
opts := ParseQueryParams(ctx, config.Routes.LeftBody)
|
||||
|
||||
uuid, ok, err := LookupUUID(user)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !ok && !opts.Fallback {
|
||||
return ctx.SendStatus(http.StatusNotFound)
|
||||
}
|
||||
|
||||
cacheKey := fmt.Sprintf("result:leftbody-%d-%t-%s", opts.Scale, opts.Overlay, uuid)
|
||||
|
||||
{
|
||||
cache, ok, err := r.GetBytes(cacheKey)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if ok {
|
||||
if opts.Download {
|
||||
ctx.Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s.png"`, user))
|
||||
}
|
||||
|
||||
ctx.Set("X-Cache-Hit", "TRUE")
|
||||
|
||||
return ctx.Type("png").Send(cache)
|
||||
}
|
||||
}
|
||||
|
||||
rawSkin, slim, err := GetPlayerSkin(uuid)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
render := skin.RenderLeftBody(rawSkin, skin.Options{
|
||||
Overlay: opts.Overlay,
|
||||
Slim: slim,
|
||||
Scale: opts.Scale,
|
||||
})
|
||||
|
||||
data, err := EncodePNG(render)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = r.Set(cacheKey, data, config.Cache.RenderCacheDuration); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if opts.Download {
|
||||
ctx.Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s.png"`, user))
|
||||
}
|
||||
|
||||
ctx.Set("X-Cache-Hit", "FALSE")
|
||||
|
||||
return ctx.Type("png").Send(data)
|
||||
}
|
||||
|
||||
func RightBodyHandler(ctx *fiber.Ctx) error {
|
||||
user := ctx.Params("user")
|
||||
|
||||
opts := ParseQueryParams(ctx, config.Routes.RightBody)
|
||||
|
||||
uuid, ok, err := LookupUUID(user)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !ok && !opts.Fallback {
|
||||
return ctx.SendStatus(http.StatusNotFound)
|
||||
}
|
||||
|
||||
cacheKey := fmt.Sprintf("result:rightbody-%d-%t-%s", opts.Scale, opts.Overlay, uuid)
|
||||
|
||||
{
|
||||
cache, ok, err := r.GetBytes(cacheKey)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if ok {
|
||||
if opts.Download {
|
||||
ctx.Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s.png"`, user))
|
||||
}
|
||||
|
||||
ctx.Set("X-Cache-Hit", "TRUE")
|
||||
|
||||
return ctx.Type("png").Send(cache)
|
||||
}
|
||||
}
|
||||
|
||||
rawSkin, slim, err := GetPlayerSkin(uuid)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
render := skin.RenderRightBody(rawSkin, skin.Options{
|
||||
Overlay: opts.Overlay,
|
||||
Slim: slim,
|
||||
Scale: opts.Scale,
|
||||
})
|
||||
|
||||
data, err := EncodePNG(render)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = r.Set(cacheKey, data, config.Cache.RenderCacheDuration); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if opts.Download {
|
||||
ctx.Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s.png"`, user))
|
||||
}
|
||||
|
||||
ctx.Set("X-Cache-Hit", "FALSE")
|
||||
|
||||
return ctx.Type("png").Send(data)
|
||||
}
|
||||
|
||||
func FaceHandler(ctx *fiber.Ctx) error {
|
||||
user := ctx.Params("user")
|
||||
|
||||
opts := ParseQueryParams(ctx, config.Routes.Face)
|
||||
|
||||
uuid, ok, err := LookupUUID(user)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !ok && !opts.Fallback {
|
||||
return ctx.SendStatus(http.StatusNotFound)
|
||||
}
|
||||
|
||||
cacheKey := fmt.Sprintf("result:face-%d-%t-%s", opts.Scale, opts.Overlay, uuid)
|
||||
|
||||
{
|
||||
cache, ok, err := r.GetBytes(cacheKey)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if ok {
|
||||
if opts.Download {
|
||||
ctx.Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s.png"`, user))
|
||||
}
|
||||
|
||||
ctx.Set("X-Cache-Hit", "TRUE")
|
||||
|
||||
return ctx.Type("png").Send(cache)
|
||||
}
|
||||
}
|
||||
|
||||
rawSkin, slim, err := GetPlayerSkin(uuid)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
render := skin.RenderFace(rawSkin, skin.Options{
|
||||
Overlay: opts.Overlay,
|
||||
Slim: slim,
|
||||
Scale: opts.Scale,
|
||||
})
|
||||
|
||||
data, err := EncodePNG(render)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = r.Set(cacheKey, data, config.Cache.RenderCacheDuration); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if opts.Download {
|
||||
ctx.Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s.png"`, user))
|
||||
}
|
||||
|
||||
ctx.Set("X-Cache-Hit", "FALSE")
|
||||
|
||||
return ctx.Type("png").Send(data)
|
||||
}
|
||||
|
||||
func HeadHandler(ctx *fiber.Ctx) error {
|
||||
user := ctx.Params("user")
|
||||
|
||||
opts := ParseQueryParams(ctx, config.Routes.Head)
|
||||
|
||||
uuid, ok, err := LookupUUID(user)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !ok && !opts.Fallback {
|
||||
return ctx.SendStatus(http.StatusNotFound)
|
||||
}
|
||||
|
||||
cacheKey := fmt.Sprintf("result:head-%d-%t-%s", opts.Scale, opts.Overlay, uuid)
|
||||
|
||||
{
|
||||
cache, ok, err := r.GetBytes(cacheKey)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if ok {
|
||||
if opts.Download {
|
||||
ctx.Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s.png"`, user))
|
||||
}
|
||||
|
||||
ctx.Set("X-Cache-Hit", "TRUE")
|
||||
|
||||
return ctx.Type("png").Send(cache)
|
||||
}
|
||||
}
|
||||
|
||||
rawSkin, slim, err := GetPlayerSkin(uuid)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
render := skin.RenderHead(rawSkin, skin.Options{
|
||||
Overlay: opts.Overlay,
|
||||
Slim: slim,
|
||||
Scale: opts.Scale,
|
||||
})
|
||||
|
||||
data, err := EncodePNG(render)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = r.Set(cacheKey, data, config.Cache.RenderCacheDuration); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if opts.Download {
|
||||
ctx.Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s.png"`, user))
|
||||
}
|
||||
|
||||
ctx.Set("X-Cache-Hit", "FALSE")
|
||||
|
||||
return ctx.Type("png").Send(data)
|
||||
}
|
||||
|
||||
func SkinHandler(ctx *fiber.Ctx) error {
|
||||
user := ctx.Params("user")
|
||||
|
||||
opts := ParseQueryParams(ctx, config.Routes.RawSkin)
|
||||
|
||||
uuid, ok, err := LookupUUID(user)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !ok && !opts.Fallback {
|
||||
return ctx.SendStatus(http.StatusNotFound)
|
||||
}
|
||||
|
||||
rawSkin, _, err := GetPlayerSkin(uuid)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
data, err := EncodePNG(rawSkin)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if opts.Download {
|
||||
ctx.Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s.png"`, user))
|
||||
}
|
||||
|
||||
return ctx.Type("png").Send(data)
|
||||
}
|
||||
|
||||
func UUIDHandler(ctx *fiber.Ctx) error {
|
||||
user := ctx.Params("user")
|
||||
|
||||
uuid, ok, err := LookupUUID(user)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !ok {
|
||||
return ctx.SendStatus(http.StatusNotFound)
|
||||
}
|
||||
|
||||
return ctx.SendString(uuid)
|
||||
}
|
||||
|
||||
func NotFoundHandler(ctx *fiber.Ctx) error {
|
||||
return ctx.SendStatus(http.StatusNotFound)
|
||||
}
|
||||
@@ -1,451 +0,0 @@
|
||||
package routes
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"github.com/mineatar-io/api-server/src/util"
|
||||
"github.com/mineatar-io/skin-render"
|
||||
"github.com/valyala/fasthttp"
|
||||
)
|
||||
|
||||
func FullBodyHandler(ctx *fasthttp.RequestCtx) {
|
||||
user := ctx.UserValue("user").(string)
|
||||
|
||||
opts := util.ParseQueryParams(ctx, config.Routes.FullBody)
|
||||
|
||||
uuid, ok, err := util.LookupUUID(user)
|
||||
|
||||
if err != nil {
|
||||
util.WriteError(ctx, err, http.StatusInternalServerError)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if !ok && !opts.Fallback {
|
||||
util.WriteError(ctx, nil, http.StatusNotFound)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
cacheKey := fmt.Sprintf("result:fullbody-%d-%t-%s", opts.Scale, opts.Overlay, uuid)
|
||||
|
||||
{
|
||||
cache, ok, err := r.GetBytes(cacheKey)
|
||||
|
||||
if err != nil {
|
||||
util.WriteError(ctx, err, http.StatusInternalServerError)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if ok {
|
||||
if opts.Download {
|
||||
ctx.Response.Header.Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s.png"`, user))
|
||||
}
|
||||
|
||||
if util.Debug {
|
||||
log.Printf("Retrieved cache for full body render for '%s'\n", uuid)
|
||||
}
|
||||
|
||||
ctx.Response.Header.Set("X-Cache-Hit", "TRUE")
|
||||
ctx.SetContentType("image/png")
|
||||
ctx.SetBody(cache)
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
rawSkin, slim, err := util.GetPlayerSkin(uuid)
|
||||
|
||||
if err != nil {
|
||||
util.WriteError(ctx, err, http.StatusInternalServerError)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
render := skin.RenderBody(rawSkin, skin.Options{
|
||||
Overlay: opts.Overlay,
|
||||
Slim: slim,
|
||||
Scale: opts.Scale,
|
||||
})
|
||||
|
||||
if util.Debug {
|
||||
log.Printf("Rendered full body image for '%s'\n", uuid)
|
||||
}
|
||||
|
||||
data, err := util.EncodePNG(render)
|
||||
|
||||
if err != nil {
|
||||
util.WriteError(ctx, err, http.StatusInternalServerError)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if err = r.Set(cacheKey, data, config.Cache.RenderCacheDuration); err != nil {
|
||||
util.WriteError(ctx, err, http.StatusInternalServerError)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if opts.Download {
|
||||
ctx.Response.Header.Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s.png"`, user))
|
||||
}
|
||||
|
||||
ctx.Response.Header.Set("X-Cache-Hit", "FALSE")
|
||||
ctx.SetContentType("image/png")
|
||||
ctx.SetBody(data)
|
||||
}
|
||||
|
||||
func FrontBodyHandler(ctx *fasthttp.RequestCtx) {
|
||||
user := ctx.UserValue("user").(string)
|
||||
|
||||
opts := util.ParseQueryParams(ctx, config.Routes.FrontBody)
|
||||
|
||||
uuid, ok, err := util.LookupUUID(user)
|
||||
|
||||
if err != nil {
|
||||
util.WriteError(ctx, err, http.StatusInternalServerError)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if !ok && !opts.Fallback {
|
||||
util.WriteError(ctx, nil, http.StatusNotFound)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
cacheKey := fmt.Sprintf("result:frontbody-%d-%t-%s", opts.Scale, opts.Overlay, uuid)
|
||||
|
||||
{
|
||||
cache, ok, err := r.GetBytes(cacheKey)
|
||||
|
||||
if err != nil {
|
||||
util.WriteError(ctx, err, http.StatusInternalServerError)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if ok {
|
||||
if opts.Download {
|
||||
ctx.Response.Header.Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s.png"`, user))
|
||||
}
|
||||
|
||||
if util.Debug {
|
||||
log.Printf("Retrieved cache for front body render for '%s'\n", uuid)
|
||||
}
|
||||
|
||||
ctx.Response.Header.Set("X-Cache-Hit", "TRUE")
|
||||
ctx.SetContentType("image/png")
|
||||
ctx.SetBody(cache)
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
rawSkin, slim, err := util.GetPlayerSkin(uuid)
|
||||
|
||||
if err != nil {
|
||||
util.WriteError(ctx, err, http.StatusInternalServerError)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
render := skin.RenderFrontBody(rawSkin, skin.Options{
|
||||
Overlay: opts.Overlay,
|
||||
Slim: slim,
|
||||
Scale: opts.Scale,
|
||||
})
|
||||
|
||||
if util.Debug {
|
||||
log.Printf("Rendered front body image for '%s'\n", uuid)
|
||||
}
|
||||
|
||||
data, err := util.EncodePNG(render)
|
||||
|
||||
if err != nil {
|
||||
util.WriteError(ctx, err, http.StatusInternalServerError)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if err = r.Set(cacheKey, data, config.Cache.RenderCacheDuration); err != nil {
|
||||
util.WriteError(ctx, err, http.StatusInternalServerError)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if opts.Download {
|
||||
ctx.Response.Header.Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s.png"`, user))
|
||||
}
|
||||
|
||||
ctx.Response.Header.Set("X-Cache-Hit", "FALSE")
|
||||
ctx.SetContentType("image/png")
|
||||
ctx.SetBody(data)
|
||||
}
|
||||
|
||||
func BackBodyHandler(ctx *fasthttp.RequestCtx) {
|
||||
user := ctx.UserValue("user").(string)
|
||||
|
||||
opts := util.ParseQueryParams(ctx, config.Routes.BackBody)
|
||||
|
||||
uuid, ok, err := util.LookupUUID(user)
|
||||
|
||||
if err != nil {
|
||||
util.WriteError(ctx, err, http.StatusInternalServerError)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if !ok && !opts.Fallback {
|
||||
util.WriteError(ctx, nil, http.StatusNotFound)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
cacheKey := fmt.Sprintf("result:backbody-%d-%t-%s", opts.Scale, opts.Overlay, uuid)
|
||||
|
||||
{
|
||||
cache, ok, err := r.GetBytes(cacheKey)
|
||||
|
||||
if err != nil {
|
||||
util.WriteError(ctx, err, http.StatusInternalServerError)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if ok {
|
||||
if opts.Download {
|
||||
ctx.Response.Header.Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s.png"`, user))
|
||||
}
|
||||
|
||||
if util.Debug {
|
||||
log.Printf("Retrieved cache for back body render for '%s'\n", uuid)
|
||||
}
|
||||
|
||||
ctx.Response.Header.Set("X-Cache-Hit", "TRUE")
|
||||
ctx.SetContentType("image/png")
|
||||
ctx.SetBody(cache)
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
rawSkin, slim, err := util.GetPlayerSkin(uuid)
|
||||
|
||||
if err != nil {
|
||||
util.WriteError(ctx, err, http.StatusInternalServerError)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
render := skin.RenderBackBody(rawSkin, skin.Options{
|
||||
Overlay: opts.Overlay,
|
||||
Slim: slim,
|
||||
Scale: opts.Scale,
|
||||
})
|
||||
|
||||
if util.Debug {
|
||||
log.Printf("Rendered back body image for '%s'\n", uuid)
|
||||
}
|
||||
|
||||
data, err := util.EncodePNG(render)
|
||||
|
||||
if err != nil {
|
||||
util.WriteError(ctx, err, http.StatusInternalServerError)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if err = r.Set(cacheKey, data, config.Cache.RenderCacheDuration); err != nil {
|
||||
util.WriteError(ctx, err, http.StatusInternalServerError)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if opts.Download {
|
||||
ctx.Response.Header.Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s.png"`, user))
|
||||
}
|
||||
|
||||
ctx.Response.Header.Set("X-Cache-Hit", "FALSE")
|
||||
ctx.SetContentType("image/png")
|
||||
ctx.SetBody(data)
|
||||
}
|
||||
|
||||
func LeftBodyHandler(ctx *fasthttp.RequestCtx) {
|
||||
user := ctx.UserValue("user").(string)
|
||||
|
||||
opts := util.ParseQueryParams(ctx, config.Routes.LeftBody)
|
||||
|
||||
uuid, ok, err := util.LookupUUID(user)
|
||||
|
||||
if err != nil {
|
||||
util.WriteError(ctx, err, http.StatusInternalServerError)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if !ok && !opts.Fallback {
|
||||
util.WriteError(ctx, nil, http.StatusNotFound)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
cacheKey := fmt.Sprintf("result:leftbody-%d-%t-%s", opts.Scale, opts.Overlay, uuid)
|
||||
|
||||
{
|
||||
cache, ok, err := r.GetBytes(cacheKey)
|
||||
|
||||
if err != nil {
|
||||
util.WriteError(ctx, err, http.StatusInternalServerError)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if ok {
|
||||
if opts.Download {
|
||||
ctx.Response.Header.Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s.png"`, user))
|
||||
}
|
||||
|
||||
if util.Debug {
|
||||
log.Printf("Retrieved cache for left body render for '%s'\n", uuid)
|
||||
}
|
||||
|
||||
ctx.Response.Header.Set("X-Cache-Hit", "TRUE")
|
||||
ctx.SetContentType("image/png")
|
||||
ctx.SetBody(cache)
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
rawSkin, slim, err := util.GetPlayerSkin(uuid)
|
||||
|
||||
if err != nil {
|
||||
util.WriteError(ctx, err, http.StatusInternalServerError)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
render := skin.RenderLeftBody(rawSkin, skin.Options{
|
||||
Overlay: opts.Overlay,
|
||||
Slim: slim,
|
||||
Scale: opts.Scale,
|
||||
})
|
||||
|
||||
if util.Debug {
|
||||
log.Printf("Rendered left body image for '%s'\n", uuid)
|
||||
}
|
||||
|
||||
data, err := util.EncodePNG(render)
|
||||
|
||||
if err != nil {
|
||||
util.WriteError(ctx, err, http.StatusInternalServerError)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if err = r.Set(cacheKey, data, config.Cache.RenderCacheDuration); err != nil {
|
||||
util.WriteError(ctx, err, http.StatusInternalServerError)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if opts.Download {
|
||||
ctx.Response.Header.Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s.png"`, user))
|
||||
}
|
||||
|
||||
ctx.Response.Header.Set("X-Cache-Hit", "FALSE")
|
||||
ctx.SetContentType("image/png")
|
||||
ctx.SetBody(data)
|
||||
}
|
||||
|
||||
func RightBodyHandler(ctx *fasthttp.RequestCtx) {
|
||||
user := ctx.UserValue("user").(string)
|
||||
|
||||
opts := util.ParseQueryParams(ctx, config.Routes.RightBody)
|
||||
|
||||
uuid, ok, err := util.LookupUUID(user)
|
||||
|
||||
if err != nil {
|
||||
util.WriteError(ctx, err, http.StatusInternalServerError)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if !ok && !opts.Fallback {
|
||||
util.WriteError(ctx, nil, http.StatusNotFound)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
cacheKey := fmt.Sprintf("result:rightbody-%d-%t-%s", opts.Scale, opts.Overlay, uuid)
|
||||
|
||||
{
|
||||
cache, ok, err := r.GetBytes(cacheKey)
|
||||
|
||||
if err != nil {
|
||||
util.WriteError(ctx, err, http.StatusInternalServerError)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if ok {
|
||||
if opts.Download {
|
||||
ctx.Response.Header.Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s.png"`, user))
|
||||
}
|
||||
|
||||
if util.Debug {
|
||||
log.Printf("Retrieved cache for right body render for '%s'\n", uuid)
|
||||
}
|
||||
|
||||
ctx.Response.Header.Set("X-Cache-Hit", "TRUE")
|
||||
ctx.SetContentType("image/png")
|
||||
ctx.SetBody(cache)
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
rawSkin, slim, err := util.GetPlayerSkin(uuid)
|
||||
|
||||
if err != nil {
|
||||
util.WriteError(ctx, err, http.StatusInternalServerError)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
render := skin.RenderRightBody(rawSkin, skin.Options{
|
||||
Overlay: opts.Overlay,
|
||||
Slim: slim,
|
||||
Scale: opts.Scale,
|
||||
})
|
||||
|
||||
if util.Debug {
|
||||
log.Printf("Rendered right body image for '%s'\n", uuid)
|
||||
}
|
||||
|
||||
data, err := util.EncodePNG(render)
|
||||
|
||||
if err != nil {
|
||||
util.WriteError(ctx, err, http.StatusInternalServerError)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if err = r.Set(cacheKey, data, config.Cache.RenderCacheDuration); err != nil {
|
||||
util.WriteError(ctx, err, http.StatusInternalServerError)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if opts.Download {
|
||||
ctx.Response.Header.Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s.png"`, user))
|
||||
}
|
||||
|
||||
ctx.Response.Header.Set("X-Cache-Hit", "FALSE")
|
||||
ctx.SetContentType("image/png")
|
||||
ctx.SetBody(data)
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
package routes
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"github.com/mineatar-io/api-server/src/util"
|
||||
"github.com/mineatar-io/skin-render"
|
||||
"github.com/valyala/fasthttp"
|
||||
)
|
||||
|
||||
func FaceHandler(ctx *fasthttp.RequestCtx) {
|
||||
user := ctx.UserValue("user").(string)
|
||||
|
||||
opts := util.ParseQueryParams(ctx, config.Routes.Face)
|
||||
|
||||
uuid, ok, err := util.LookupUUID(user)
|
||||
|
||||
if err != nil {
|
||||
util.WriteError(ctx, err, http.StatusInternalServerError)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if !ok && !opts.Fallback {
|
||||
util.WriteError(ctx, nil, http.StatusNotFound)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
cacheKey := fmt.Sprintf("result:face-%d-%t-%s", opts.Scale, opts.Overlay, uuid)
|
||||
|
||||
{
|
||||
cache, ok, err := r.GetBytes(cacheKey)
|
||||
|
||||
if err != nil {
|
||||
util.WriteError(ctx, err, http.StatusInternalServerError)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if ok {
|
||||
if opts.Download {
|
||||
ctx.Response.Header.Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s.png"`, user))
|
||||
}
|
||||
|
||||
if util.Debug {
|
||||
log.Printf("Retrieved cache for face render for '%s'\n", uuid)
|
||||
}
|
||||
|
||||
ctx.Response.Header.Set("X-Cache-Hit", "TRUE")
|
||||
ctx.SetContentType("image/png")
|
||||
ctx.SetBody(cache)
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
rawSkin, slim, err := util.GetPlayerSkin(uuid)
|
||||
|
||||
if err != nil {
|
||||
util.WriteError(ctx, err, http.StatusInternalServerError)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
render := skin.RenderFace(rawSkin, skin.Options{
|
||||
Overlay: opts.Overlay,
|
||||
Slim: slim,
|
||||
Scale: opts.Scale,
|
||||
})
|
||||
|
||||
if util.Debug {
|
||||
log.Printf("Rendered face image for '%s'\n", uuid)
|
||||
}
|
||||
|
||||
data, err := util.EncodePNG(render)
|
||||
|
||||
if err != nil {
|
||||
util.WriteError(ctx, err, http.StatusInternalServerError)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if err = r.Set(cacheKey, data, config.Cache.RenderCacheDuration); err != nil {
|
||||
util.WriteError(ctx, err, http.StatusInternalServerError)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if opts.Download {
|
||||
ctx.Response.Header.Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s.png"`, user))
|
||||
}
|
||||
|
||||
ctx.Response.Header.Set("X-Cache-Hit", "FALSE")
|
||||
ctx.SetContentType("image/png")
|
||||
ctx.SetBody(data)
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
package routes
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"github.com/mineatar-io/api-server/src/util"
|
||||
"github.com/mineatar-io/skin-render"
|
||||
"github.com/valyala/fasthttp"
|
||||
)
|
||||
|
||||
func HeadHandler(ctx *fasthttp.RequestCtx) {
|
||||
user := ctx.UserValue("user").(string)
|
||||
|
||||
opts := util.ParseQueryParams(ctx, config.Routes.Head)
|
||||
|
||||
uuid, ok, err := util.LookupUUID(user)
|
||||
|
||||
if err != nil {
|
||||
util.WriteError(ctx, err, http.StatusInternalServerError)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if !ok && !opts.Fallback {
|
||||
util.WriteError(ctx, nil, http.StatusNotFound)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
cacheKey := fmt.Sprintf("result:head-%d-%t-%s", opts.Scale, opts.Overlay, uuid)
|
||||
|
||||
{
|
||||
cache, ok, err := r.GetBytes(cacheKey)
|
||||
|
||||
if err != nil {
|
||||
util.WriteError(ctx, err, http.StatusInternalServerError)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if ok {
|
||||
if opts.Download {
|
||||
ctx.Response.Header.Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s.png"`, user))
|
||||
}
|
||||
|
||||
if util.Debug {
|
||||
log.Printf("Retrieved cache for head render for '%s'\n", uuid)
|
||||
}
|
||||
|
||||
ctx.Response.Header.Set("X-Cache-Hit", "TRUE")
|
||||
ctx.SetContentType("image/png")
|
||||
ctx.SetBody(cache)
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
rawSkin, slim, err := util.GetPlayerSkin(uuid)
|
||||
|
||||
if err != nil {
|
||||
util.WriteError(ctx, err, http.StatusInternalServerError)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
render := skin.RenderHead(rawSkin, skin.Options{
|
||||
Overlay: opts.Overlay,
|
||||
Slim: slim,
|
||||
Scale: opts.Scale,
|
||||
})
|
||||
|
||||
if util.Debug {
|
||||
log.Printf("Rendered head image for '%s'\n", uuid)
|
||||
}
|
||||
|
||||
data, err := util.EncodePNG(render)
|
||||
|
||||
if err != nil {
|
||||
util.WriteError(ctx, err, http.StatusInternalServerError)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if err = r.Set(cacheKey, data, config.Cache.RenderCacheDuration); err != nil {
|
||||
util.WriteError(ctx, err, http.StatusInternalServerError)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if opts.Download {
|
||||
ctx.Response.Header.Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s.png"`, user))
|
||||
}
|
||||
|
||||
ctx.Response.Header.Set("X-Cache-Hit", "FALSE")
|
||||
ctx.SetContentType("image/png")
|
||||
ctx.SetBody(data)
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
package routes
|
||||
|
||||
import (
|
||||
"github.com/mineatar-io/api-server/src/conf"
|
||||
"github.com/mineatar-io/api-server/src/redis"
|
||||
)
|
||||
|
||||
var (
|
||||
r *redis.Redis
|
||||
config *conf.Configuration
|
||||
)
|
||||
|
||||
func Init(red *redis.Redis, c *conf.Configuration) {
|
||||
r = red
|
||||
config = c
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
package routes
|
||||
|
||||
import (
|
||||
"github.com/valyala/fasthttp"
|
||||
)
|
||||
|
||||
func PingHandler(ctx *fasthttp.RequestCtx) {
|
||||
ctx.SetBodyString("Pong!")
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
package routes
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/mineatar-io/api-server/src/util"
|
||||
"github.com/valyala/fasthttp"
|
||||
)
|
||||
|
||||
func SkinHandler(ctx *fasthttp.RequestCtx) {
|
||||
user := ctx.UserValue("user").(string)
|
||||
|
||||
opts := util.ParseQueryParams(ctx, config.Routes.RawSkin)
|
||||
|
||||
uuid, ok, err := util.LookupUUID(user)
|
||||
|
||||
if err != nil {
|
||||
util.WriteError(ctx, err, http.StatusInternalServerError)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if !ok && !opts.Fallback {
|
||||
util.WriteError(ctx, nil, http.StatusNotFound)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
rawSkin, _, err := util.GetPlayerSkin(uuid)
|
||||
|
||||
if err != nil {
|
||||
util.WriteError(ctx, err, http.StatusInternalServerError)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
data, err := util.EncodePNG(rawSkin)
|
||||
|
||||
if err != nil {
|
||||
util.WriteError(ctx, err, http.StatusInternalServerError)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if opts.Download {
|
||||
ctx.Response.Header.Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s.png"`, user))
|
||||
}
|
||||
|
||||
ctx.SetContentType("image/png")
|
||||
ctx.SetBody(data)
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
package routes
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/mineatar-io/api-server/src/util"
|
||||
"github.com/valyala/fasthttp"
|
||||
)
|
||||
|
||||
func UUIDHandler(ctx *fasthttp.RequestCtx) {
|
||||
user := ctx.UserValue("user").(string)
|
||||
|
||||
uuid, ok, err := util.LookupUUID(user)
|
||||
|
||||
if err != nil {
|
||||
util.WriteError(ctx, err, http.StatusInternalServerError)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if !ok {
|
||||
util.WriteError(ctx, nil, http.StatusNotFound)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
ctx.SetBodyString(uuid)
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package util
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@@ -9,34 +9,20 @@ import (
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/mineatar-io/api-server/src/conf"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/mineatar-io/skin-render"
|
||||
"github.com/mineatar-io/yggdrasil"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
"github.com/valyala/fasthttp"
|
||||
)
|
||||
|
||||
var (
|
||||
Debug bool = os.Getenv("DEBUG") == "true"
|
||||
yggdrasilUUIDLookupMetric = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "yggdrasil_uuid_lookup_count",
|
||||
Help: "The amount of Yggdrasil UUID lookup requests",
|
||||
})
|
||||
yggdrasilTextureLookupMetric = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "yggdrasil_texture_lookup_count",
|
||||
Help: "The amount of Yggdrasil texture lookup requests",
|
||||
})
|
||||
)
|
||||
|
||||
type QueryParams struct {
|
||||
Scale int
|
||||
Download bool
|
||||
Overlay bool
|
||||
Fallback bool
|
||||
Scale int `query:"scale"`
|
||||
Download bool `query:"download"`
|
||||
Overlay bool `query:"overlay"`
|
||||
Fallback bool `query:"fallback"`
|
||||
}
|
||||
|
||||
func FormatUUID(uuid string) string {
|
||||
@@ -59,10 +45,6 @@ func LookupUUID(value string) (string, bool, error) {
|
||||
}
|
||||
|
||||
if ok {
|
||||
if Debug {
|
||||
log.Printf("Retrieved UUID from cache for '%s' (%s)\n", value, cache)
|
||||
}
|
||||
|
||||
return cache, true, nil
|
||||
}
|
||||
|
||||
@@ -72,13 +54,7 @@ func LookupUUID(value string) (string, bool, error) {
|
||||
return "", false, err
|
||||
}
|
||||
|
||||
yggdrasilUUIDLookupMetric.Inc()
|
||||
|
||||
if profile == nil {
|
||||
if Debug {
|
||||
log.Printf("Fetched UUID from Mojang for '%s', did not exist\n", value)
|
||||
}
|
||||
|
||||
return "", false, nil
|
||||
}
|
||||
|
||||
@@ -86,10 +62,6 @@ func LookupUUID(value string) (string, bool, error) {
|
||||
return "", true, err
|
||||
}
|
||||
|
||||
if Debug {
|
||||
log.Printf("Fetched UUID from Mojang for '%s' (%s)\n", value, profile.UUID)
|
||||
}
|
||||
|
||||
return profile.UUID, true, nil
|
||||
}
|
||||
|
||||
@@ -100,10 +72,6 @@ func FetchImage(url string) (*image.NRGBA, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if Debug {
|
||||
log.Printf("Fetched image from URL: %s\n", url)
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
img, format, err := image.Decode(resp.Body)
|
||||
@@ -143,10 +111,6 @@ func GetPlayerSkin(uuid string) (*image.NRGBA, bool, error) {
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
if Debug {
|
||||
log.Printf("Retrieved skin for '%s' (slim: %t) from cache\n", uuid, slim)
|
||||
}
|
||||
|
||||
return cache, slim, nil
|
||||
}
|
||||
|
||||
@@ -156,22 +120,12 @@ func GetPlayerSkin(uuid string) (*image.NRGBA, bool, error) {
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
yggdrasilTextureLookupMetric.Inc()
|
||||
|
||||
if textures == nil {
|
||||
if Debug {
|
||||
log.Printf("Fetched textures for '%s' from Mojang, none exists, using default skin\n", uuid)
|
||||
}
|
||||
|
||||
slim := skin.IsSlimFromUUID(uuid)
|
||||
|
||||
return skin.GetDefaultSkin(slim), slim, nil
|
||||
}
|
||||
|
||||
if Debug {
|
||||
log.Printf("Fetched textures for '%s' from Mojang\n", uuid)
|
||||
}
|
||||
|
||||
value := ""
|
||||
|
||||
for _, property := range textures.Properties {
|
||||
@@ -255,8 +209,8 @@ func EncodePNG(img image.Image) ([]byte, error) {
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
func ParseQueryParams(ctx *fasthttp.RequestCtx, route conf.RouteConfig) *QueryParams {
|
||||
args := ctx.QueryArgs()
|
||||
func ParseQueryParams(ctx *fiber.Ctx, route RouteConfig) *QueryParams {
|
||||
args := ctx.Context().QueryArgs()
|
||||
|
||||
response := &QueryParams{
|
||||
Scale: route.DefaultScale,
|
||||
@@ -286,16 +240,16 @@ func ParseQueryParams(ctx *fasthttp.RequestCtx, route conf.RouteConfig) *QueryPa
|
||||
return response
|
||||
}
|
||||
|
||||
func WriteError(ctx *fasthttp.RequestCtx, err error, statusCode int, body ...string) {
|
||||
ctx.SetStatusCode(statusCode)
|
||||
func GetInstanceID() (uint16, error) {
|
||||
if instanceID := os.Getenv("INSTANCE_ID"); len(instanceID) > 0 {
|
||||
value, err := strconv.ParseUint(instanceID, 10, 16)
|
||||
|
||||
if len(body) > 0 {
|
||||
ctx.SetBodyString(body[0])
|
||||
} else {
|
||||
ctx.SetBodyString(http.StatusText(statusCode))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
return uint16(value), nil
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
return 0, nil
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"github.com/mineatar-io/api-server/src/conf"
|
||||
"github.com/mineatar-io/api-server/src/redis"
|
||||
)
|
||||
|
||||
var (
|
||||
config *conf.Configuration
|
||||
r *redis.Redis
|
||||
)
|
||||
|
||||
func Init(red *redis.Redis, c *conf.Configuration) {
|
||||
r = red
|
||||
config = c
|
||||
}
|
||||
Reference in New Issue
Block a user