From 1d5ec4353e64b750b947248f0acf4d2de7a4dec6 Mon Sep 17 00:00:00 2001 From: Laptop Date: Tue, 31 Dec 2024 16:22:23 +0200 Subject: [PATCH] port to fiber v3, optimize static assets serving --- .dockerignore | 5 +- .gitignore | 5 +- Dockerfile | 3 +- docs/DEV_GUIDE.md | 6 +- go.mod | 7 +- go.sum | 11 +-- lib/cfg/init.go | 2 +- lib/preferences/init.go | 22 ++--- lib/proxy_images/init.go | 4 +- lib/proxy_streams/init.go | 10 +- lib/restream/init.go | 4 +- main.go | 188 +++++++++++++++++++++++++++----------- templates/base.templ | 1 + 13 files changed, 176 insertions(+), 92 deletions(-) diff --git a/.dockerignore b/.dockerignore index 5a5633d..cacf93c 100644 --- a/.dockerignore +++ b/.dockerignore @@ -8,7 +8,10 @@ LICENSE README.md docs -*.fiber.gz +.compression +.br +.gzip +.zstd *_templ.go regexp2_codegen.go main diff --git a/.gitignore b/.gitignore index c8c8cc5..23ab167 100644 --- a/.gitignore +++ b/.gitignore @@ -10,7 +10,10 @@ compose.yaml fly.toml # created by soundcloak/dependencies -*.fiber.gz +.compression +*.br +*.gzip +*.zstd # codegen *_templ.go diff --git a/Dockerfile b/Dockerfile index 29927aa..2f34593 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -ARG GO_VERSION=1.22.10 +ARG GO_VERSION=1.23.4 FROM --platform=$BUILDPLATFORM golang:${GO_VERSION} AS build ARG TARGETOS @@ -20,6 +20,7 @@ RUN soundcloakctl js download RUN templ generate RUN go generate ./lib/* RUN soundcloakctl config codegen +RUN soundcloakctl -nozstd -notable precompress RUN CGO_ENABLED=0 GOARCH=${TARGETARCH} GOOS=${TARGETOS} go build -ldflags "-s -w -extldflags '-static'" -o ./app RUN echo "soundcloak:x:5000:5000:Soundcloak user:/:/sbin/nologin" > /etc/minimal-passwd && \ diff --git a/docs/DEV_GUIDE.md b/docs/DEV_GUIDE.md index fee317d..edb16dd 100644 --- a/docs/DEV_GUIDE.md +++ b/docs/DEV_GUIDE.md @@ -1,6 +1,6 @@ # Setup ## Prerequisites -1. [golang](https://go.dev) (I recommend version 1.22.10. Technically, you need 1.21.4 or higher) +1. [golang](https://go.dev) (I recommend version 1.23.4) 2. [git](https://git-scm.com) ## The setup @@ -90,9 +90,9 @@ soundcloakctl js download # re-download JS modules 3. Clean precompressed static files -Those are created by the webserver in order to more efficiently serve static files. They have the `.fiber.gz` extension. You can easily remove them from all directories like this: +Those are created by the webserver in order to more efficiently serve static files. They have the `.br` and `.gzip` extension. You can easily remove them from all directories using soundcloakctl: ```sh -find . -name \*.fiber.gz -type f -delete +soundcloakctl clean ``` 4. Run codegen and build the binary: diff --git a/go.mod b/go.mod index 3fe150c..4f0f9ac 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module git.maid.zone/stuff/soundcloak -go 1.22.10 +go 1.23.4 require ( github.com/a-h/templ v0.2.793 @@ -9,7 +9,7 @@ require ( github.com/gcottom/mp4meta v0.0.4 github.com/gcottom/oggmeta v0.0.7 github.com/goccy/go-json v0.10.4 - github.com/gofiber/fiber/v2 v2.52.5 + github.com/gofiber/fiber/v3 v3.0.0-beta.3 github.com/valyala/fasthttp v1.58.0 ) @@ -17,12 +17,11 @@ require ( github.com/abema/go-mp4 v1.2.0 // indirect github.com/aler9/writerseeker v1.1.0 // indirect github.com/andybalholm/brotli v1.1.1 // indirect + github.com/gofiber/utils/v2 v2.0.0-beta.4 // indirect github.com/google/uuid v1.6.0 // indirect github.com/klauspost/compress v1.17.11 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect - github.com/mattn/go-runewidth v0.0.16 // indirect - github.com/rivo/uniseg v0.4.7 // indirect github.com/sunfish-shogi/bufseekio v0.1.0 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/tcplisten v1.0.0 // indirect diff --git a/go.sum b/go.sum index de2f273..589a117 100644 --- a/go.sum +++ b/go.sum @@ -20,8 +20,10 @@ github.com/gcottom/oggmeta v0.0.7 h1:6mKm/9xhDeKKIOrB0K+daJoXOLbyRq84e5xV3dndDt8 github.com/gcottom/oggmeta v0.0.7/go.mod h1:ifdINhphaEW587BIgA+m5TJwCoVk88JuZMCHwXc/gpM= github.com/goccy/go-json v0.10.4 h1:JSwxQzIqKfmFX1swYPpUThQZp/Ka4wzJdK0LWVytLPM= github.com/goccy/go-json v0.10.4/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= -github.com/gofiber/fiber/v2 v2.52.5 h1:tWoP1MJQjGEe4GB5TUGOi7P2E0ZMMRx5ZTG4rT+yGMo= -github.com/gofiber/fiber/v2 v2.52.5/go.mod h1:KEOE+cXMhXG0zHc9d8+E38hoX+ZN7bhOtgeF2oT6jrQ= +github.com/gofiber/fiber/v3 v3.0.0-beta.3 h1:7Q2I+HsIqnIEEDB+9oe7Gadpakh6ZLhXpTYz/L20vrg= +github.com/gofiber/fiber/v3 v3.0.0-beta.3/go.mod h1:kcMur0Dxqk91R7p4vxEpJfDWZ9u5IfvrtQc8Bvv/JmY= +github.com/gofiber/utils/v2 v2.0.0-beta.4 h1:1gjbVFFwVwUb9arPcqiB6iEjHBwo7cHsyS41NeIW3co= +github.com/gofiber/utils/v2 v2.0.0-beta.4/go.mod h1:sdRsPU1FXX6YiDGGxd+q2aPJRMzpsxdzCXo9dz+xtOY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -38,15 +40,10 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= -github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/orcaman/writerseeker v0.0.0-20200621085525-1d3f536ff85e h1:s2RNOM/IGdY0Y6qfTeUKhDawdHDpK9RGBdx80qN4Ttw= github.com/orcaman/writerseeker v0.0.0-20200621085525-1d3f536ff85e/go.mod h1:nBdnFKj15wFbf94Rwfq4m30eAcyY9V/IyKAGQFtqkW0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= -github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= diff --git a/lib/cfg/init.go b/lib/cfg/init.go index 27c1503..49b00df 100644 --- a/lib/cfg/init.go +++ b/lib/cfg/init.go @@ -538,7 +538,7 @@ func init() { } } -const Debug = false +const Debug = true const Commit = "unknown" const Repo = "unknown" const CommitURL = "unknown" diff --git a/lib/preferences/init.go b/lib/preferences/init.go index 65914d0..35bbdb8 100644 --- a/lib/preferences/init.go +++ b/lib/preferences/init.go @@ -8,7 +8,7 @@ import ( "git.maid.zone/stuff/soundcloak/lib/cfg" "git.maid.zone/stuff/soundcloak/templates" - "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v3" ) func Defaults(dst *cfg.Preferences) { @@ -61,7 +61,7 @@ func Defaults(dst *cfg.Preferences) { } } -func Get(c *fiber.Ctx) (cfg.Preferences, error) { +func Get(c fiber.Ctx) (cfg.Preferences, error) { rawprefs := c.Cookies("prefs", "{}") var p cfg.Preferences @@ -94,7 +94,7 @@ type Export struct { } func Load(r fiber.Router) { - r.Get("/_/preferences", func(c *fiber.Ctx) error { + r.Get("/_/preferences", func(c fiber.Ctx) error { p, err := Get(c) if err != nil { return err @@ -104,9 +104,9 @@ func Load(r fiber.Router) { return templates.Base("preferences", templates.Preferences(p), nil).Render(context.Background(), c) }) - r.Post("/_/preferences", func(c *fiber.Ctx) error { + r.Post("/_/preferences", func(c fiber.Ctx) error { var p PrefsForm - err := c.BodyParser(&p) + err := c.Bind().Body(&p) if err != nil { return err } @@ -193,10 +193,10 @@ func Load(r fiber.Router) { SameSite: "strict", }) - return c.Redirect("/_/preferences") + return c.Redirect().To("/_/preferences") }) - r.Get("/_/preferences/reset", func(c *fiber.Ctx) error { + r.Get("/_/preferences/reset", func(c fiber.Ctx) error { // c.ClearCookie("prefs") c.Cookie(&fiber.Cookie{ // I've had some issues with c.ClearCookie() method, so using this workaround for now Name: "prefs", @@ -206,10 +206,10 @@ func Load(r fiber.Router) { SameSite: "strict", }) - return c.Redirect("/_/preferences") + return c.Redirect().To("/_/preferences") }) - r.Get("/_/preferences/export", func(c *fiber.Ctx) error { + r.Get("/_/preferences/export", func(c fiber.Ctx) error { p, err := Get(c) if err != nil { return err @@ -218,7 +218,7 @@ func Load(r fiber.Router) { return c.JSON(Export{Preferences: &p}) }) - r.Post("/_/preferences/import", func(c *fiber.Ctx) error { + r.Post("/_/preferences/import", func(c fiber.Ctx) error { f, err := c.FormFile("prefs") if err != nil { return err @@ -257,6 +257,6 @@ func Load(r fiber.Router) { SameSite: "strict", }) - return c.Redirect("/_/preferences") + return c.Redirect().To("/_/preferences") }) } diff --git a/lib/proxy_images/init.go b/lib/proxy_images/init.go index ea85b47..2b080bf 100644 --- a/lib/proxy_images/init.go +++ b/lib/proxy_images/init.go @@ -6,7 +6,7 @@ import ( "git.maid.zone/stuff/soundcloak/lib/cfg" "git.maid.zone/stuff/soundcloak/lib/misc" "git.maid.zone/stuff/soundcloak/lib/sc" - "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v3" "github.com/valyala/fasthttp" ) @@ -32,7 +32,7 @@ func Load(r fiber.Router) { StreamResponseBody: true, } - r.Get("/_/proxy/images", func(c *fiber.Ctx) error { + r.Get("/_/proxy/images", func(c fiber.Ctx) error { url := c.Query("url") if url == "" { return fiber.ErrBadRequest diff --git a/lib/proxy_streams/init.go b/lib/proxy_streams/init.go index 54644f3..5ced213 100644 --- a/lib/proxy_streams/init.go +++ b/lib/proxy_streams/init.go @@ -7,7 +7,7 @@ import ( "git.maid.zone/stuff/soundcloak/lib/cfg" "git.maid.zone/stuff/soundcloak/lib/misc" "git.maid.zone/stuff/soundcloak/lib/sc" - "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v3" "github.com/valyala/fasthttp" ) @@ -33,7 +33,7 @@ func Load(r fiber.Router) { StreamResponseBody: true, } - r.Get("/_/proxy/streams", func(c *fiber.Ctx) error { + r.Get("/_/proxy/streams", func(c fiber.Ctx) error { ur := c.Query("url") if ur == "" { return fiber.ErrBadRequest @@ -72,7 +72,7 @@ func Load(r fiber.Router) { return c.SendStream(pr) }) - r.Get("/_/proxy/streams/aac", func(c *fiber.Ctx) error { + r.Get("/_/proxy/streams/aac", func(c fiber.Ctx) error { ur := c.Query("url") if ur == "" { return fiber.ErrBadRequest @@ -110,7 +110,7 @@ func Load(r fiber.Router) { return c.SendStream(pr) }) - r.Get("/_/proxy/streams/playlist", func(c *fiber.Ctx) error { + r.Get("/_/proxy/streams/playlist", func(c fiber.Ctx) error { ur := c.Query("url") if ur == "" { return fiber.ErrBadRequest @@ -161,7 +161,7 @@ func Load(r fiber.Router) { return c.Send(bytes.Join(sp, []byte("\n"))) }) - r.Get("/_/proxy/streams/playlist/aac", func(c *fiber.Ctx) error { + r.Get("/_/proxy/streams/playlist/aac", func(c fiber.Ctx) error { ur := c.Query("url") if ur == "" { return fiber.ErrBadRequest diff --git a/lib/restream/init.go b/lib/restream/init.go index 2c9c225..0fbbafa 100644 --- a/lib/restream/init.go +++ b/lib/restream/init.go @@ -13,7 +13,7 @@ import ( "github.com/bogem/id3v2/v2" "github.com/gcottom/mp4meta" "github.com/gcottom/oggmeta" - "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v3" "github.com/valyala/fasthttp" ) @@ -220,7 +220,7 @@ func Load(r fiber.Router) { StreamResponseBody: true, } - r.Get("/_/restream/:author/:track", func(c *fiber.Ctx) error { + r.Get("/_/restream/:author/:track", func(c fiber.Ctx) error { p, err := preferences.Get(c) if err != nil { return err diff --git a/main.go b/main.go index 7301de1..6859b36 100644 --- a/main.go +++ b/main.go @@ -1,18 +1,19 @@ package main import ( + "io/fs" "log" "math/rand" - "net/http" "net/url" + "os" "strings" - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/compress" - "github.com/gofiber/fiber/v2/middleware/filesystem" + "git.maid.zone/stuff/soundcloak/lib/misc" + "github.com/gofiber/fiber/v3" "github.com/goccy/go-json" - "github.com/gofiber/fiber/v2/middleware/recover" + "github.com/gofiber/fiber/v3/middleware/compress" + "github.com/gofiber/fiber/v3/middleware/recover" "github.com/valyala/fasthttp" "git.maid.zone/stuff/soundcloak/lib/cfg" @@ -21,13 +22,81 @@ import ( proxystreams "git.maid.zone/stuff/soundcloak/lib/proxy_streams" "git.maid.zone/stuff/soundcloak/lib/restream" "git.maid.zone/stuff/soundcloak/lib/sc" - "git.maid.zone/stuff/soundcloak/static" + static_files "git.maid.zone/stuff/soundcloak/static" "git.maid.zone/stuff/soundcloak/templates" ) +type osfs struct{} + +func (osfs) Open(name string) (fs.File, error) { + misc.Log("osfs:", name) + + if strings.HasPrefix(name, "js/") { + return os.Open("static/external/" + name[3:]) + } + + f, err := os.Open("static/instance/" + name) + if err == nil { + return f, nil + } + + f, err = os.Open("static/assets/" + name) + return f, err +} + +type staticfs struct { +} + +func (staticfs) Open(name string) (fs.File, error) { + misc.Log("staticfs:", name) + + if strings.HasPrefix(name, "js/") { + return static_files.External.Open("external/" + name[3:]) + } + + f, err := static_files.Instance.Open("instance/" + name) + if err == nil { + return f, nil + } + + f, err = static_files.Assets.Open("assets/" + name) + return f, err +} + +// stubby implementation of static middleware +// why? mainly because of the pathrewrite +// i hate seeing it trying to get root directory +func ServeFromFS(r fiber.Router, path string, filesystem fs.FS, cachecontrol string, compress bool) { + l := len(path) + fs := fasthttp.FS{ + FS: filesystem, + PathRewrite: func(ctx *fasthttp.RequestCtx) []byte { + return ctx.Path()[l:] + }, + Compress: compress, + CompressBrotli: compress, + CompressedFileSuffixes: map[string]string{ + "gzip": ".gzip", + "br": ".br", + "zstd": ".zstd", + }, + } + + handler := fs.NewRequestHandler() + + r.Use(path, func(c fiber.Ctx) error { + handler(c.Context()) + if c.Context().Response.StatusCode() == 200 { + c.Set("Cache-Control", cachecontrol) + } + + return nil + }) +} + func main() { app := fiber.New(fiber.Config{ - Prefork: cfg.Prefork, + //Prefork: cfg.Prefork, // moved to ListenConfig in v3 JSONEncoder: json.Marshal, JSONDecoder: json.Unmarshal, @@ -38,29 +107,35 @@ func main() { if !cfg.Debug { // you wanna catch any possible panics as soon as possible app.Use(recover.New()) } - app.Use(compress.New(compress.Config{Level: compress.LevelBestSpeed})) + + app.Use(compress.New(compress.Config{ + Next: func(c fiber.Ctx) bool { + return strings.HasPrefix(c.Path(), "/_/static") + }, + Level: compress.LevelBestSpeed, + })) // Just for easy inspection of cache in development. Since debug is constant, the compiler will just remove the code below if it's set to false, so this has no runtime overhead. if cfg.Debug { - app.Get("/_/cachedump/tracks", func(c *fiber.Ctx) error { + app.Get("/_/cachedump/tracks", func(c fiber.Ctx) error { return c.JSON(sc.TracksCache) }) - app.Get("/_/cachedump/playlists", func(c *fiber.Ctx) error { + app.Get("/_/cachedump/playlists", func(c fiber.Ctx) error { return c.JSON(sc.PlaylistsCache) }) - app.Get("/_/cachedump/users", func(c *fiber.Ctx) error { + app.Get("/_/cachedump/users", func(c fiber.Ctx) error { return c.JSON(sc.UsersCache) }) - app.Get("/_/cachedump/clientId", func(c *fiber.Ctx) error { + app.Get("/_/cachedump/clientId", func(c fiber.Ctx) error { return c.JSON(sc.ClientIDCache) }) } { - mainPageHandler := func(c *fiber.Ctx) error { + mainPageHandler := func(c fiber.Ctx) error { prefs, err := preferences.Get(c) if err != nil { return err @@ -74,20 +149,25 @@ func main() { app.Get("/index.html", mainPageHandler) } - const InstanceMaxAge = 7200 // 2hrs - const AssetsMaxAge = 14400 // 4hrs - const ExternalMaxAge = 28800 // 8hrs + const AssetsCacheControl = "public, max-age=28800" // 8hrs if cfg.EmbedFiles { - app.Use("/_/static/", filesystem.New(filesystem.Config{Root: http.FS(static.Instance), PathPrefix: "instance", MaxAge: InstanceMaxAge})) - app.Use("/_/static/", filesystem.New(filesystem.Config{Root: http.FS(static.Assets), PathPrefix: "assets", MaxAge: AssetsMaxAge})) - app.Use("/_/static/js/", filesystem.New(filesystem.Config{Root: http.FS(static.External), PathPrefix: "external", MaxAge: ExternalMaxAge})) + misc.Log("using embedded files") + ServeFromFS(app, "/_/static", staticfs{}, AssetsCacheControl, true) } else { - app.Static("/_/static/", "static/instance", fiber.Static{Compress: true, MaxAge: InstanceMaxAge}) - app.Static("/_/static/", "static/assets", fiber.Static{Compress: true, MaxAge: AssetsMaxAge}) - app.Static("/_/static/js/", "static/external", fiber.Static{Compress: true, MaxAge: ExternalMaxAge}) + misc.Log("loading files dynamically") + ServeFromFS(app, "/_/static", osfs{}, AssetsCacheControl, true) } - app.Get("/search", func(c *fiber.Ctx) error { + // why? because when you load a page without link rel="icon" the browser will + // try to load favicon from default location, + // and this path loads the user "favicon.ico" by default + if cfg.Debug { + app.Get("favicon.ico", func(c fiber.Ctx) error { + return c.Redirect().To("/_/static/favicon.ico") + }) + } + + app.Get("/search", func(c fiber.Ctx) error { prefs, err := preferences.Get(c) if err != nil { return err @@ -132,15 +212,15 @@ func main() { // someone is trying to hit those endpoints on sc.maid.zone at like 4am lol // those are authentication-only, planning to make something similar later on - app.Get("/stream", func(c *fiber.Ctx) error { - return c.Redirect("/") + app.Get("/stream", func(c fiber.Ctx) error { + return c.Redirect().To("/") }) - app.Get("/feed", func(c *fiber.Ctx) error { - return c.Redirect("/") + app.Get("/feed", func(c fiber.Ctx) error { + return c.Redirect().To("/") }) - app.Get("/on/:id", func(c *fiber.Ctx) error { + app.Get("/on/:id", func(c fiber.Ctx) error { id := c.Params("id") if id == "" { return fiber.ErrNotFound @@ -173,10 +253,10 @@ func main() { return err } - return c.Redirect(u.Path) + return c.Redirect().To(u.Path) }) - app.Get("/w/player", func(c *fiber.Ctx) error { + app.Get("/w/player", func(c fiber.Ctx) error { u := c.Query("url") if u == "" { return fiber.ErrNotFound @@ -222,7 +302,7 @@ func main() { return templates.TrackEmbed(prefs, track, stream, displayErr).Render(c.Context(), c) }) - app.Get("/tags/:tag", func(c *fiber.Ctx) error { + app.Get("/tags/:tag", func(c fiber.Ctx) error { prefs, err := preferences.Get(c) if err != nil { return err @@ -244,7 +324,7 @@ func main() { return templates.Base("Recent tracks tagged "+tag, templates.RecentTracks(tag, p), nil).Render(c.Context(), c) }) - app.Get("/tags/:tag/popular-tracks", func(c *fiber.Ctx) error { + app.Get("/tags/:tag/popular-tracks", func(c fiber.Ctx) error { prefs, err := preferences.Get(c) if err != nil { return err @@ -266,7 +346,7 @@ func main() { return templates.Base("Popular tracks tagged "+tag, templates.PopularTracks(tag, p), nil).Render(c.Context(), c) }) - app.Get("/tags/:tag/playlists", func(c *fiber.Ctx) error { + app.Get("/tags/:tag/playlists", func(c fiber.Ctx) error { prefs, err := preferences.Get(c) if err != nil { return err @@ -289,7 +369,7 @@ func main() { return templates.Base("Playlists tagged "+tag, templates.TaggedPlaylists(tag, p), nil).Render(c.Context(), c) }) - app.Get("/_/featured", func(c *fiber.Ctx) error { + app.Get("/_/featured", func(c fiber.Ctx) error { prefs, err := preferences.Get(c) if err != nil { return err @@ -305,7 +385,7 @@ func main() { return templates.Base("Featured Tracks", templates.FeaturedTracks(tracks), nil).Render(c.Context(), c) }) - app.Get("/discover", func(c *fiber.Ctx) error { + app.Get("/discover", func(c fiber.Ctx) error { prefs, err := preferences.Get(c) if err != nil { return err @@ -353,7 +433,7 @@ func main() { log.Fatalln("failed to marshal info: ", err) } - app.Get("/_/info", func(c *fiber.Ctx) error { + app.Get("/_/info", func(c fiber.Ctx) error { c.Set("Content-Type", "application/json") return c.Send(inf) }) @@ -365,7 +445,7 @@ func main() { preferences.Load(app) - app.Get("/_/searchSuggestions", func(c *fiber.Ctx) error { + app.Get("/_/searchSuggestions", func(c fiber.Ctx) error { q := c.Query("q") if q == "" { return fiber.ErrBadRequest @@ -380,11 +460,11 @@ func main() { }) // Currently, /:user is the tracks page - app.Get("/:user/tracks", func(c *fiber.Ctx) error { - return c.Redirect("/" + c.Params("user")) + app.Get("/:user/tracks", func(c fiber.Ctx) error { + return c.Redirect().To("/" + c.Params("user")) }) - app.Get("/:user/sets", func(c *fiber.Ctx) error { + app.Get("/:user/sets", func(c fiber.Ctx) error { prefs, err := preferences.Get(c) if err != nil { return err @@ -412,7 +492,7 @@ func main() { return templates.Base(user.Username, templates.UserPlaylists(prefs, user, pl), templates.UserHeader(user)).Render(c.Context(), c) }) - app.Get("/:user/albums", func(c *fiber.Ctx) error { + app.Get("/:user/albums", func(c fiber.Ctx) error { prefs, err := preferences.Get(c) if err != nil { return err @@ -440,7 +520,7 @@ func main() { return templates.Base(user.Username, templates.UserAlbums(prefs, user, pl), templates.UserHeader(user)).Render(c.Context(), c) }) - app.Get("/:user/reposts", func(c *fiber.Ctx) error { + app.Get("/:user/reposts", func(c fiber.Ctx) error { prefs, err := preferences.Get(c) if err != nil { return err @@ -468,7 +548,7 @@ func main() { return templates.Base(user.Username, templates.UserReposts(prefs, user, p), templates.UserHeader(user)).Render(c.Context(), c) }) - app.Get("/:user/likes", func(c *fiber.Ctx) error { + app.Get("/:user/likes", func(c fiber.Ctx) error { prefs, err := preferences.Get(c) if err != nil { return err @@ -496,7 +576,7 @@ func main() { return templates.Base(user.Username, templates.UserLikes(prefs, user, p), templates.UserHeader(user)).Render(c.Context(), c) }) - app.Get("/:user/popular-tracks", func(c *fiber.Ctx) error { + app.Get("/:user/popular-tracks", func(c fiber.Ctx) error { prefs, err := preferences.Get(c) if err != nil { return err @@ -524,7 +604,7 @@ func main() { return templates.Base(user.Username, templates.UserTopTracks(prefs, user, p), templates.UserHeader(user)).Render(c.Context(), c) }) - app.Get("/:user/followers", func(c *fiber.Ctx) error { + app.Get("/:user/followers", func(c fiber.Ctx) error { prefs, err := preferences.Get(c) if err != nil { return err @@ -552,7 +632,7 @@ func main() { return templates.Base(user.Username, templates.UserFollowers(prefs, user, p), templates.UserHeader(user)).Render(c.Context(), c) }) - app.Get("/:user/following", func(c *fiber.Ctx) error { + app.Get("/:user/following", func(c fiber.Ctx) error { prefs, err := preferences.Get(c) if err != nil { return err @@ -580,7 +660,7 @@ func main() { return templates.Base(user.Username, templates.UserFollowing(prefs, user, p), templates.UserHeader(user)).Render(c.Context(), c) }) - app.Get("/:user/:track", func(c *fiber.Ctx) error { + app.Get("/:user/:track", func(c fiber.Ctx) error { prefs, err := preferences.Get(c) if err != nil { return err @@ -674,7 +754,7 @@ func main() { return templates.Base(track.Title+" by "+track.Author.Username, templates.Track(prefs, track, stream, displayErr, c.Query("autoplay") == "true", playlist, nextTrack, c.Query("volume"), mode, audio), templates.TrackHeader(prefs, track, true)).Render(c.Context(), c) }) - app.Get("/:user", func(c *fiber.Ctx) error { + app.Get("/:user", func(c fiber.Ctx) error { prefs, err := preferences.Get(c) if err != nil { return err @@ -702,7 +782,7 @@ func main() { return templates.Base(usr.Username, templates.User(prefs, usr, p), templates.UserHeader(usr)).Render(c.Context(), c) }) - app.Get("/:user/sets/:playlist", func(c *fiber.Ctx) error { + app.Get("/:user/sets/:playlist", func(c fiber.Ctx) error { prefs, err := preferences.Get(c) if err != nil { return err @@ -742,7 +822,7 @@ func main() { return templates.Base(playlist.Title+" by "+playlist.Author.Username, templates.Playlist(prefs, playlist), templates.PlaylistHeader(playlist)).Render(c.Context(), c) }) - app.Get("/:user/_/related", func(c *fiber.Ctx) error { + app.Get("/:user/_/related", func(c fiber.Ctx) error { prefs, err := preferences.Get(c) if err != nil { return err @@ -771,7 +851,7 @@ func main() { }) // I'd like to make this "related" but keeping it "recommended" to have the same url as soundcloud - app.Get("/:user/:track/recommended", func(c *fiber.Ctx) error { + app.Get("/:user/:track/recommended", func(c fiber.Ctx) error { prefs, err := preferences.Get(c) if err != nil { return err @@ -799,7 +879,7 @@ func main() { return templates.Base(track.Title+" by "+track.Author.Username, templates.RelatedTracks(track, r), templates.TrackHeader(prefs, track, false)).Render(c.Context(), c) }) - app.Get("/:user/:track/sets", func(c *fiber.Ctx) error { + app.Get("/:user/:track/sets", func(c fiber.Ctx) error { prefs, err := preferences.Get(c) if err != nil { return err @@ -827,7 +907,7 @@ func main() { return templates.Base(track.Title+" by "+track.Author.Username, templates.TrackInPlaylists(track, p), templates.TrackHeader(prefs, track, false)).Render(c.Context(), c) }) - app.Get("/:user/:track/albums", func(c *fiber.Ctx) error { + app.Get("/:user/:track/albums", func(c fiber.Ctx) error { prefs, err := preferences.Get(c) if err != nil { return err @@ -858,5 +938,5 @@ func main() { if cfg.CodegenConfig { log.Println("Warning: you have CodegenConfig enabled, but the config was loaded dynamically.") } - log.Fatal(app.Listen(cfg.Addr)) + log.Fatal(app.Listen(cfg.Addr, fiber.ListenConfig{EnablePrefork: cfg.Prefork})) } diff --git a/templates/base.templ b/templates/base.templ index 4eaa198..a4f8e67 100644 --- a/templates/base.templ +++ b/templates/base.templ @@ -10,6 +10,7 @@ templ Base(title string, content templ.Component, head templ.Component) { + if title != "" { { title } ~ soundcloak } else {