Deep-review round 3 of bug hunt 2026-09.
- get_or_404 / owned_or_404 answer a structured {code: "not_found", reason: <what>}
(errors.notFound.* in HU+EN, generic fallback). The structured-404 fix of round 2 had gone into the
ad-hoc `_own_playlist` (CLAUDE.md's "Not" pattern) while the shared helpers stayed string-only, so
views, tags, pins, Plex playlists, invites and users still 404'd in English. `_own_playlist` now
goes through owned_or_404; admin's hand-rolled user lookups use get_or_404.
- A move into a list that is gone names the TARGET (reason "move_target") — "this playlist doesn't
exist any more" was read as the list on screen, which still exists.
- Every refusal in the playlists router is a {code: "playlist", reason} (playlists.errors.* HU+EN)
instead of a bare English string shown verbatim in the Hungarian UI.
- `req` / `uploadReq` mark the errors they already put on screen (the error dialog, the connection
notice, the 401 sign-out), and notifyActionError skips those. The watched toggles, the saved views
and a playlist delete toasted a fault the dialog was already showing; Playlists' two hand-written
status filters now go through the same check.
98 lines
3.0 KiB
TypeScript
98 lines
3.0 KiB
TypeScript
// The API façade. This file owns NO endpoint logic and NO types of its own — every domain lives in
|
|
// its own api/<domain>.ts module (types + method slice together). This barrel only:
|
|
// 1. re-exports the shared HTTP machinery from api/core,
|
|
// 2. re-exports each domain's TYPES (`export type *`) so consumers import them from "./api"
|
|
// unchanged,
|
|
// 3. spreads each domain's method slice into the single `api` object consumers call.
|
|
// A new endpoint therefore has nowhere to land except a domain module — the interleave that
|
|
// motivated R7.5 cannot re-grow here. (An automated guard for that invariant lands in S3.)
|
|
import {
|
|
alreadyReported,
|
|
getActiveAccount,
|
|
HttpError,
|
|
setActiveAccount,
|
|
clearActiveAccount,
|
|
setUnauthorizedHandler,
|
|
} from "./api/core";
|
|
|
|
// Re-export the shared HTTP machinery so consumers keep importing these from "./api" unchanged.
|
|
export {
|
|
alreadyReported,
|
|
HttpError,
|
|
getActiveAccount,
|
|
setActiveAccount,
|
|
clearActiveAccount,
|
|
setUnauthorizedHandler,
|
|
};
|
|
|
|
import { accountApi } from "./api/account";
|
|
export type * from "./api/account";
|
|
import { authApi } from "./api/auth";
|
|
export type * from "./api/auth";
|
|
export { loginBlocked, deadResetLink } from "./api/auth";
|
|
import { setupApi } from "./api/setup";
|
|
export type * from "./api/setup";
|
|
import { feedApi } from "./api/feed";
|
|
export type * from "./api/feed";
|
|
import { discoverApi } from "./api/discover";
|
|
export type * from "./api/discover";
|
|
export { DISCOVER_BUDGETS, discoverCardKey } from "./api/discover";
|
|
export type * from "./api/tagModes";
|
|
export {
|
|
DEFAULT_TAG_MODES,
|
|
TAG_CATEGORIES,
|
|
NO_TAG_SENTINEL,
|
|
coerceTagModes,
|
|
coerceUntagged,
|
|
} from "./api/tagModes";
|
|
import { channelsApi } from "./api/channels";
|
|
export type * from "./api/channels";
|
|
import { syncApi } from "./api/sync";
|
|
export type * from "./api/sync";
|
|
import { playlistsApi } from "./api/playlists";
|
|
export type * from "./api/playlists";
|
|
import { tagsApi } from "./api/tags";
|
|
export type * from "./api/tags";
|
|
import { notificationsApi } from "./api/notifications";
|
|
export type * from "./api/notifications";
|
|
import { savedViewsApi } from "./api/savedViews";
|
|
export type * from "./api/savedViews";
|
|
import { quotaApi } from "./api/quota";
|
|
export type * from "./api/quota";
|
|
import { adminApi } from "./api/admin";
|
|
export type * from "./api/admin";
|
|
import { plexApi } from "./api/plex";
|
|
export type * from "./api/plex";
|
|
export {
|
|
EMPTY_PLEX_FILTERS,
|
|
plexFilterCount,
|
|
coercePlexFilters,
|
|
plexFiltersSignature,
|
|
plexFiltersToParams,
|
|
parsePlexFilters,
|
|
isIntrinsicReadOnly,
|
|
} from "./api/plex";
|
|
import { messagingApi } from "./api/messaging";
|
|
export type * from "./api/messaging";
|
|
import { downloadsApi } from "./api/downloads";
|
|
export type * from "./api/downloads";
|
|
|
|
export const api = {
|
|
...accountApi,
|
|
...authApi,
|
|
...setupApi,
|
|
...feedApi,
|
|
...discoverApi,
|
|
...channelsApi,
|
|
...syncApi,
|
|
...playlistsApi,
|
|
...tagsApi,
|
|
...notificationsApi,
|
|
...savedViewsApi,
|
|
...quotaApi,
|
|
...adminApi,
|
|
...plexApi,
|
|
...messagingApi,
|
|
...downloadsApi,
|
|
};
|