From 9cf5062bf06a13c4e6178cdf544b345a1822f062 Mon Sep 17 00:00:00 2001 From: Silo Server Migration Date: Mon, 25 May 2026 01:12:47 -0400 Subject: [PATCH] chore: restore tmdb project api key fallback and move requests nav - tmdb client falls back to bundled project API key when none configured - move Requests link next to Recommendations in the sidebar --- internal/metadata/tmdb/client.go | 21 +++++++---------- internal/metadata/tmdb/client_test.go | 19 +++++++++++---- web/src/components/AppSidebar.tsx | 34 +++++++++++++-------------- 3 files changed, 39 insertions(+), 35 deletions(-) diff --git a/internal/metadata/tmdb/client.go b/internal/metadata/tmdb/client.go index 89ef8a6f..e0508a25 100644 --- a/internal/metadata/tmdb/client.go +++ b/internal/metadata/tmdb/client.go @@ -17,16 +17,12 @@ import ( const ( defaultBaseURL = "https://api.themoviedb.org/3" + projectAPIKey = "4ef0d7355d9ffb5151e987764708ce96" maxRetries = 3 maxResponseBody = 1 << 20 // 1 MB maxCollectionPresetResults = 500 ) -// ErrMissingAPIKey is returned by client methods when the client was -// constructed without an API key. Callers should source the key from -// configuration rather than relying on a built-in default. -var ErrMissingAPIKey = fmt.Errorf("tmdb: api key is required") - // Client is an HTTP client for the TMDB collection preset API surface. type Client struct { httpClient *http.Client @@ -36,14 +32,16 @@ type Client struct { } // NewClient creates a TMDB API client with the given API key and rate limit -// (requests per second). The apiKey must be sourced from configuration; a -// client constructed with an empty key returns ErrMissingAPIKey from every -// API call so misconfiguration surfaces immediately rather than silently -// using a shared built-in key. +// (requests per second). If apiKey is empty, Silo's public project API key is +// used. func NewClient(apiKey string, rateLimit int) *Client { + apiKey = strings.TrimSpace(apiKey) + if apiKey == "" { + apiKey = projectAPIKey + } return &Client{ httpClient: &http.Client{Timeout: 30 * time.Second}, - apiKey: strings.TrimSpace(apiKey), + apiKey: apiKey, baseURL: defaultBaseURL, limiter: rate.NewLimiter(rate.Limit(rateLimit), rateLimit), } @@ -57,9 +55,6 @@ func (c *Client) SetBaseURL(url string) { // doGet executes a GET request against the TMDB API with rate limiting, // exponential backoff on 5xx/429, and JSON decoding into dest. func (c *Client) doGet(ctx context.Context, path string, dest any) error { - if c.apiKey == "" { - return ErrMissingAPIKey - } if err := c.limiter.Wait(ctx); err != nil { return err } diff --git a/internal/metadata/tmdb/client_test.go b/internal/metadata/tmdb/client_test.go index 288dbc9d..4cfbb864 100644 --- a/internal/metadata/tmdb/client_test.go +++ b/internal/metadata/tmdb/client_test.go @@ -2,17 +2,26 @@ package tmdb import ( "context" - "errors" "net/http" "net/http/httptest" "testing" ) -func TestNewClientRequiresAPIKey(t *testing.T) { +func TestNewClientUsesProjectAPIKeyWhenEmpty(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if got := r.URL.Query().Get("api_key"); got != projectAPIKey { + t.Fatalf("api_key query = %q, want project API key", got) + } + w.Header().Set("Content-Type", "application/json") + _, _ = w.Write([]byte(`{"page":1,"total_pages":1,"total_results":0,"results":[]}`)) + })) + defer server.Close() + client := NewClient("", 1000) - _, err := client.GetCollectionPreset(context.Background(), "trending", "all", "day", 10) - if !errors.Is(err, ErrMissingAPIKey) { - t.Fatalf("err = %v, want ErrMissingAPIKey", err) + client.SetBaseURL(server.URL) + + if _, err := client.GetCollectionPreset(context.Background(), "trending", "all", "day", 10); err != nil { + t.Fatalf("GetCollectionPreset returned error: %v", err) } } diff --git a/web/src/components/AppSidebar.tsx b/web/src/components/AppSidebar.tsx index 928cab98..59c4ab13 100644 --- a/web/src/components/AppSidebar.tsx +++ b/web/src/components/AppSidebar.tsx @@ -495,6 +495,23 @@ export default function AppSidebar({ onNavigate, collapsed = false }: AppSidebar Recommendations +
  • + + {isActive("/requests") && ( + + )} + + Requests + +
  • Collections
  • -
  • - - {isActive("/requests") && ( - - )} - - Requests - -