Carries the still-applicable findings from the PR #116 review into PR #120, since #120 includes the mobile-bootstrap commit. CORS hardening (removing the unconditional localhost/capacitor origins) is intentionally left out of scope. Service worker routing (latent bug fix + testability): - vite.config.ts: anchor Workbox path matching to a segment boundary (^/<seg>(/|$)) instead of startsWith, so siblings like /apidocs, /collaborators, /socket.iox are no longer mis-routed as API/realtime and forced NetworkOnly; align navigateFallbackDenylist with the same anchors. - new apps/client/src/pwa/sw-strategy.ts holds the canonical predicates (isApiPath, isCollabOrSocketPath) + unit tests; the vite.config regexes mirror it inline (Workbox generateSW serializes urlPattern fns standalone, so they cannot import the module). Server CORS (R1 extraction + coverage): - extract buildCorsAllowlist / isOriginAllowed into cors.util.ts with unit tests (evil-origin rejected, WebView/no-Origin allowed); main.ts rewired to use them with byte-for-byte identical behavior. Privacy — clear offline cache on logout: - new clear-offline-cache.ts purges the persisted query cache (idb-keyval gitmost-rq-cache), the Yjs page.* IndexedDB databases, and the service-worker api-get-cache; wired into handleLogout (best-effort, before the redirect) so a previous user's private data does not linger locally. Conventions & docs: - prettier fixes on main.ts and login.dto.ts. - CHANGELOG: document offline reading, returnToken opt-in, optional Swagger, new env vars, logout cache-clear, and the CORS open->allowlist breaking change. - docs/mobile-app-plan.md: correct the now-false §2.4 claims and update the §12 checklist (native cap add ios left unchecked — generated locally, gitignored). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
40 lines
1.4 KiB
TypeScript
40 lines
1.4 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. CORS hardening of these is
|
|
// intentionally out of scope.
|
|
const NATIVE_WEBVIEW_ORIGINS = [
|
|
'capacitor://localhost',
|
|
'ionic://localhost',
|
|
'http://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);
|
|
}
|