- Service worker (vite-plugin-pwa/Workbox): add /share/, /mcp, and /robots.txt to navigateFallbackDenylist so the SPA app-shell never shadows those server-rendered routes (they mirror the server static-serve exclude list — the share SEO/OG HTML, the MCP endpoint, and robots.txt must come from the server). - Remove the dead /api GET NetworkFirst Workbox rule (api-get-cache): offline reads are served by the persisted TanStack Query cache (IndexedDB) + y-indexeddb, never by an SW HTTP cache, so caching GET /api only risked stale responses. All /api is now NetworkOnly. clearOfflineCache still deletes any legacy api-get-cache defensively (comment updated to note it is no longer created). - CORS: drop the cleartext 'http://localhost' native-WebView origin. The Capacitor shell uses the secure scheme (capacitor.config cleartext:false, default Android scheme https, iOS hosted via CAP_SERVER_URL), so no native client uses it; allowing it only widened the credentialed-CORS surface. Keeps capacitor://, ionic://, and https://localhost. - docs/mobile-bootstrap.md: replace the inaccurate 'hand-rolled service worker' description with the real Workbox generateSW setup (prompt registration via virtual:pwa-register, production-only, denylist, NetworkOnly, RQ/y-indexeddb offline reads) and drop http://localhost from the CORS origins list. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
51 lines
2.1 KiB
TypeScript
51 lines
2.1 KiB
TypeScript
// CORS trust boundary helpers. `buildCorsAllowlist` produces the exact set of
|
|
// origins the API trusts, and `isOriginAllowed` is the predicate the enableCors
|
|
// origin callback uses to accept/reject each request. With credentials:true a
|
|
// foreign credentialed origin must never be allowed, so anything not in the
|
|
// allowlist (apart from no-Origin requests) is rejected.
|
|
|
|
// Native WebView origins used by the Capacitor/Ionic mobile shell. Always
|
|
// trusted so the native client can call the API.
|
|
//
|
|
// - `capacitor://localhost` — iOS native custom scheme.
|
|
// - `ionic://localhost` — legacy native custom scheme.
|
|
// - `https://localhost` — Android default secure scheme.
|
|
//
|
|
// The cleartext `http://localhost` origin is intentionally NOT trusted: the
|
|
// Capacitor shell uses the secure scheme (capacitor.config.ts sets
|
|
// `cleartext: false` and does not override `androidScheme`, so Capacitor's
|
|
// default Android scheme is `https` => origin `https://localhost`), and iOS runs
|
|
// in hosted mode (`server.url` = CAP_SERVER_URL, whose origin is the app URL
|
|
// already in the allowlist). No native client legitimately uses
|
|
// `http://localhost`, so allowing it would only widen the credentialed-CORS
|
|
// surface to arbitrary local http content.
|
|
const NATIVE_WEBVIEW_ORIGINS = [
|
|
'capacitor://localhost',
|
|
'ionic://localhost',
|
|
'https://localhost',
|
|
] as const;
|
|
|
|
// Build the CORS allowlist: the app URL, all configured cross-origin clients,
|
|
// and the native WebView origins. Dedup is automatic via Set.
|
|
export function buildCorsAllowlist(input: {
|
|
appUrl: string;
|
|
configuredOrigins: readonly string[];
|
|
}): Set<string> {
|
|
return new Set<string>([
|
|
input.appUrl,
|
|
...input.configuredOrigins,
|
|
...NATIVE_WEBVIEW_ORIGINS,
|
|
]);
|
|
}
|
|
|
|
// Decide whether a request's Origin is allowed. A missing Origin header (curl,
|
|
// server-to-server, some native WebViews) is allowed; otherwise the origin must
|
|
// be present in the allowlist.
|
|
export function isOriginAllowed(
|
|
origin: string | undefined,
|
|
allowlist: ReadonlySet<string>,
|
|
): boolean {
|
|
if (!origin) return true;
|
|
return allowlist.has(origin);
|
|
}
|