Replace the static `https://sofa.example.com/api/v1` server entry with a variable-based URL (`{protocol}://{host}:{port}/api/v1`) so the spec works against any instance out of the box. Also move the output path from `docs/openapi.json` to `docs/public/openapi.json` so the file is served as a static asset by the docs site.
Switch `mock.module("../src/image-cache", ...)` stubs in thumbhash, person-detail, colors, and credits tests to `spyOn(globalThis, "fetch")` instead. This avoids the cross-file persistence problem with `mock.module` and tests the real code paths more faithfully.
Update `image-cache.ts` to call `globalThis.fetch` so the spy intercepts it, add `mock.restore()` to the global `afterEach` in `preload.ts`, and set `IMAGE_CACHE_ENABLED=false` in affected tests to avoid disk writes during test runs.
Replace the standalone `ChangePasswordSection` card with a `ChangePasswordDialog` triggered from a button next to "Sign out" in the account section. This removes the separate card, merges the dialog logic into `account-section.tsx`, and cleans up the settings page layout accordingly.
Replace the single MDX file per API tag (e.g. `admin.mdx`, `titles.mdx`) with individual files for each procedure (e.g. `admin/admin.backups.create.mdx`). Each file renders a single `<APIPage>` with one operation, includes an inline description, and carries its own `_openapi` frontmatter.
Update `generate-api-docs.ts` to emit this per-procedure structure and adjust `source.ts` and `next.config.mjs` to resolve the new paths correctly.
Add `purgeMetadataCache` and `purgeImageCache` procedures that let admins free disk space on demand. Metadata purge removes un-enriched stub titles not in any user's library plus orphaned person records; image purge deletes all cached TMDB files from disk.
Expose both as POST endpoints under `/admin/cache/` via the oRPC contract, implement the core logic in a new `@sofa/core/cache` module, and surface them in a new "Danger Zone" section on the Settings page with individual confirmation dialogs and a combined "Purge all" action.
Extract spec generation into `openapi-spec.ts` with a `generateOpenApiSpec` function that registers common `$ref` schemas (Title, Person, Episode, Season, etc.) to deduplicate inline definitions across the spec. Add a `/api/v1/spec.json` interceptor that returns the fully normalized document, and point the Scalar docs UI at it instead of the plugin's raw generator output.
Also strip oRPC's impossible `{ not: {} }` void placeholders from request/response bodies via `normalizeOpenApiSpec`, and fix the handler registration so both `/api/v1` and `/api/v1/*` routes are matched.
Seed the Better Auth session atom from SecureStore cache before React
renders so the app shows cached data instantly instead of hanging on the
splash screen for 30-60s when the server is down. Add a server
reachability monitor that probes /api/health and displays an amber
"Can't reach server" banner (distinct from the offline banner). When the
server recovers, the session is re-validated automatically — if expired,
a toast explains the sign-out.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Generate an OpenAPI 3.1 spec from the oRPC contract at build time
using @orpc/openapi, then use fumadocs-openapi to render interactive
API reference pages grouped by tag (Titles, Episodes, Seasons, etc.).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Swap the generic `fumadocs-ui/og/takumi` default for a hand-crafted OG image using the Sofa cinema theme — dark background, amber accent, DM Serif Display title, DM Sans body, and a radial gradient — matching the app's visual identity.
- Load DM Sans (400/500) and DM Serif Display (400) at runtime from Google Fonts via old Safari UA to get TrueType URLs
- Render header with Sofa logo SVG + "Sofa Docs" label, centered title/description block, and an amber accent bar
- Scale title font size based on length (68px → 56px for longer strings)
- Define theme constants (BG, FG, PRIMARY, MUTED) from the app's oklch palette
- Add `docs/` — Next.js 15 app powered by Fumadocs with a landing page, docs layout, search, OG image generation, and LLM text routes
- Cover getting started, configuration, integrations (Plex, Emby, Jellyfin, Radarr, Sonarr), mobile app setup, and telemetry in MDX
- Exclude `docs/` from root Biome config since it has its own `biome.json`
Isolate the query cache and recently-viewed history by instance ID +
user ID so that switching servers or accounts never bleeds data
between them.
- Split `mmkv.ts` into `globalStorage` (app-wide settings, analytics)
and a scoped store keyed by `${instanceId}_${userId}`; add
`setStorageScope`, `clearStorageScope`, `onStorageScopeChange`
- Add `QueryProvider` component — mounts `PersistQueryClientProvider`
keyed by `${instanceId}_${userId}` when scoped storage is ready,
falls back to plain `QueryClientProvider` otherwise; remounts on
scope change to restore from the correct MMKV partition
- `registerServer` in `server-url.ts` persists the instance ID
returned from the health check; `ensureInstanceId` fetches it from
the server for upgrade paths and env-based URLs
- Auth client `storagePrefix` now includes the instance ID so
SecureStore tokens are namespaced per server
- Export `rebuildAuthClient` for use when instance ID resolves after
initial mount
- Remove explicit `clearRecentlyViewed` calls from sign-out — scoped
storage naturally separates history per account
- Update `recently-viewed.ts` and `posthog.ts` to use the appropriate
storage tier (`scopedStorage` / `globalStorage`)
Add an opt-in telemetry system that sends anonymised instance
statistics to the public API once per day, which proxies them to
PostHog when `POSTHOG_API_KEY` is configured.
- Add `packages/core/src/telemetry.ts` — `performTelemetryReport`
and `isTelemetryEnabled`; user and title counts are bucketed
before sending to avoid exposing exact figures
- Add `getInstanceId()` to `settings.ts` — generates and persists a
stable UUIDv7 for the instance
- Schedule a daily `telemetryReport` cron job (00:30)
- Add `POST /v1/telemetry` to `apps/public-api`; forwards payload
to PostHog or returns 204 silently if key is absent
- Add `admin.telemetry` and `admin.toggleTelemetry` oRPC procedures
for inspecting and toggling the setting
- Expose `instanceId` on `system.publicInfo`
Add a change password feature using Better Auth's built-in
changePassword endpoint. On web, a dialog opens from a new card in the
Account section. On mobile, a dedicated screen is pushed from a new
SettingsRow. Both use listAccounts to detect credential-based accounts
and only show the option when the user has a password and password login
is enabled (respecting OIDC-only configurations).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add `apps/public-api` (`@sofa/public-api`) — minimal Hono app
deployable to Vercel; `GET /v1/version` fetches the latest GitHub
release and returns `{ version, releaseUrl }` with a 15-minute
CDN cache (`s-maxage=900, stale-while-revalidate=3600`)
- Update `packages/core/src/update-check.ts` to call
`PUBLIC_API_URL/v1/version` instead of the GitHub API directly;
`PUBLIC_API_URL` defaults to `https://public-api.sofa.watch`
Swap the hand-rolled `ToastView`/`ToastProvider`/queue system for the
`burnt` library, which delegates to native iOS/Android toast APIs.
- Add `burnt@0.13.0` dependency
- Delete `toast.tsx` and `toast-provider.tsx`; remove `<ToastProvider />`
from `_layout.tsx`
- Rewrite `lib/toast.ts` to wrap `burnt` — maps success/error/info/warning
to burnt presets and haptic types; duration converted from ms to seconds
Track titles and people visited in the native app and surface them
in the search tab when the query is empty, replacing the static
empty-state icon.
- Add `lib/recently-viewed.ts` — MMKV-backed store (max 20 items)
with `addRecentlyViewed`, `removeItem`, `clearAll`, and a
`useRecentlyViewed` hook
- Track visits via `useEffect` in `title/[id].tsx` and
`person/[id].tsx` once data resolves
- `RecentlyViewedList` — platform-split component:
- iOS (`.ios.tsx`): native SwiftUI List via `@expo/ui` with
system SF Symbols, swipe-to-delete, and a "Clear" button
- Android/fallback: FlatList with `SwipeableRow` for delete
- Add `SwipeableRow` component using `ReanimatedSwipeable`
- Clear recently viewed history on sign-out (both `HeaderAvatar`
and Settings screen)
- Add `@expo/ui@55.0.2` dependency
- Swap `runOnJS` for `scheduleOnRN` from `react-native-worklets` in
gesture handler `.onEnd()` callbacks (HeroBanner, PosterCard)
- Delete custom `timeAgo` util; use `date-fns` `formatDistanceToNow`
in integration status strings
- Enhance person detail screen: formatted birth date with age/death
calculation, place of birth with MapPin icon, department label
mapping, CalendarIcon for dates
- Fix `ExpandableText` truncation detection by measuring line count
on a hidden full-text node rather than the clamped one; add
`actionColor` prop so title accent color can theme the toggle
- Polish PosterCard metadata row: remove icon opacity, tighten gaps
Replace three separate status toggle buttons (Watchlist/Watching/Completed)
with a single status button matching the web pattern: "+ Watchlist" when
inactive, "Watching"/"Completed" when active (tap to remove). Consolidate
actions into a single row with separator and star rating. Use title-accent
color for theming consistency with other native components.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Introduced HeroBanner component for displaying movie or TV show details with animated press effects and navigation.
- Added OfflineBanner component to notify users of network connectivity issues, utilizing glass effect when available.
- Implemented haptic feedback utilities for Android to enhance user interactions.
Replace the conditional GlassView backdrop overlay with plain tinted
Views, and apply the liquid glass effect to the play button circle
instead when supported.
Replace hardcoded `--color-primary` references on the title detail screen
with a dedicated `--color-title-accent` / `--color-title-accent-foreground`
variable pair, allowing the poster-derived palette to style title UI
elements without polluting the global primary color.
- Add `--color-title-accent` and `--color-title-accent-foreground` to
`global.css` with default warm amber values
- Update `use-title-theme.ts` to set `--color-title-accent` instead of
`--color-primary`; switch from `useFocusEffect` to `useEffect`
- Replace all `bg-primary`, `text-primary`, `border-primary` etc. in
`title/[id].tsx` and title components with `title-accent` equivalents
- Add `accentColor` prop to `StarRating` so it can receive the resolved
CSS variable value at runtime
- Pass `iconColor={titleAccent}` to all `SectionHeader` instances on the
title detail screen
- Use platform-appropriate store icon (App Store / Google Play) for the
"Where to Watch" section header
- Switch progress bar in `ContinueWatchingBanner` from `bg-status-watching`
to `bg-title-accent`
* Convert to Turborepo monorepo with shared API contract package
Restructure the repository as a monorepo in preparation for adding
future clients (mobile app, CLI). Extract the oRPC contract and Zod
schemas into `@sofa/api` (packages/api/) as a JIT internal package,
and relocate the Next.js app to `@sofa/web` (apps/web/).
- Add Turborepo with Bun workspaces for task orchestration and caching
- Extract `contract.ts` and `schemas.ts` into `@sofa/api` package
- Move all app code, configs, tests, and migrations to `apps/web/`
- Update 17 import paths from `@/lib/orpc/schemas` to `@sofa/api/schemas`
- Add `outputFileTracingRoot` and `transpilePackages` to next.config.ts
- Rewrite Dockerfile with `turbo prune --docker` for efficient builds
- Update CI workflows to use `turbo run` for lint/check-types/test
- Update CLAUDE.md with monorepo structure and commands
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Extract standalone Hono API server and split shared packages
Separate all server-side concerns from the Next.js frontend into a new
`apps/server/` Hono app and dedicated shared packages, making `@sofa/web`
a frontend-only app with no direct DB or service access.
- Add `@sofa/server` (`apps/server/`) — Hono API on port 3001 hosting
oRPC procedures, Better Auth, cron jobs, and non-RPC routes
- Add `@sofa/core` (`packages/core/`) — All 15 business logic services
moved from `apps/web/lib/services/`; tests moved to `packages/core/test/`
- Add `@sofa/db` (`packages/db/`) — DB client, schema, migrations,
constants, and logger extracted from `apps/web/lib/db/` and `lib/`
- Add `@sofa/tmdb` (`packages/tmdb/`) — TMDB client and image helpers
moved from `apps/web/lib/tmdb/`
- Add `@sofa/auth` (`packages/auth/`) — Better Auth server config moved
from `apps/web/lib/auth/`
- Move oRPC procedures, handler, router, middleware to `apps/server/src/orpc/`
- Move Hono route handlers (avatars, backups, images, lists, webhooks,
health) to `apps/server/src/routes/`; delete equivalent Next.js API routes
- Strip `apps/web` to frontend-only: no DB imports, no service imports,
all data via oRPC client calls to the API server
- Add `entrypoint.sh` to start API server, wait for health, then Next.js
- Update `next.config.ts` rewrites to proxy `/rpc/*` and `/api/*` to
`INTERNAL_API_URL` (default `http://localhost:3001`)
- Update Dockerfile and CLAUDE.md for the new structure
* Migrate web app from Next.js to Vite + TanStack Router SPA and add workspace catalog
Replace Next.js with a pure Vite SPA using TanStack Router for file-based routing,
removing all SSR complexity. The API server (Hono) now serves both API routes and
SPA static files in production, simplifying Docker to a single-process container.
Key changes:
- Vite 7 + @tanstack/react-router with file-based routing via plugin
- Route guards via beforeLoad + authClient.getSession() (replaces server-side auth)
- Route loaders with queryClient.ensureQueryData() (replaces SSR data fetching)
- Self-hosted fonts via @fontsource (replaces next/font/google)
- Tailwind v4 via @tailwindcss/vite (replaces @tailwindcss/postcss)
- Single oRPC client (removed SSR client and server-side session helper)
- Hono serves SPA static files in production (single port 3000)
- Single-process Dockerfile (removed entrypoint.sh)
- Bun workspace catalog for centralized dependency version management
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Extract @sofa/logger and @sofa/config shared packages
- Add `@sofa/logger` (`packages/logger/`) — standalone logger package
extracted from `@sofa/db/logger`; update all imports across server,
core, auth, db, and tmdb packages
- Add `@sofa/config` (`packages/config/`) — standalone config/constants
package extracted from `@sofa/db/constants`; exports `DATA_DIR`,
`DATABASE_URL`, `CACHE_DIR`, `AVATAR_DIR`, `BACKUP_DIR`
- Move `.env.example` from `apps/web/` to repo root; update server dev
scripts to load it via `--env-file=../../.env`
- Move image serving from `/api/images` to `/images`; add `serveStatic`
fast path in `index.ts` for cached files before falling back to the
TMDB fetch route; add `/images` proxy to Vite dev config
- Fix `Sparkline` component: replace `ResponsiveContainer` with
`ResizeObserver` to avoid SSR/hydration issues with recharts
- Replace `VITE_SERVER_URL` env var with `window.location.origin` in
the oRPC client (always same-origin in both dev and production)
* Fix asset caching, SPA 404 fallback, and DATA_DIR resolution
- Add `Cache-Control: immutable` header for hashed `/assets/*` files;
return 404 for missing asset paths instead of falling back to
`index.html` (prevents serving stale chunks after deploy)
- Wrap `query.invalidate` in an arrow function in the oRPC QueryClient
error handler to avoid illegal invocation errors
- Resolve `DATA_DIR` to an absolute path via `path.resolve()` so
relative paths work regardless of the process working directory
* Migrate @sofa/logger to pino for structured logging
- Replace custom logger implementation in `packages/logger/` with pino
+ pino-pretty; add both as workspace catalog dependencies
- Add `pino` and `pino-pretty` to the workspace catalog in `package.json`
- Fix `log.error()` calls in oRPC and OpenAPI handlers to pass the
error directly instead of wrapping it in `{ error }` to match pino's
serializer expectations
* Rename discoverProcedure/statsProcedure exports to discover/stats
* Add TanStackDevtools unified panel and VS Code workspace config
- Replace separate Router/Query devtools with unified `TanStackDevtools`
from `@tanstack/react-devtools` + `@tanstack/devtools-vite` plugin
- Wrap app in `<StrictMode>` in `main.tsx`
- Add `.vscode/settings.json` (Biome formatter, format-on-save, readonly
`routeTree.gen.ts`) and `.vscode/extensions.json` (recommended extensions)
* Move test DB helpers to @sofa/db/test-utils and add root bunfig.toml
Extract in-memory SQLite setup and fixture helpers (insertUser, insertTitle,
etc.) from packages/core/test/sqlite.ts into packages/db/src/test-utils.ts
so DB test utilities live alongside the schema they depend on. Use
import.meta.dir for CWD-independent migration path resolution.
Add root bunfig.toml so `bun test` works from the repo root in addition
to `bun run test` (turbo).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Fix devtools plugin order and whitespace-only TMDB token check
Move devtools() to first position in Vite plugins array per TanStack
docs, and trim TMDB_API_READ_ACCESS_TOKEN before boolean coercion so
whitespace-only values are treated as unconfigured.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* Replace server actions with API routes and migrate SWR to TanStack Query
- Add 33 new REST API routes covering titles, episodes, seasons, people,
dashboard, explore, integrations, admin, backups, and account operations
- Replace SWR with @tanstack/react-query: add QueryProvider, api-client
helper, query-client singleton, and TanStack Query hooks for titles
- Migrate all 26 server actions to fetch calls against the new API routes
in 10 client components (use-title-actions, title-card, command-palette,
hero-banner, integration-card, account/backup/registration/update-check
settings sections)
- Delete lib/actions/ directory (titles.ts, watchlist.ts, people.ts,
settings.ts) and lib/swr/fetcher.ts
https://claude.ai/code/session_01L6gF2sqqe6gw5bemZUnRBb
* Convert pages to TanStack Query client-side data fetching
Migrate dashboard, explore, settings, and people pages from server
component data fetching to client-side TanStack Query hooks. Create
query hook files for each domain (dashboard, explore, people, admin,
integrations). Add GET routes for admin/registration and admin/update-check.
Update CLAUDE.md architecture docs to reflect TanStack Query patterns.
https://claude.ai/code/session_01L6gF2sqqe6gw5bemZUnRBb
* Migrate API layer from REST routes to oRPC with TanStack Query
Replace ~35 hand-written REST API routes and manual TanStack Query hooks
with a contract-first oRPC setup that provides end-to-end type safety
from contract → server procedures → client → TanStack Query.
- Add oRPC foundation: contract, context, auth/admin middleware, RPCHandler,
client (browser + SSR via globalThis), TanStack Query utils
- Implement ~30 procedures across 14 files (titles, episodes, seasons,
people, dashboard, explore, search, discover, stats, status,
integrations, admin, account, watchlist)
- Migrate all client components from api()/lib/queries/ to
orpc.*.queryOptions() and client.*.method() calls
- Delete all replaced REST API routes, lib/queries/, lib/api-client.ts,
and old hooks (use-discover, use-search, use-stats, use-system-health)
- Wire up SSR optimization via instrumentation.ts
- Keep non-RPC routes: auth, images, avatars, backup restore/download,
webhooks, lists, health
https://claude.ai/code/session_01L6gF2sqqe6gw5bemZUnRBb
* Add strict Zod output schemas to all oRPC contract procedures
Replaces the permissive z.any() output schemas with precise Zod schemas
for every procedure that returns data. This makes the contract fully
self-documenting and enables runtime output validation.
- Define ~30 output schemas in schemas.ts covering titles, people,
dashboard, explore, search, discover, stats, integrations, admin, etc.
- Wire all output schemas into contract.ts
- Fix procedure return type mismatches (nullable fields, enum narrowing)
- Align consumer types (SearchResult, IntegrationConnection) with contract
https://claude.ai/code/session_01L6gF2sqqe6gw5bemZUnRBb
* Delete lib/types.ts — infer all types from Zod schemas
The oRPC Zod schemas are now the single source of truth for domain types.
All 17 consumer files (services, components, atoms, utils) now import
inferred types (Episode, Season, ResolvedTitle, CastMember, etc.) from
lib/orpc/schemas instead of hand-written interfaces in lib/types.ts.
https://claude.ai/code/session_01L6gF2sqqe6gw5bemZUnRBb
* Migrate all mutations to TanStack Query useMutation
Replace raw oRPC client calls with useMutation/mutateAsync via
orpc.*.mutationOptions() for consistent mutation tracking across
all components: title-card, command-palette, account, system-health,
integrations, backups, backup-schedule, and title-actions.
https://claude.ai/code/session_01L6gF2sqqe6gw5bemZUnRBb
* Fix bugs in settings/explore and bump dependencies
- Deduplicate backup list on create (filter by filename before prepend)
- Add isError state to backup-schedule section with fallback UI
- Add error check for avatar DELETE response before clearing state
- Strengthen backup restore file guard with `instanceof File`
- Change explore trending default type from "movie" to "all"
- Pin all oRPC and TanStack Query packages to exact versions
- Bump jotai 2.18.0→2.18.1, motion 12.35.1→12.35.2,
shadcn 4.0.0→4.0.2, @types/node 25.3.5→25.4.0,
semver 6.3.1→7.7.4
* Migrate title recommendations to client-side TanStack Query
- Convert TitleRecommendations from server component with `"use cache"`
to a client component using `orpc.titles.recommendations.queryOptions()`
with an inline skeleton loading state
- Delete recommendations-grid.tsx — merged into title-recommendations.tsx
- Remove RecommendationsSkeleton from skeletons.tsx (now inline)
- Drop Suspense wrapper in title detail page (client component handles
its own loading state)
- Include userStatuses in recommendations procedure output so the
component no longer needs a separate session/tracking lookup
- Remove `revalidate` option and all `updateTag`/`cacheTag`/`cacheLife`
calls from refreshCredits and refreshRecommendations — no longer
needed without "use cache" server components
- Remove `cacheComponents: true` from next.config.ts
* Replace Jotai title atoms with React Context + TanStack Query cache
- Add title-context.tsx with TitleContext and useTitleContext /
useTitleUserInfo hooks; useTitleUserInfo reads from
orpc.titles.userInfo cache instead of atoms
- Rewrite TitleProvider to seed the userInfo cache via
queryClient.setQueryData on mount (unconditional overwrite
prevents stale data on revisit / account switch) and provide
titleId, titleType, titleName, seasons, and watchingEp via
context
- Rewrite use-title-actions to read/write userInfo via
queryClient.getQueryData / setQueryData helpers instead of
useStore + individual atoms; optimistic updates and rollbacks
now operate on a single UserInfo object in the query cache
- Migrate title-actions, title-keyboard-shortcuts, and
title-seasons off useAtomValue / useSetAtom to the new
context hooks
- Delete per-title atoms from lib/atoms/title.ts
* Migrate avatar and backup restore routes to oRPC procedures
- Add `account.uploadAvatar` and `account.removeAvatar` procedures
(FormData file input, auth middleware) replacing `PUT`/`DELETE`
`/api/account/avatar` route
- Add `admin.backups.restore` procedure (File input, admin middleware)
replacing `POST /api/admin/backups/restore` route
- Delete both REST route files
- Migrate account-section and backup-restore-section off
`useTransition` + raw `fetch` to `useMutation` via
`orpc.*.mutationOptions()`
- Rename `lib/utils/title-theme.ts` → `lib/theme.ts` and
`getTitleThemeStyle` → `getThemeCssProperties`; update title
detail page import accordingly
* Add OpenAPI v1 REST API endpoint via @orpc/openapi
- Add `@orpc/openapi` and `@orpc/json-schema` dependencies
- Add `lib/orpc/openapi-handler.ts` with OpenAPIHandler instance
- Add `app/api/v1/[[...rest]]/route.ts` catch-all serving GET/POST/PUT/DELETE
at `/api/v1` with request-headers context
- Annotate contract procedures with OpenAPI metadata (tags, summaries,
descriptions, and successStatus codes) across all ~30 procedures
* Fix 8 bugs: memory safety, data races, and correctness issues
- Move queryClient.setQueryData from useState initializer to useEffect
to avoid mutating the query cache during React render phase
- Add SQL LIMIT to per-integration event queries instead of loading all
events and trimming in memory
- Lower backup restore upload limit from 500 MB to 100 MB and stream
upload to disk instead of buffering entirely in memory
- Reorder avatar upload to write new file before deleting old one so a
failed upload doesn't remove the current avatar
- Make optimistic rollback field-specific so a failed rating update
doesn't erase concurrent status or episode-watch changes
- Gate titles.userInfo query behind session state to prevent failing
RPC calls for anonymous users
- Derive OpenAPI session cookie name from BETTER_AUTH_URL so HTTPS
deployments use the correct __Secure- prefixed cookie
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Add explicit z.void() output schemas to all 17 void procedures
Ensures the OpenAPI spec correctly generates 204 No Content responses
for mutation endpoints that don't return data.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
updateTag (Next.js cache revalidation) only works inside Route Handlers
and Server Actions. It was being called from cron jobs and fire-and-forget
promises, causing intermittent errors attributed to whichever route was
rendering concurrently (e.g. /explore).
- Pass `{ revalidate: false }` from cron and fire-and-forget contexts
- Add try-catch around updateTag calls as a safety net
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Drop `authRoutes` set and both redirect branches so the proxy no
longer redirects logged-in users away from auth pages or
unauthenticated users away from protected pages
- Convert discover, stats, status, and system-health server actions to
proper API route handlers under `app/api/`; delete `lib/actions/explore.ts`,
`lib/actions/settings.ts`, and `lib/actions/setup.ts`
- Add `use-discover`, `use-stats`, and `use-system-health` SWR hooks
that call the new routes; update `command-palette`, `title-card`,
`update-toast`, and `stats-display` to consume them
- Lift auth centering wrapper from individual login/register pages into
`(auth)/layout.tsx`; switch both pages from `auth.api.getSession` to
the cached `getSession()` helper
- Relocate setup wizard from `app/(auth)/setup/` to `app/setup/` (outside
auth group) with dedicated `copy-button` and `refresh-button` client
components
- Move `not-found.tsx` and `error.tsx` to app root so they apply
globally instead of only within the pages route group
- Update the success toast message to include a count of unwatched episodes from previous seasons when marking a season as watched.
- Introduce a catch-up action in the toast for users to easily access unwatched episodes.
- Refactor the logic to gather unwatched episodes from earlier seasons for improved user feedback.
- Set `modal={false}` on all Select and DropdownMenu instances to
prevent scroll lock and backdrop interference when opened inside
scrollable or fixed-position containers
- Reduce cast carousel card gap from `gap-4` to `gap-1` and switch
actor name from `line-clamp-2` to `truncate` for a tighter layout
- Fix backup schedule description layout: move `suppressHydrationWarning`
to wrapper span, add margin to inline SelectTrigger, use `!h-auto`
to override default height
- Add `nuqs` dependency; define `dashboardSearchParams` and
`exploreSearchParams` loaders in new `search-params.ts` files
- Thread `moviePeriod`/`episodePeriod` from URL into `StatsSection`
and `StatsDisplay`, replacing local `useState` with `useQueryStates`
- Rename `getUserStats` fields `moviesThisMonth`/`episodesThisWeek` →
`movieCount`/`episodeCount` and accept period args so SSR stats
match the active URL params
- Pre-fetch genre-filtered results server-side on the explore page
when `movieGenre`/`tvGenre` params are present; pass as
`initialGenreItems` to `FilterableTitleRow` so first paint is
already populated
- Render genre chips as `<Link>` elements (with click-handler
fallback) so genre selections produce shareable, bookmarkable URLs