diff --git a/src/mojang.go b/src/mojang.go index 2530d90..3a98d39 100644 --- a/src/mojang.go +++ b/src/mojang.go @@ -8,12 +8,6 @@ import ( "net/http" ) -// MinecraftProfile is Minecraft profile information returned from the Mojang API. -type MinecraftProfile struct { - Username string `json:"name"` - UUID string `json:"id"` -} - // MinecraftProfileTextures is texture information about a Minecraft profile returned from the Mojang API. type MinecraftProfileTextures struct { UUID string `json:"id"` @@ -45,47 +39,6 @@ type MinecraftDecodedTextures struct { } `json:"textures"` } -// UsernameToUUID converts a Minecraft username into a UUID using Mojang. -func UsernameToUUID(username string) (*MinecraftProfile, error) { - req, err := http.NewRequest("GET", fmt.Sprintf("https://api.mojang.com/users/profiles/minecraft/%s", username), nil) - - if err != nil { - return nil, err - } - - req.Header.Set("User-Agent", "mineatar.io Skin Render API") - - resp, err := http.DefaultClient.Do(req) - - if err != nil { - return nil, err - } - - if resp.StatusCode != http.StatusOK { - if resp.StatusCode == http.StatusNoContent || resp.StatusCode == http.StatusNotFound { - return nil, nil - } - - return nil, fmt.Errorf("mojang: unexpected response: %s", resp.Status) - } - - defer resp.Body.Close() - - body, err := io.ReadAll(resp.Body) - - if err != nil { - return nil, err - } - - response := &MinecraftProfile{} - - if err = json.Unmarshal(body, response); err != nil { - return nil, err - } - - return response, nil -} - // GetProfileTextures returns the textures of a Minecraft player from Mojang. func GetProfileTextures(uuid string) (*MinecraftProfileTextures, error) { req, err := http.NewRequest("GET", fmt.Sprintf("https://sessionserver.mojang.com/session/minecraft/profile/%s", uuid), nil) @@ -118,13 +71,13 @@ func GetProfileTextures(uuid string) (*MinecraftProfileTextures, error) { return nil, err } - response := &MinecraftProfileTextures{} + response := MinecraftProfileTextures{} - if err = json.Unmarshal(body, response); err != nil { + if err = json.Unmarshal(body, &response); err != nil { return nil, err } - return response, nil + return &response, nil } // GetDecodedTexturesValue decodes the values from a MinecraftProfileTextures texture value. diff --git a/src/routes.go b/src/routes.go index 6bf2ae1..ed1bf1d 100644 --- a/src/routes.go +++ b/src/routes.go @@ -11,15 +11,14 @@ import ( 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.Get("/skin/:uuid", SkinHandler) + app.Get("/face/:uuid", FaceHandler) + app.Get("/head/:uuid", HeadHandler) + app.Get("/body/full/:uuid", FullBodyHandler) + app.Get("/body/front/:uuid", FrontBodyHandler) + app.Get("/body/back/:uuid", BackBodyHandler) + app.Get("/body/left/:uuid", LeftBodyHandler) + app.Get("/body/right/:uuid", RightBodyHandler) app.Use(NotFoundHandler) } @@ -28,18 +27,14 @@ func PingHandler(ctx *fiber.Ctx) error { return ctx.SendStatus(http.StatusOK) } -// FullBodyHandler is the API handler used for the `/body/full/:user` route. +// FullBodyHandler is the API handler used for the `/body/full/:uuid` route. func FullBodyHandler(ctx *fiber.Ctx) error { opts := ParseQueryParams(ctx, conf.Routes.FullBody) - uuid, ok, err := LookupUUID(ParseUserParam(ctx)) + uuid, ok := ParseUUID(ExtractUUID(ctx)) - if err != nil { - return err - } - - if !ok && !opts.Fallback { - return ctx.SendStatus(http.StatusNotFound) + if !ok { + return SendUsernameDeprecation(ctx) } cacheKey := fmt.Sprintf("result:fullbody-%d-%t-%s", opts.Scale, opts.Overlay, uuid) @@ -79,18 +74,14 @@ func FullBodyHandler(ctx *fiber.Ctx) error { return ctx.Type("png").Send(data) } -// FrontBodyHandler is the API handler used for the `/body/front/:user` route. +// FrontBodyHandler is the API handler used for the `/body/front/:uuid` route. func FrontBodyHandler(ctx *fiber.Ctx) error { opts := ParseQueryParams(ctx, conf.Routes.FrontBody) - uuid, ok, err := LookupUUID(ParseUserParam(ctx)) + uuid, ok := ParseUUID(ExtractUUID(ctx)) - if err != nil { - return err - } - - if !ok && !opts.Fallback { - return ctx.SendStatus(http.StatusNotFound) + if !ok { + return SendUsernameDeprecation(ctx) } cacheKey := fmt.Sprintf("result:frontbody-%d-%t-%s", opts.Scale, opts.Overlay, uuid) @@ -132,18 +123,14 @@ func FrontBodyHandler(ctx *fiber.Ctx) error { return ctx.Type("png").Send(data) } -// BackBodyHandler is the API handler used for the `/body/back/:user` route. +// BackBodyHandler is the API handler used for the `/body/back/:uuid` route. func BackBodyHandler(ctx *fiber.Ctx) error { opts := ParseQueryParams(ctx, conf.Routes.BackBody) - uuid, ok, err := LookupUUID(ParseUserParam(ctx)) + uuid, ok := ParseUUID(ExtractUUID(ctx)) - if err != nil { - return err - } - - if !ok && !opts.Fallback { - return ctx.SendStatus(http.StatusNotFound) + if !ok { + return SendUsernameDeprecation(ctx) } cacheKey := fmt.Sprintf("result:backbody-%d-%t-%s", opts.Scale, opts.Overlay, uuid) @@ -185,18 +172,14 @@ func BackBodyHandler(ctx *fiber.Ctx) error { return ctx.Type("png").Send(data) } -// LeftBodyHandler is the API handler used for the `/body/left/:user` route. +// LeftBodyHandler is the API handler used for the `/body/left/:uuid` route. func LeftBodyHandler(ctx *fiber.Ctx) error { opts := ParseQueryParams(ctx, conf.Routes.LeftBody) - uuid, ok, err := LookupUUID(ParseUserParam(ctx)) + uuid, ok := ParseUUID(ExtractUUID(ctx)) - if err != nil { - return err - } - - if !ok && !opts.Fallback { - return ctx.SendStatus(http.StatusNotFound) + if !ok { + return SendUsernameDeprecation(ctx) } cacheKey := fmt.Sprintf("result:leftbody-%d-%t-%s", opts.Scale, opts.Overlay, uuid) @@ -238,18 +221,14 @@ func LeftBodyHandler(ctx *fiber.Ctx) error { return ctx.Type("png").Send(data) } -// RightBodyHandler is the API handler used for the `/body/right/:user` route. +// RightBodyHandler is the API handler used for the `/body/right/:uuid` route. func RightBodyHandler(ctx *fiber.Ctx) error { opts := ParseQueryParams(ctx, conf.Routes.RightBody) - uuid, ok, err := LookupUUID(ParseUserParam(ctx)) + uuid, ok := ParseUUID(ExtractUUID(ctx)) - if err != nil { - return err - } - - if !ok && !opts.Fallback { - return ctx.SendStatus(http.StatusNotFound) + if !ok { + return SendUsernameDeprecation(ctx) } cacheKey := fmt.Sprintf("result:rightbody-%d-%t-%s", opts.Scale, opts.Overlay, uuid) @@ -291,18 +270,14 @@ func RightBodyHandler(ctx *fiber.Ctx) error { return ctx.Type("png").Send(data) } -// FaceHandler is the API handler used for the `/face/:user` route. +// FaceHandler is the API handler used for the `/face/:uuid` route. func FaceHandler(ctx *fiber.Ctx) error { opts := ParseQueryParams(ctx, conf.Routes.Face) - uuid, ok, err := LookupUUID(ParseUserParam(ctx)) + uuid, ok := ParseUUID(ExtractUUID(ctx)) - if err != nil { - return err - } - - if !ok && !opts.Fallback { - return ctx.SendStatus(http.StatusNotFound) + if !ok { + return SendUsernameDeprecation(ctx) } cacheKey := fmt.Sprintf("result:face-%d-%t-%s", opts.Scale, opts.Overlay, uuid) @@ -344,18 +319,14 @@ func FaceHandler(ctx *fiber.Ctx) error { return ctx.Type("png").Send(data) } -// HeadHandler is the API handler used for the `/head/:user` route. +// HeadHandler is the API handler used for the `/head/:uuid` route. func HeadHandler(ctx *fiber.Ctx) error { opts := ParseQueryParams(ctx, conf.Routes.Head) - uuid, ok, err := LookupUUID(ParseUserParam(ctx)) + uuid, ok := ParseUUID(ExtractUUID(ctx)) - if err != nil { - return err - } - - if !ok && !opts.Fallback { - return ctx.SendStatus(http.StatusNotFound) + if !ok { + return SendUsernameDeprecation(ctx) } cacheKey := fmt.Sprintf("result:head-%d-%t-%s", opts.Scale, opts.Overlay, uuid) @@ -397,18 +368,14 @@ func HeadHandler(ctx *fiber.Ctx) error { return ctx.Type("png").Send(data) } -// SkinHandler is the API handler used for the `/skin/:user` route. +// SkinHandler is the API handler used for the `/skin/:uuid` route. func SkinHandler(ctx *fiber.Ctx) error { opts := ParseQueryParams(ctx, conf.Routes.RawSkin) - uuid, ok, err := LookupUUID(ParseUserParam(ctx)) + uuid, ok := ParseUUID(ctx.Params("uuid")) - if err != nil { - return err - } - - if !ok && !opts.Fallback { - return ctx.SendStatus(http.StatusNotFound) + if !ok { + return SendUsernameDeprecation(ctx) } rawSkin, _, err := GetPlayerSkin(uuid) @@ -430,21 +397,6 @@ func SkinHandler(ctx *fiber.Ctx) error { return ctx.Type("png").Send(data) } -// UUIDHandler is the API handler used for the `/uuid/:user` route. -func UUIDHandler(ctx *fiber.Ctx) error { - uuid, ok, err := LookupUUID(ctx.Params("user")) - - if err != nil { - return err - } - - if !ok { - return ctx.SendStatus(http.StatusNotFound) - } - - return ctx.SendString(uuid) -} - // NotFoundHandler is the API handler used for any requests that do not match an existing route. func NotFoundHandler(ctx *fiber.Ctx) error { return ctx.SendStatus(http.StatusNotFound) diff --git a/src/util.go b/src/util.go index bc05f4a..ceda468 100644 --- a/src/util.go +++ b/src/util.go @@ -21,7 +21,6 @@ type QueryParams struct { Scale int `query:"scale"` Download bool `query:"download"` Overlay bool `query:"overlay"` - Fallback bool `query:"fallback"` } // FormatUUID returns the UUID string without any dashes. @@ -29,41 +28,15 @@ func FormatUUID(uuid string) string { return strings.ToLower(strings.ReplaceAll(uuid, "-", "")) } -// LookupUUID returns the UUID of a player either by username or UUID, while attempting to use any cached values in the database. -func LookupUUID(value string) (string, bool, error) { +// ParseUUID parses the UUID given by the route parameters, and returns a boolean if the UUID is valid. +func ParseUUID(value string) (string, bool) { value = FormatUUID(value) - if len(value) == 32 { - return value, true, nil + if len(value) != 32 { + return "", false } - cacheKey := fmt.Sprintf("uuid:%s", value) - - cache, ok, err := r.GetString(cacheKey) - - if err != nil { - return "", false, err - } - - if ok { - return cache, true, nil - } - - profile, err := UsernameToUUID(value) - - if err != nil { - return "", false, err - } - - if profile == nil { - return "", false, nil - } - - if err = r.Set(cacheKey, profile.UUID, conf.Cache.UUIDCacheDuration); err != nil { - return "", true, err - } - - return profile.UUID, true, nil + return value, true } // FetchImage fetches the image by the URL and returns it as a parsed image. @@ -95,12 +68,6 @@ func FetchImage(url string) (*image.NRGBA, error) { // GetPlayerSkin fetches the skin of the Minecraft player by the UUID. func GetPlayerSkin(uuid string) (*image.NRGBA, bool, error) { - uuid = FormatUUID(uuid) - - if len(uuid) < 1 { - return skin.GetDefaultSkin(false), false, nil - } - cache, ok, err := r.GetNRGBA(fmt.Sprintf("skin:%s", uuid)) if err != nil { @@ -221,7 +188,6 @@ func ParseQueryParams(ctx *fiber.Ctx, route RouteConfig) *QueryParams { Scale: route.DefaultScale, Download: route.DefaultDownload, Overlay: route.DefaultOverlay, - Fallback: route.DefaultFallback, } if args.Has("scale") { @@ -234,10 +200,6 @@ func ParseQueryParams(ctx *fiber.Ctx, route RouteConfig) *QueryParams { response.Overlay = args.GetBool("overlay") } - if args.Has("fallback") { - response.Fallback = args.GetBool("fallback") - } - if args.Has("download") { response.Download = args.GetBool("download") } @@ -260,7 +222,12 @@ func GetInstanceID() (uint16, error) { return 0, nil } -// ParseUserParam returns the user name from the route param, allowing values such as "PassTheMayo.png" to be returned as "PassTheMayo". -func ParseUserParam(ctx *fiber.Ctx) string { - return strings.Split(ctx.Params("user"), ".")[0] +// ExtractUUID returns the user name from the route param, allowing values such as "PassTheMayo.png" to be returned as "PassTheMayo". +func ExtractUUID(ctx *fiber.Ctx) string { + return strings.Split(ctx.Params("uuid"), ".")[0] +} + +// SendUsernameDeprecation sends a deprecation warning about usernames. +func SendUsernameDeprecation(ctx *fiber.Ctx) error { + return ctx.Status(http.StatusBadRequest).SendString("Deprecated: Username support has been deprecated since May 1st 2023, please use a valid UUID instead.") }