Files
ersatztv/web/src/main.tsx
T
timothyandClaude Opus 4.8 9348a9938a feat(web): #295 PR2 SPA session-auth foundation (boot gate, CSRF header, login/setup/logout)
Replace the API-key SPA model with the session-cookie auth from PR1:

- client.ts: stop sending X-Api-Key; attach X-Csrf on mutating verbs (POST/PUT/
  PATCH/DELETE); add suppressUnauthorizedSignal to skip the global 401 signal on
  expected wrong-credentials 401s (login / change-password).
- api/auth.ts: hand-written wire types (AuthConfig/AuthSession/MachineKey — the
  auth surface is IgnoreApi, deliberately not in generated types) + endpoint fns
  (getAuthConfig/getAuthSession/login/setup/logout/changePassword/getMachineKey);
  keep the notifyUnauthorized/subscribeUnauthorized 401 signal; add
  clearLegacyStoredApiKey. Legacy get/set/clearStoredApiKey retained ONLY so the
  still-shipping ApiKeyScreen (machine-key slice) compiles without a cross-slice
  conflict — the client no longer reads them.
- AuthGate.tsx: boot gate wrapping <App/> outside the shell (mints no URL, deep
  links survive login). checking -> setup | login | ready | error, with a safe
  default AuthContext so App.test.tsx renders without a provider; config-fetch
  failure lands on an explicit error+Retry, never a blank screen.
- LoginScreen / SetupScreen: shell-less centered cards; inline 401 / 409 handling;
  SSO button + local-form gating from AuthConfig.
- UnauthorizedBanner: rewritten to prompt re-login (passive; consults the
  unsaved-changes guard before flipping the gate).
- UserMenu: TopBar sign-out (guard -> logout -> signOut), mounted next to
  ConnectMenu.
- main.tsx: wrap <AuthGate><App/></AuthGate> inside StrictMode.

Tests: client/auth/AuthGate/LoginScreen/SetupScreen/UnauthorizedBanner/UserMenu
(723 pass). Lint + build green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 21:50:24 +02:00

15 lines
331 B
TypeScript

import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import { App } from './App';
import { AuthGate } from './AuthGate';
import './designSystem';
import './shell.css';
createRoot(document.getElementById('root')!).render(
<StrictMode>
<AuthGate>
<App />
</AuthGate>
</StrictMode>
);