Add format query parameter
This commit is contained in:
@@ -7,46 +7,54 @@ routes:
|
|||||||
default_overlay: true
|
default_overlay: true
|
||||||
default_download: false
|
default_download: false
|
||||||
default_scale: 4
|
default_scale: 4
|
||||||
|
default_format: png
|
||||||
min_scale: 1
|
min_scale: 1
|
||||||
max_scale: 64
|
max_scale: 64
|
||||||
head:
|
head:
|
||||||
default_overlay: true
|
default_overlay: true
|
||||||
default_download: false
|
default_download: false
|
||||||
default_scale: 4
|
default_scale: 4
|
||||||
|
default_format: png
|
||||||
min_scale: 1
|
min_scale: 1
|
||||||
max_scale: 64
|
max_scale: 64
|
||||||
full_body:
|
full_body:
|
||||||
default_overlay: true
|
default_overlay: true
|
||||||
default_download: false
|
default_download: false
|
||||||
default_scale: 4
|
default_scale: 4
|
||||||
|
default_format: png
|
||||||
min_scale: 1
|
min_scale: 1
|
||||||
max_scale: 64
|
max_scale: 64
|
||||||
front_body:
|
front_body:
|
||||||
default_overlay: true
|
default_overlay: true
|
||||||
default_download: false
|
default_download: false
|
||||||
default_scale: 4
|
default_scale: 4
|
||||||
|
default_format: png
|
||||||
min_scale: 1
|
min_scale: 1
|
||||||
max_scale: 64
|
max_scale: 64
|
||||||
back_body:
|
back_body:
|
||||||
default_overlay: true
|
default_overlay: true
|
||||||
default_download: false
|
default_download: false
|
||||||
default_scale: 4
|
default_scale: 4
|
||||||
|
default_format: png
|
||||||
min_scale: 1
|
min_scale: 1
|
||||||
max_scale: 64
|
max_scale: 64
|
||||||
left_body:
|
left_body:
|
||||||
default_overlay: true
|
default_overlay: true
|
||||||
default_download: false
|
default_download: false
|
||||||
default_scale: 4
|
default_scale: 4
|
||||||
|
default_format: png
|
||||||
min_scale: 1
|
min_scale: 1
|
||||||
max_scale: 64
|
max_scale: 64
|
||||||
right_body:
|
right_body:
|
||||||
default_overlay: true
|
default_overlay: true
|
||||||
default_download: false
|
default_download: false
|
||||||
default_scale: 4
|
default_scale: 4
|
||||||
|
default_format: png
|
||||||
min_scale: 1
|
min_scale: 1
|
||||||
max_scale: 64
|
max_scale: 64
|
||||||
raw_skin:
|
raw_skin:
|
||||||
default_download: false
|
default_download: false
|
||||||
|
default_format: png
|
||||||
cache:
|
cache:
|
||||||
skin_cache_duration: 12h # 12 hours
|
skin_cache_duration: 12h # 12 hours
|
||||||
render_cache_duration: 12h # 12 hours
|
render_cache_duration: 12h # 12 hours
|
||||||
|
|||||||
14
go.mod
14
go.mod
@@ -1,12 +1,14 @@
|
|||||||
module github.com/mineatar-io/api-server
|
module github.com/mineatar-io/api-server
|
||||||
|
|
||||||
go 1.18
|
go 1.21
|
||||||
|
|
||||||
|
toolchain go1.21.0
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/go-redsync/redsync/v4 v4.8.1
|
github.com/go-redsync/redsync/v4 v4.8.1
|
||||||
github.com/gofiber/fiber/v2 v2.48.0
|
github.com/gofiber/fiber/v2 v2.49.0
|
||||||
github.com/mineatar-io/skin-render v1.0.9
|
github.com/mineatar-io/skin-render v1.0.9
|
||||||
github.com/redis/go-redis/v9 v9.0.5
|
github.com/redis/go-redis/v9 v9.1.0
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -15,18 +17,18 @@ require (
|
|||||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||||
github.com/go-redis/redis/v8 v8.11.5 // indirect
|
github.com/go-redis/redis/v8 v8.11.5 // indirect
|
||||||
github.com/google/uuid v1.3.0 // indirect
|
github.com/google/uuid v1.3.1 // indirect
|
||||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||||
github.com/klauspost/compress v1.16.7 // indirect
|
github.com/klauspost/compress v1.16.7 // indirect
|
||||||
github.com/kr/pretty v0.3.1 // indirect
|
github.com/kr/pretty v0.3.1 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.19 // indirect
|
github.com/mattn/go-isatty v0.0.19 // indirect
|
||||||
github.com/mattn/go-runewidth v0.0.14 // indirect
|
github.com/mattn/go-runewidth v0.0.15 // indirect
|
||||||
github.com/rivo/uniseg v0.4.4 // indirect
|
github.com/rivo/uniseg v0.4.4 // indirect
|
||||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||||
github.com/valyala/fasthttp v1.48.0 // indirect
|
github.com/valyala/fasthttp v1.48.0 // indirect
|
||||||
github.com/valyala/tcplisten v1.0.0 // indirect
|
github.com/valyala/tcplisten v1.0.0 // indirect
|
||||||
golang.org/x/sys v0.10.0 // indirect
|
golang.org/x/sys v0.11.0 // indirect
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
24
go.sum
24
go.sum
@@ -1,9 +1,11 @@
|
|||||||
github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
|
github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
|
||||||
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
||||||
github.com/bsm/ginkgo/v2 v2.5.0/go.mod h1:AiKlXPm7ItEHNc/2+OkrNG4E0ITzojb9/xWzvQ9XZ9w=
|
github.com/bsm/ginkgo/v2 v2.5.0/go.mod h1:AiKlXPm7ItEHNc/2+OkrNG4E0ITzojb9/xWzvQ9XZ9w=
|
||||||
github.com/bsm/ginkgo/v2 v2.7.0 h1:ItPMPH90RbmZJt5GtkcNvIRuGEdwlBItdNVoyzaNQao=
|
github.com/bsm/ginkgo/v2 v2.9.5 h1:rtVBYPs3+TC5iLUVOis1B9tjLTup7Cj5IfzosKtvTJ0=
|
||||||
|
github.com/bsm/ginkgo/v2 v2.9.5/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
|
||||||
github.com/bsm/gomega v1.20.0/go.mod h1:JifAceMQ4crZIWYUKrlGcmbN3bqHogVTADMD2ATsbwk=
|
github.com/bsm/gomega v1.20.0/go.mod h1:JifAceMQ4crZIWYUKrlGcmbN3bqHogVTADMD2ATsbwk=
|
||||||
github.com/bsm/gomega v1.26.0 h1:LhQm+AFcgV2M0WyKroMASzAzCAJVpAxQXv4SaI9a69Y=
|
github.com/bsm/gomega v1.26.0 h1:LhQm+AFcgV2M0WyKroMASzAzCAJVpAxQXv4SaI9a69Y=
|
||||||
|
github.com/bsm/gomega v1.26.0/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
|
||||||
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
||||||
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
@@ -24,8 +26,8 @@ github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq
|
|||||||
github.com/go-redsync/redsync/v4 v4.8.1 h1:rq2RvdTI0obznMdxKUWGdmmulo7lS9yCzb8fgDKOlbM=
|
github.com/go-redsync/redsync/v4 v4.8.1 h1:rq2RvdTI0obznMdxKUWGdmmulo7lS9yCzb8fgDKOlbM=
|
||||||
github.com/go-redsync/redsync/v4 v4.8.1/go.mod h1:LmUAsQuQxhzZAoGY7JS6+dNhNmZyonMZiiEDY9plotM=
|
github.com/go-redsync/redsync/v4 v4.8.1/go.mod h1:LmUAsQuQxhzZAoGY7JS6+dNhNmZyonMZiiEDY9plotM=
|
||||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||||
github.com/gofiber/fiber/v2 v2.48.0 h1:cRVMCb9aUJDsyHxGFLwz/sGzDggdailZZyptU9F9cU0=
|
github.com/gofiber/fiber/v2 v2.49.0 h1:xBVG2c66GDcWfww56xHvMn52Q0XX7UrSvjj6MD8/5EE=
|
||||||
github.com/gofiber/fiber/v2 v2.48.0/go.mod h1:xqJgfqrc23FJuqGOW6DVgi3HyZEm2Mn9pRqUb2kHSX8=
|
github.com/gofiber/fiber/v2 v2.49.0/go.mod h1:oxpt7wQaEYgdDmq7nMxCGhilYicBLFnZ+jQSJcQDlSE=
|
||||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||||
@@ -43,8 +45,8 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
|
|||||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4=
|
||||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||||
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
||||||
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||||
@@ -66,8 +68,8 @@ 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.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||||
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
|
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
|
||||||
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
|
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
|
||||||
github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||||
github.com/mineatar-io/skin-render v1.0.9 h1:EIdHfj01Rs4P8JUk6kuvCxZG6i46c8LNGJV7EttwqQ8=
|
github.com/mineatar-io/skin-render v1.0.9 h1:EIdHfj01Rs4P8JUk6kuvCxZG6i46c8LNGJV7EttwqQ8=
|
||||||
github.com/mineatar-io/skin-render v1.0.9/go.mod h1:KkgHwrhTIqD73dkmeQwh5k2aHuWS8cDahVmd64gM128=
|
github.com/mineatar-io/skin-render v1.0.9/go.mod h1:KkgHwrhTIqD73dkmeQwh5k2aHuWS8cDahVmd64gM128=
|
||||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||||
@@ -83,8 +85,8 @@ github.com/onsi/gomega v1.16.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAl
|
|||||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/redis/go-redis/v9 v9.0.2/go.mod h1:/xDTe9EF1LM61hek62Poq2nzQSGj0xSrEtEHbBQevps=
|
github.com/redis/go-redis/v9 v9.0.2/go.mod h1:/xDTe9EF1LM61hek62Poq2nzQSGj0xSrEtEHbBQevps=
|
||||||
github.com/redis/go-redis/v9 v9.0.5 h1:CuQcn5HIEeK7BgElubPP8CGtE0KakrnbBSTLjathl5o=
|
github.com/redis/go-redis/v9 v9.1.0 h1:137FnGdk+EQdCbye1FW+qOEcY5S+SpY9T0NiuqvtfMY=
|
||||||
github.com/redis/go-redis/v9 v9.0.5/go.mod h1:WqMKv5vnQbRuZstUwxQI195wHy+t4PuXDOjzMvcuQHk=
|
github.com/redis/go-redis/v9 v9.1.0/go.mod h1:urWj3He21Dj5k4TK1y59xH8Uj6ATueP8AH1cY3lZl4c=
|
||||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
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 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
|
||||||
github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||||
@@ -134,8 +136,8 @@ golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA=
|
golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
|
||||||
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.11.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-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/text v0.3.0/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.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
|
|||||||
41
src/cache.go
41
src/cache.go
@@ -1,9 +1,10 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"image"
|
"image"
|
||||||
|
"net/url"
|
||||||
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ResultCacheKey struct {
|
type ResultCacheKey struct {
|
||||||
@@ -14,21 +15,15 @@ type ResultCacheKey struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetCacheKey returns the key used in the cache based on the rendering options, calculated as an SHA-256 hash.
|
// GetCacheKey returns the key used in the cache based on the rendering options, calculated as an SHA-256 hash.
|
||||||
func GetResultCacheKey(uuid, renderType string, opts *QueryParams) (string, error) {
|
func GetResultCacheKey(uuid, renderType string, opts *QueryParams) string {
|
||||||
rawKey := ResultCacheKey{
|
values := &url.Values{}
|
||||||
UUID: uuid,
|
values.Set("uuid", uuid)
|
||||||
Type: renderType,
|
values.Set("type", renderType)
|
||||||
Scale: opts.Scale,
|
values.Set("scale", strconv.FormatInt(int64(opts.Scale), 10))
|
||||||
Overlay: opts.Overlay,
|
values.Set("overlay", strconv.FormatBool(opts.Overlay))
|
||||||
}
|
values.Set("format", opts.Format)
|
||||||
|
|
||||||
rawKeyData, err := json.Marshal(rawKey)
|
return SHA256(values.Encode())
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
return fmt.Sprintf("result:%s", SHA256(rawKeyData)), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCachedRenderResult returns the render result from Redis cache, or nil if it does not exist or cache is disabled.
|
// GetCachedRenderResult returns the render result from Redis cache, or nil if it does not exist or cache is disabled.
|
||||||
@@ -37,13 +32,7 @@ func GetCachedRenderResult(renderType, uuid string, opts *QueryParams) ([]byte,
|
|||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
key, err := GetResultCacheKey(uuid, renderType, opts)
|
return r.GetBytes(fmt.Sprintf("result:%s", GetResultCacheKey(uuid, renderType, opts)))
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return r.GetBytes(key)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetCachedRenderResult puts the render result into cache, or does nothing is cache is disabled.
|
// SetCachedRenderResult puts the render result into cache, or does nothing is cache is disabled.
|
||||||
@@ -52,13 +41,7 @@ func SetCachedRenderResult(renderType, uuid string, opts *QueryParams, data []by
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
key, err := GetResultCacheKey(uuid, renderType, opts)
|
return r.Set(fmt.Sprintf("result:%s", GetResultCacheKey(uuid, renderType, opts)), data, *config.Cache.RenderCacheDuration)
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return r.Set(key, data, *config.Cache.RenderCacheDuration)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCachedSkin returns the raw skin of a player by UUID from the cache, also returning if the player has a slim player model.
|
// GetCachedSkin returns the raw skin of a player by UUID from the cache, also returning if the player has a slim player model.
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ var (
|
|||||||
DefaultScale: 4,
|
DefaultScale: 4,
|
||||||
MinScale: 1,
|
MinScale: 1,
|
||||||
MaxScale: 64,
|
MaxScale: 64,
|
||||||
|
DefaultFormat: "png",
|
||||||
},
|
},
|
||||||
Head: RouteConfig{
|
Head: RouteConfig{
|
||||||
DefaultOverlay: true,
|
DefaultOverlay: true,
|
||||||
@@ -28,6 +29,7 @@ var (
|
|||||||
DefaultScale: 4,
|
DefaultScale: 4,
|
||||||
MinScale: 1,
|
MinScale: 1,
|
||||||
MaxScale: 64,
|
MaxScale: 64,
|
||||||
|
DefaultFormat: "png",
|
||||||
},
|
},
|
||||||
FullBody: RouteConfig{
|
FullBody: RouteConfig{
|
||||||
DefaultOverlay: true,
|
DefaultOverlay: true,
|
||||||
@@ -35,6 +37,7 @@ var (
|
|||||||
DefaultScale: 4,
|
DefaultScale: 4,
|
||||||
MinScale: 1,
|
MinScale: 1,
|
||||||
MaxScale: 64,
|
MaxScale: 64,
|
||||||
|
DefaultFormat: "png",
|
||||||
},
|
},
|
||||||
FrontBody: RouteConfig{
|
FrontBody: RouteConfig{
|
||||||
DefaultOverlay: true,
|
DefaultOverlay: true,
|
||||||
@@ -42,6 +45,7 @@ var (
|
|||||||
DefaultScale: 4,
|
DefaultScale: 4,
|
||||||
MinScale: 1,
|
MinScale: 1,
|
||||||
MaxScale: 64,
|
MaxScale: 64,
|
||||||
|
DefaultFormat: "png",
|
||||||
},
|
},
|
||||||
BackBody: RouteConfig{
|
BackBody: RouteConfig{
|
||||||
DefaultOverlay: true,
|
DefaultOverlay: true,
|
||||||
@@ -49,6 +53,7 @@ var (
|
|||||||
DefaultScale: 4,
|
DefaultScale: 4,
|
||||||
MinScale: 1,
|
MinScale: 1,
|
||||||
MaxScale: 64,
|
MaxScale: 64,
|
||||||
|
DefaultFormat: "png",
|
||||||
},
|
},
|
||||||
LeftBody: RouteConfig{
|
LeftBody: RouteConfig{
|
||||||
DefaultOverlay: true,
|
DefaultOverlay: true,
|
||||||
@@ -56,6 +61,7 @@ var (
|
|||||||
DefaultScale: 4,
|
DefaultScale: 4,
|
||||||
MinScale: 1,
|
MinScale: 1,
|
||||||
MaxScale: 64,
|
MaxScale: 64,
|
||||||
|
DefaultFormat: "png",
|
||||||
},
|
},
|
||||||
RightBody: RouteConfig{
|
RightBody: RouteConfig{
|
||||||
DefaultOverlay: true,
|
DefaultOverlay: true,
|
||||||
@@ -63,9 +69,11 @@ var (
|
|||||||
DefaultScale: 4,
|
DefaultScale: 4,
|
||||||
MinScale: 1,
|
MinScale: 1,
|
||||||
MaxScale: 64,
|
MaxScale: 64,
|
||||||
|
DefaultFormat: "png",
|
||||||
},
|
},
|
||||||
RawSkin: RouteConfig{
|
RawSkin: RouteConfig{
|
||||||
DefaultDownload: false,
|
DefaultDownload: false,
|
||||||
|
DefaultFormat: "png",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Cache: CacheConfig{
|
Cache: CacheConfig{
|
||||||
@@ -103,6 +111,7 @@ type RouteConfig struct {
|
|||||||
DefaultScale int `yaml:"default_scale"`
|
DefaultScale int `yaml:"default_scale"`
|
||||||
DefaultOverlay bool `yaml:"default_overlay"`
|
DefaultOverlay bool `yaml:"default_overlay"`
|
||||||
DefaultDownload bool `yaml:"default_download"`
|
DefaultDownload bool `yaml:"default_download"`
|
||||||
|
DefaultFormat string `yaml:"default_format"`
|
||||||
MinScale int `yaml:"min_scale"`
|
MinScale int `yaml:"min_scale"`
|
||||||
MaxScale int `yaml:"max_scale"`
|
MaxScale int `yaml:"max_scale"`
|
||||||
}
|
}
|
||||||
|
|||||||
27
src/main.go
27
src/main.go
@@ -1,21 +1,25 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
"github.com/gofiber/fiber/v2/middleware/cors"
|
|
||||||
"github.com/gofiber/fiber/v2/middleware/logger"
|
|
||||||
"github.com/gofiber/fiber/v2/middleware/recover"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
app *fiber.App = fiber.New(fiber.Config{
|
app *fiber.App = fiber.New(fiber.Config{
|
||||||
DisableStartupMessage: true,
|
DisableStartupMessage: true,
|
||||||
ErrorHandler: func(ctx *fiber.Ctx, err error) error {
|
ErrorHandler: func(ctx *fiber.Ctx, err error) error {
|
||||||
log.Println(ctx.Request().URI(), err)
|
var fiberError *fiber.Error
|
||||||
|
|
||||||
|
if errors.As(err, &fiberError) {
|
||||||
|
return ctx.SendStatus(fiberError.Code)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("Error: %v - URI: %s\n", err, ctx.Request().URI())
|
||||||
|
|
||||||
return ctx.SendStatus(http.StatusInternalServerError)
|
return ctx.SendStatus(http.StatusInternalServerError)
|
||||||
},
|
},
|
||||||
@@ -38,21 +42,6 @@ func init() {
|
|||||||
|
|
||||||
log.Println("Successfully connected to Redis")
|
log.Println("Successfully connected to Redis")
|
||||||
|
|
||||||
app.Use(recover.New())
|
|
||||||
|
|
||||||
if config.Environment == "development" {
|
|
||||||
app.Use(cors.New(cors.Config{
|
|
||||||
AllowOrigins: "*",
|
|
||||||
AllowMethods: "HEAD,OPTIONS,GET",
|
|
||||||
ExposeHeaders: "X-Cache-Hit,X-Cache-Time-Remaining",
|
|
||||||
}))
|
|
||||||
|
|
||||||
app.Use(logger.New(logger.Config{
|
|
||||||
Format: "${time} ${ip}:${port} -> ${status}: ${method} ${path} (${latency})\n",
|
|
||||||
TimeFormat: "2006/01/02 15:04:05",
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
if instanceID, err = GetInstanceID(); err != nil {
|
if instanceID, err = GetInstanceID(); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ func Render(renderType, uuid string, rawSkin *image.NRGBA, isSlim bool, opts *Qu
|
|||||||
|
|
||||||
// Encode the image into a PNG in byte-array format
|
// Encode the image into a PNG in byte-array format
|
||||||
{
|
{
|
||||||
data, err = EncodePNG(result)
|
data, err = EncodeImage(result, opts)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, false, err
|
return nil, false, err
|
||||||
|
|||||||
137
src/routes.go
137
src/routes.go
@@ -5,22 +5,35 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
)
|
"github.com/gofiber/fiber/v2/middleware/cors"
|
||||||
|
"github.com/gofiber/fiber/v2/middleware/favicon"
|
||||||
var (
|
"github.com/gofiber/fiber/v2/middleware/logger"
|
||||||
lastCount uint64 = 0
|
"github.com/gofiber/fiber/v2/middleware/recover"
|
||||||
lastCountRetrievedAt *time.Time = nil
|
|
||||||
lastCountMutex *sync.Mutex = &sync.Mutex{}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
app.Use(recover.New())
|
||||||
|
|
||||||
|
app.Use(favicon.New(favicon.Config{
|
||||||
|
Data: faviconData,
|
||||||
|
}))
|
||||||
|
|
||||||
|
if config.Environment == "development" {
|
||||||
|
app.Use(cors.New(cors.Config{
|
||||||
|
AllowOrigins: "*",
|
||||||
|
AllowMethods: "HEAD,OPTIONS,GET",
|
||||||
|
ExposeHeaders: "X-Cache-Hit,X-Cache-Time-Remaining",
|
||||||
|
}))
|
||||||
|
|
||||||
|
app.Use(logger.New(logger.Config{
|
||||||
|
Format: "${time} ${ip}:${port} -> ${status}: ${method} ${path} (${latency})\n",
|
||||||
|
TimeFormat: "2006/01/02 15:04:05",
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
app.Get("/ping", PingHandler)
|
app.Get("/ping", PingHandler)
|
||||||
app.Get("/favicon.ico", FaviconHandler)
|
|
||||||
app.Get("/count", CountHandler)
|
|
||||||
app.Get("/list", ListHandler)
|
app.Get("/list", ListHandler)
|
||||||
app.Get("/skin/:uuid", SkinHandler)
|
app.Get("/skin/:uuid", SkinHandler)
|
||||||
app.Get("/face/:uuid", FaceHandler)
|
app.Get("/face/:uuid", FaceHandler)
|
||||||
@@ -30,11 +43,6 @@ func init() {
|
|||||||
app.Get("/body/back/:uuid", BackBodyHandler)
|
app.Get("/body/back/:uuid", BackBodyHandler)
|
||||||
app.Get("/body/left/:uuid", LeftBodyHandler)
|
app.Get("/body/left/:uuid", LeftBodyHandler)
|
||||||
app.Get("/body/right/:uuid", RightBodyHandler)
|
app.Get("/body/right/:uuid", RightBodyHandler)
|
||||||
app.Use(NotFoundHandler)
|
|
||||||
}
|
|
||||||
|
|
||||||
type CountResponse struct {
|
|
||||||
Count uint64 `json:"count"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// PingHandler is the API handler used for the `/ping` route.
|
// PingHandler is the API handler used for the `/ping` route.
|
||||||
@@ -42,48 +50,6 @@ func PingHandler(ctx *fiber.Ctx) error {
|
|||||||
return ctx.SendStatus(http.StatusOK)
|
return ctx.SendStatus(http.StatusOK)
|
||||||
}
|
}
|
||||||
|
|
||||||
// FaviconHandler serves the favicon.ico file to any users that visit the API using a browser.
|
|
||||||
func FaviconHandler(ctx *fiber.Ctx) error {
|
|
||||||
return ctx.Type("ico").Send(favicon)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CountHandler is the API handler used for the `/count` route.
|
|
||||||
func CountHandler(ctx *fiber.Ctx) error {
|
|
||||||
lastCountMutex.Lock()
|
|
||||||
|
|
||||||
defer lastCountMutex.Unlock()
|
|
||||||
|
|
||||||
if lastCountRetrievedAt == nil || time.Since(*lastCountRetrievedAt) > time.Minute*15 {
|
|
||||||
lastCount = 0
|
|
||||||
|
|
||||||
var (
|
|
||||||
keys []string
|
|
||||||
cursor uint64 = 0
|
|
||||||
err error
|
|
||||||
)
|
|
||||||
|
|
||||||
for {
|
|
||||||
keys, cursor, err = r.Scan(cursor, "unique:*", 25)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
lastCount += uint64(len(keys))
|
|
||||||
|
|
||||||
if cursor == 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
lastCountRetrievedAt = PointerOf(time.Now())
|
|
||||||
}
|
|
||||||
|
|
||||||
return ctx.JSON(CountResponse{
|
|
||||||
Count: lastCount,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListHandler is the API handler used for the `/list` route.
|
// ListHandler is the API handler used for the `/list` route.
|
||||||
func ListHandler(ctx *fiber.Ctx) error {
|
func ListHandler(ctx *fiber.Ctx) error {
|
||||||
result := make([]string, 0)
|
result := make([]string, 0)
|
||||||
@@ -117,6 +83,10 @@ func ListHandler(ctx *fiber.Ctx) error {
|
|||||||
func SkinHandler(ctx *fiber.Ctx) error {
|
func SkinHandler(ctx *fiber.Ctx) error {
|
||||||
opts := ParseQueryParams(ctx, config.Routes.RawSkin)
|
opts := ParseQueryParams(ctx, config.Routes.RawSkin)
|
||||||
|
|
||||||
|
if opts == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
uuid, ok := ParseUUID(ctx.Params("uuid"))
|
uuid, ok := ParseUUID(ctx.Params("uuid"))
|
||||||
|
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -129,23 +99,27 @@ func SkinHandler(ctx *fiber.Ctx) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
data, err := EncodePNG(rawSkin)
|
data, err := EncodeImage(rawSkin, opts)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if opts.Download {
|
if opts.Download {
|
||||||
ctx.Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s.png"`, uuid))
|
ctx.Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s.%s"`, uuid, opts.Format))
|
||||||
}
|
}
|
||||||
|
|
||||||
return ctx.Type("png").Send(data)
|
return ctx.Type(opts.Format).Send(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
// FaceHandler is the API handler used for the `/face/:uuid` route.
|
// FaceHandler is the API handler used for the `/face/:uuid` route.
|
||||||
func FaceHandler(ctx *fiber.Ctx) error {
|
func FaceHandler(ctx *fiber.Ctx) error {
|
||||||
opts := ParseQueryParams(ctx, config.Routes.Face)
|
opts := ParseQueryParams(ctx, config.Routes.Face)
|
||||||
|
|
||||||
|
if opts == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
uuid, ok := ParseUUID(ExtractUUID(ctx))
|
uuid, ok := ParseUUID(ExtractUUID(ctx))
|
||||||
|
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -170,13 +144,17 @@ func FaceHandler(ctx *fiber.Ctx) error {
|
|||||||
ctx.Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s.png"`, uuid))
|
ctx.Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s.png"`, uuid))
|
||||||
}
|
}
|
||||||
|
|
||||||
return ctx.Type("png").Send(result)
|
return ctx.Type(opts.Format).Send(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
// HeadHandler is the API handler used for the `/head/:uuid` route.
|
// HeadHandler is the API handler used for the `/head/:uuid` route.
|
||||||
func HeadHandler(ctx *fiber.Ctx) error {
|
func HeadHandler(ctx *fiber.Ctx) error {
|
||||||
opts := ParseQueryParams(ctx, config.Routes.Head)
|
opts := ParseQueryParams(ctx, config.Routes.Head)
|
||||||
|
|
||||||
|
if opts == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
uuid, ok := ParseUUID(ExtractUUID(ctx))
|
uuid, ok := ParseUUID(ExtractUUID(ctx))
|
||||||
|
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -201,13 +179,17 @@ func HeadHandler(ctx *fiber.Ctx) error {
|
|||||||
ctx.Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s.png"`, uuid))
|
ctx.Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s.png"`, uuid))
|
||||||
}
|
}
|
||||||
|
|
||||||
return ctx.Type("png").Send(result)
|
return ctx.Type(opts.Format).Send(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
// FullBodyHandler is the API handler used for the `/body/full/:uuid` route.
|
// FullBodyHandler is the API handler used for the `/body/full/:uuid` route.
|
||||||
func FullBodyHandler(ctx *fiber.Ctx) error {
|
func FullBodyHandler(ctx *fiber.Ctx) error {
|
||||||
opts := ParseQueryParams(ctx, config.Routes.FullBody)
|
opts := ParseQueryParams(ctx, config.Routes.FullBody)
|
||||||
|
|
||||||
|
if opts == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
uuid, ok := ParseUUID(ExtractUUID(ctx))
|
uuid, ok := ParseUUID(ExtractUUID(ctx))
|
||||||
|
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -232,13 +214,17 @@ func FullBodyHandler(ctx *fiber.Ctx) error {
|
|||||||
ctx.Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s.png"`, uuid))
|
ctx.Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s.png"`, uuid))
|
||||||
}
|
}
|
||||||
|
|
||||||
return ctx.Type("png").Send(result)
|
return ctx.Type(opts.Format).Send(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
// FrontBodyHandler is the API handler used for the `/body/front/:uuid` route.
|
// FrontBodyHandler is the API handler used for the `/body/front/:uuid` route.
|
||||||
func FrontBodyHandler(ctx *fiber.Ctx) error {
|
func FrontBodyHandler(ctx *fiber.Ctx) error {
|
||||||
opts := ParseQueryParams(ctx, config.Routes.FrontBody)
|
opts := ParseQueryParams(ctx, config.Routes.FrontBody)
|
||||||
|
|
||||||
|
if opts == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
uuid, ok := ParseUUID(ExtractUUID(ctx))
|
uuid, ok := ParseUUID(ExtractUUID(ctx))
|
||||||
|
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -263,13 +249,17 @@ func FrontBodyHandler(ctx *fiber.Ctx) error {
|
|||||||
ctx.Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s.png"`, uuid))
|
ctx.Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s.png"`, uuid))
|
||||||
}
|
}
|
||||||
|
|
||||||
return ctx.Type("png").Send(result)
|
return ctx.Type(opts.Format).Send(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
// BackBodyHandler is the API handler used for the `/body/back/:uuid` route.
|
// BackBodyHandler is the API handler used for the `/body/back/:uuid` route.
|
||||||
func BackBodyHandler(ctx *fiber.Ctx) error {
|
func BackBodyHandler(ctx *fiber.Ctx) error {
|
||||||
opts := ParseQueryParams(ctx, config.Routes.BackBody)
|
opts := ParseQueryParams(ctx, config.Routes.BackBody)
|
||||||
|
|
||||||
|
if opts == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
uuid, ok := ParseUUID(ExtractUUID(ctx))
|
uuid, ok := ParseUUID(ExtractUUID(ctx))
|
||||||
|
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -294,13 +284,17 @@ func BackBodyHandler(ctx *fiber.Ctx) error {
|
|||||||
ctx.Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s.png"`, uuid))
|
ctx.Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s.png"`, uuid))
|
||||||
}
|
}
|
||||||
|
|
||||||
return ctx.Type("png").Send(result)
|
return ctx.Type(opts.Format).Send(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
// LeftBodyHandler is the API handler used for the `/body/left/:uuid` route.
|
// LeftBodyHandler is the API handler used for the `/body/left/:uuid` route.
|
||||||
func LeftBodyHandler(ctx *fiber.Ctx) error {
|
func LeftBodyHandler(ctx *fiber.Ctx) error {
|
||||||
opts := ParseQueryParams(ctx, config.Routes.LeftBody)
|
opts := ParseQueryParams(ctx, config.Routes.LeftBody)
|
||||||
|
|
||||||
|
if opts == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
uuid, ok := ParseUUID(ExtractUUID(ctx))
|
uuid, ok := ParseUUID(ExtractUUID(ctx))
|
||||||
|
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -325,13 +319,17 @@ func LeftBodyHandler(ctx *fiber.Ctx) error {
|
|||||||
ctx.Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s.png"`, uuid))
|
ctx.Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s.png"`, uuid))
|
||||||
}
|
}
|
||||||
|
|
||||||
return ctx.Type("png").Send(result)
|
return ctx.Type(opts.Format).Send(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
// RightBodyHandler is the API handler used for the `/body/right/:uuid` route.
|
// RightBodyHandler is the API handler used for the `/body/right/:uuid` route.
|
||||||
func RightBodyHandler(ctx *fiber.Ctx) error {
|
func RightBodyHandler(ctx *fiber.Ctx) error {
|
||||||
opts := ParseQueryParams(ctx, config.Routes.RightBody)
|
opts := ParseQueryParams(ctx, config.Routes.RightBody)
|
||||||
|
|
||||||
|
if opts == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
uuid, ok := ParseUUID(ExtractUUID(ctx))
|
uuid, ok := ParseUUID(ExtractUUID(ctx))
|
||||||
|
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -356,10 +354,5 @@ func RightBodyHandler(ctx *fiber.Ctx) error {
|
|||||||
ctx.Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s.png"`, uuid))
|
ctx.Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s.png"`, uuid))
|
||||||
}
|
}
|
||||||
|
|
||||||
return ctx.Type("png").Send(result)
|
return ctx.Type(opts.Format).Send(result)
|
||||||
}
|
|
||||||
|
|
||||||
// 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)
|
|
||||||
}
|
}
|
||||||
|
|||||||
101
src/util.go
101
src/util.go
@@ -8,6 +8,8 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"image"
|
"image"
|
||||||
"image/draw"
|
"image/draw"
|
||||||
|
"image/gif"
|
||||||
|
"image/jpeg"
|
||||||
"image/png"
|
"image/png"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
@@ -21,14 +23,21 @@ import (
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
//go:embed favicon.ico
|
//go:embed favicon.ico
|
||||||
favicon []byte
|
faviconData []byte
|
||||||
|
AllowedFormats []string = []string{
|
||||||
|
"png",
|
||||||
|
"jpg",
|
||||||
|
"jpeg",
|
||||||
|
"gif",
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// QueryParams is used by most all API routes as options for how the image should be rendered, or how errors should be handled.
|
// QueryParams is used by most all API routes as options for how the image should be rendered, or how errors should be handled.
|
||||||
type QueryParams struct {
|
type QueryParams struct {
|
||||||
Scale int `query:"scale"`
|
Scale int
|
||||||
Download bool `query:"download"`
|
Download bool
|
||||||
Overlay bool `query:"overlay"`
|
Overlay bool
|
||||||
|
Format string
|
||||||
}
|
}
|
||||||
|
|
||||||
// PointerOf returns the value of the first argument as a pointer.
|
// PointerOf returns the value of the first argument as a pointer.
|
||||||
@@ -36,6 +45,19 @@ func PointerOf[T any](v T) *T {
|
|||||||
return &v
|
return &v
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Contains returns true if the array contains the value.
|
||||||
|
func Contains[T comparable](arr []T, value T) bool {
|
||||||
|
for _, v := range arr {
|
||||||
|
if v != value {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// Clamp clamps the input value between the minimum and maximum values.
|
// Clamp clamps the input value between the minimum and maximum values.
|
||||||
// This method is preferred over `math.Min()` and `math.Max()` to prevent any type coercion between floats and integers.
|
// This method is preferred over `math.Min()` and `math.Max()` to prevent any type coercion between floats and integers.
|
||||||
func Clamp[T int | uint | int8 | uint8 | int16 | uint16 | int32 | uint32 | int64 | uint64](value, min, max T) T {
|
func Clamp[T int | uint | int8 | uint8 | int16 | uint16 | int32 | uint32 | int64 | uint64](value, min, max T) T {
|
||||||
@@ -200,33 +222,60 @@ func EncodePNG(img image.Image) ([]byte, error) {
|
|||||||
return buf.Bytes(), nil
|
return buf.Bytes(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// EncodeImage encodes the image into the format specified by the query parameters.
|
||||||
|
func EncodeImage(img image.Image, opts *QueryParams) ([]byte, error) {
|
||||||
|
buf := &bytes.Buffer{}
|
||||||
|
|
||||||
|
switch opts.Format {
|
||||||
|
case "png":
|
||||||
|
{
|
||||||
|
if err := png.Encode(buf, img); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case "jpg", "jpeg":
|
||||||
|
{
|
||||||
|
if err := jpeg.Encode(buf, img, nil); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case "gif":
|
||||||
|
{
|
||||||
|
if err := gif.Encode(buf, img, nil); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("invalid format: %s", opts.Format)
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf.Bytes(), nil
|
||||||
|
}
|
||||||
|
|
||||||
// ParseQueryParams parses the query parameters from the request and returns a QueryParams struct, using default values from the provided configuration.
|
// ParseQueryParams parses the query parameters from the request and returns a QueryParams struct, using default values from the provided configuration.
|
||||||
func ParseQueryParams(ctx *fiber.Ctx, route RouteConfig) *QueryParams {
|
func ParseQueryParams(ctx *fiber.Ctx, route RouteConfig) *QueryParams {
|
||||||
args := ctx.Context().QueryArgs()
|
format := ctx.Query("format", route.DefaultFormat)
|
||||||
|
|
||||||
response := &QueryParams{
|
if !Contains(AllowedFormats, format) {
|
||||||
Scale: route.DefaultScale,
|
ctx.Status(http.StatusBadRequest).SendString("Invalid 'format' query parameter")
|
||||||
Download: route.DefaultDownload,
|
|
||||||
Overlay: route.DefaultOverlay,
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if args.Has("scale") {
|
return &QueryParams{
|
||||||
if scale, err := args.GetUint("scale"); err == nil {
|
Scale: Clamp(ctx.QueryInt("scale", route.DefaultScale), route.MinScale, route.MaxScale),
|
||||||
response.Scale = Clamp(scale, route.MinScale, route.MaxScale)
|
Download: ctx.QueryBool("download", route.DefaultDownload),
|
||||||
|
Overlay: ctx.QueryBool("overlay", route.DefaultOverlay),
|
||||||
|
Format: ctx.Query("format", route.DefaultFormat),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if args.Has("overlay") {
|
|
||||||
response.Overlay = args.GetBool("overlay")
|
|
||||||
}
|
|
||||||
|
|
||||||
if args.Has("download") {
|
|
||||||
response.Download = args.GetBool("download")
|
|
||||||
}
|
|
||||||
|
|
||||||
return response
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetInstanceID returns the INSTANCE_ID environment variable parsed as an unsigned 16-bit integer.
|
// GetInstanceID returns the INSTANCE_ID environment variable parsed as an unsigned 16-bit integer.
|
||||||
func GetInstanceID() (uint16, error) {
|
func GetInstanceID() (uint16, error) {
|
||||||
if instanceID := os.Getenv("INSTANCE_ID"); len(instanceID) > 0 {
|
if instanceID := os.Getenv("INSTANCE_ID"); len(instanceID) > 0 {
|
||||||
@@ -242,9 +291,9 @@ func GetInstanceID() (uint16, error) {
|
|||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SHA256 computes the SHA-256 hash of the input byte-array.
|
// SHA256 computes the SHA-256 hash of the input string.
|
||||||
func SHA256(value []byte) string {
|
func SHA256(value string) string {
|
||||||
hash := sha256.Sum256(value)
|
hash := sha256.Sum256([]byte(value))
|
||||||
|
|
||||||
return hex.EncodeToString(hash[:])
|
return hex.EncodeToString(hash[:])
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user