From eaabb28b23b62b806566b335ec649c496b9b9bbb Mon Sep 17 00:00:00 2001 From: mkelvers Date: Tue, 16 Jun 2026 17:20:49 +0200 Subject: [PATCH] feat: redirect browse to canonical sfw url --- internal/anime/browse_handler.go | 25 +++++++++++++++++ internal/anime/browse_handler_test.go | 39 +++++++++++++++++++++++++++ 2 files changed, 64 insertions(+) create mode 100644 internal/anime/browse_handler_test.go diff --git a/internal/anime/browse_handler.go b/internal/anime/browse_handler.go index af1cf86..ba31559 100644 --- a/internal/anime/browse_handler.go +++ b/internal/anime/browse_handler.go @@ -8,6 +8,7 @@ import ( "mal/internal/observability" "mal/internal/server" "net/http" + "net/url" "strconv" "strings" "time" @@ -181,6 +182,25 @@ func parseBrowseQuery(c *gin.Context) (browseQuery, error) { }, nil } +func canonicalBrowseURL(rawURL *url.URL) (string, bool) { + if rawURL == nil { + return "", false + } + + query := rawURL.Query() + if _, exists := query["sfw"]; exists { + return "", false + } + + query.Set("sfw", "true") + encoded := query.Encode() + if encoded == "" { + return rawURL.Path, true + } + + return rawURL.Path + "?" + encoded, true +} + func browseStudioName(ctx context.Context, svc Service, studioID int) string { if studioID <= 0 { return "" @@ -286,6 +306,11 @@ func (h *AnimeHandler) respondBrowseSearchError(c *gin.Context, query browseQuer } func (h *AnimeHandler) HandleBrowse(c *gin.Context) { + if target, ok := canonicalBrowseURL(c.Request.URL); ok { + c.Redirect(http.StatusSeeOther, target) + return + } + query, err := parseBrowseQuery(c) if err != nil { server.RespondHTMLOrJSONError(c, http.StatusBadRequest, err.Error()) diff --git a/internal/anime/browse_handler_test.go b/internal/anime/browse_handler_test.go new file mode 100644 index 0000000..d5cccbc --- /dev/null +++ b/internal/anime/browse_handler_test.go @@ -0,0 +1,39 @@ +package anime + +import ( + "net/url" + "testing" +) + +func TestCanonicalBrowseURLAddsSFWTrueWhenMissing(t *testing.T) { + t.Parallel() + + rawURL, err := url.Parse("/browse?status=airing&order_by=popularity&sort=asc") + if err != nil { + t.Fatalf("url.Parse() error = %v", err) + } + + got, ok := canonicalBrowseURL(rawURL) + if !ok { + t.Fatal("canonicalBrowseURL() should request redirect when sfw is missing") + } + + want := "/browse?order_by=popularity&sfw=true&sort=asc&status=airing" + if got != want { + t.Fatalf("canonicalBrowseURL() = %q, want %q", got, want) + } +} + +func TestCanonicalBrowseURLSkipsWhenSFWAlreadyPresent(t *testing.T) { + t.Parallel() + + rawURL, err := url.Parse("/browse?status=airing&sfw=false") + if err != nil { + t.Fatalf("url.Parse() error = %v", err) + } + + got, ok := canonicalBrowseURL(rawURL) + if ok { + t.Fatalf("canonicalBrowseURL() unexpectedly requested redirect to %q", got) + } +}