Files
Calendar_Frontend/CLAUDE.md
T
Paddy 7ce42324da Sign in through the admin app instead of this one
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>
2026-09-06 22:23:45 +02:00

73 lines
3.9 KiB
Markdown

# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Commands
```bash
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: true` is mandatory on every call (`api.service.ts` sets 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/me` and believes the
answer; the API's `requireAppAccess('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 `failure` in
`admin.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 via `withCredentials`, holds no credential itself
- `admin-auth.service.ts` — where signing in happens: the admin app's login URL (with a `?redirect=` back here) and sign-out
- `utils.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`.