The frontend half of the calendar auth cutover (step 4 of docs/calendar-auth-migration.md in the API repo). This app now shares one identity with the tickets, feedback and admin apps. Every call carries the session cookie via withCredentials rather than appending sessionId/sessionKey to the URL, so there is no credential left in api.service.ts at all - that was DEFERRED_SECURITY.md item 1. The login and registration forms are gone. Accounts exist only by invitation from the admin app, so both were one redirect; sign-out ends the session for all four apps and returns here, so doing it by accident costs one click. 401 and 403 are deliberately not collapsed. Only 401 goes to the login page: redirecting on 403 produces a loop where signing in succeeds and lands straight back on the refusal, and doing it for an unreachable API produces the same loop with no way out. Both of those now render a message instead. src/app/models/session.ts and the unrouted LoginComponent are dead but left in place; removing files is a separate decision. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
3.9 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Commands
npm start # Dev server at http://localhost:4200/ (live reload)
npm run build # Production build
npm run watch # Build with watch mode (development config)
npm test # Run unit tests (Jasmine/Karma in Chrome)
No linter is configured in this project.
Architecture
Angular 18 single-page app for managing calendar events for the "Nachklang" organization. Uses Angular Material for UI, RxJS for async data, and reactive forms. No NgRx — state lives in component local variables.
Environments (src/environments/): apiUrl and adminAppUrl.
- Dev:
http://localhost:3000(expects backend running locally) /http://localhost:3002 - Prod:
https://api.nachklang.art/https://admin.nachklang.art
Authentication
This app has no login form and no accounts of its own. Since the auth cutover
(docs/calendar-auth-migration.md in the API repo) it shares one identity with the tickets,
feedback and admin apps: accounts live in the admin app, and the session is an httpOnly
cookie on .nachklang.art that the API's host sets. Consequences that cannot be designed
around:
withCredentials: trueis mandatory on every call (api.service.tssets it once). Without it the browser sends no cookie and the API answers 401.- This app can never read or verify the session. It calls
GET /admin/meand believes the answer; the API'srequireAppAccess('calendar')is the actual gate. - 401 and 403 mean different things and must not be collapsed. 401 means "nobody is signed
in" and is the only one worth redirecting to the login page - redirecting on 403 produces a
loop where signing in succeeds and lands straight back on the refusal. See
failureinadmin.component.ts. - Sign-out ends the session for all four apps; there is only one.
The dev server must be reachable at a port the API trusts. ng serve defaults to 4200, which
is in better-auth's dev localhostOrigins and in the admin app's
NEXT_PUBLIC_ALLOWED_REDIRECT_ORIGINS; another port fails sign-out and the return redirect,
not the sign-in.
The public calendar stays anonymous. GET /calendar/events/public/json needs no session
at all, because nachklang.art reads it to show the next upcoming event.
Routing (app.routing.ts):
/→LandingpageComponent/admin→AdminComponent(main calendar management page)**→NotfoundComponent
Service layer (src/app/services/):
api.service.ts— all HTTP calls to the backend REST API; carries the session cookie viawithCredentials, holds no credential itselfadmin-auth.service.ts— where signing in happens: the admin app's login URL (with a?redirect=back here) and sign-oututils.service.ts— caches the signed-in user's display name for unsaved draft rows. Cosmetic only; the server takes the author from the session
Data models (src/app/models/): Event, User, Session
Admin page (src/app/pages/admin/) is the core of the app. It owns events state and passes it down to EventsTableComponent via @Input(). Key features: multi-calendar support (public, members, choir, management, birthdays), event filtering (future/past/all), sorting, and an event-move dialog (EventMovePopupComponent via Angular Material Dialog).
Calendar-specific behavior: Birthday calendar auto-sets recurrence to YEARLY. Events have a status field (DRAFT / DELETED).
Session lifecycle: AdminComponent calls me() on init. 401 redirects to the admin app's
login carrying this URL as the return target; 403 (or a signed-in account without the
calendar permission) shows a refusal with Reload/Sign out; anything else shows "cannot be
reached". None of those three redirect, on purpose.
Dead since the cutover, kept only because deleting files needs a decision:
src/app/models/session.ts and the unrouted LoginComponent.