7ce42324da
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>
73 lines
3.9 KiB
Markdown
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`.
|