feat(backend): implement phases 0-6 (auth, kc, wahl, files, chat, sync)
Full NestJS backend for the KC-App platform: - auth: Authentik OIDC resource-server strategy + guest invite-code JWT login, plus TokenVerificationService for the WS handshake path - kc: Leitungsteam-only KC (event) creation/listing - wahl: Wahl/Workshop admin, Force-Zuteilung overrides, ZuteilungService (port of the WP plugin's kc_run_zuteilung), CSV export - files: LT-only upload with visibility tiers; list/download filtered by caller tier; StorageProvider abstraction (WebDAV/Nextcloud default, S3) - chat: Gemeinde group / DM / LT-wide / broadcast channels; REST + raw ws gateway sharing ChatService access rules - sync: append-only SyncLogEntry replication log + local<->cloud push/pull scheduler, shared-secret guarded - common: Role enum, @Roles decorator, KC-scoped RolesGuard (LT global) - serves client/web/ interim static web client under / (API under /api) Typecheck, nest build and boot test pass; needs real Postgres/Authentik/ Nextcloud to run end to end. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
+42
-3
@@ -13,6 +13,11 @@ npx prisma migrate dev --name init # requires a running PostgreSQL instance
|
||||
npm run start:dev
|
||||
```
|
||||
|
||||
The API is served under `/api` (see `app.setGlobalPrefix('api')` in
|
||||
`main.ts`); everything else (`/`, `/app.js`, ...) is served statically from
|
||||
`../client/web` via `ServeStaticModule`, so the backend doubles as the web
|
||||
client's host - no separate web server is needed.
|
||||
|
||||
## Auth model
|
||||
|
||||
- Team members (Leitungsteam, Gemeinde Verantwortliche, Gemeinde Teamer) are
|
||||
@@ -28,10 +33,44 @@ npm run start:dev
|
||||
## Modules implemented so far
|
||||
|
||||
- `prisma/` — shared `PrismaClient` provider.
|
||||
- `auth/` — Authentik resource-server strategy + guest invite-code login.
|
||||
- `auth/` — Authentik resource-server strategy (`AuthGuard('authentik')`) +
|
||||
guest invite-code login issuing a locally-signed JWT (`AuthGuard('guest')`).
|
||||
- `kc/` — KC (event) creation/listing, Leitungsteam-only.
|
||||
- `wahl/` — Wahl/Workshop administration (Leitungsteam-only), guest
|
||||
Teilnehmer submission, Force-Zuteilung overrides, and `ZuteilungService`:
|
||||
a faithful port of the WP plugin's `kc_run_zuteilung` (force-assignments →
|
||||
up to 3 wish rounds → random fill → consolidation of workshops that stay
|
||||
below `minTeilnehmer`), plus CSV export (`GET /wahl/:id/zuteilung/csv`).
|
||||
- `files/` — Leitungsteam-only upload (`POST /files/:kcId`, multipart) tagged
|
||||
with a `FileVisibility` tier; list/download (`GET /files/:kcId`,
|
||||
`GET /files/download/:fileId`) accept either an Authentik or a guest token
|
||||
and filter by the caller's allowed visibility tiers. Storage is behind a
|
||||
`StorageProvider` abstraction: defaults to Nextcloud via WebDAV
|
||||
(`WEBDAV_*` env vars), switchable to S3-compatible storage with
|
||||
`STORAGE_PROVIDER=s3` (`S3_*` env vars).
|
||||
- `chat/` — Gemeinde-Gruppenchat, 1:1 Direktnachrichten, Leitungsteam-über-
|
||||
greifende Kanäle und Broadcast (Konfis lesen nur). Channel administration
|
||||
and message history are plain REST (`ChatController`); real-time send/
|
||||
receive is a raw `ws` gateway (`ChatGateway`, path `/chat`) since passport
|
||||
guards don't apply to WS upgrades — auth happens once via `?token=` at
|
||||
connect time (`TokenVerificationService` tries Authentik JWKS, then falls
|
||||
back to a guest token). Access rules live in `ChatService` and are shared
|
||||
between the REST and WS entry points.
|
||||
- `sync/` — replicates mutations between the local (on-site) and cloud
|
||||
server. `SyncService.capture()` is called by feature services right after
|
||||
a write, appending an entry to the append-only `SyncLogEntry` log tagged
|
||||
with this server's `SERVER_ID`. The local server (set `SYNC_ENABLED=true`,
|
||||
`SYNC_PEER_URL`) periodically pushes its new entries to the cloud's
|
||||
`POST /sync/ingest` and pulls the cloud's via `GET /sync/export`
|
||||
(`SyncSchedulerService`, every 30s), both guarded by `SYNC_SHARED_SECRET`
|
||||
(`SyncSecretGuard`) rather than user auth. No conflict resolution is
|
||||
implemented by design — the local server is the sole source of truth
|
||||
while an event is live. `POST /sync/trigger` lets a Leitungsteam member
|
||||
force an immediate push+pull. Known gap: only entity metadata is
|
||||
replicated; uploaded file bytes only resolve on both sides if local and
|
||||
cloud share the same Nextcloud/S3 backend.
|
||||
- `common/` — `Role` enum, `@Roles()` decorator, `RolesGuard` (KC-scoped,
|
||||
Leitungsteam roles are global across all KCs).
|
||||
|
||||
Not yet implemented: Wahl/Workshop/Zuteilung engine, file sharing, chat
|
||||
realtime gateway, local/cloud sync engine.
|
||||
All planned backend phases are implemented; remaining work is the Flutter
|
||||
clients (see repo root README).
|
||||
|
||||
Reference in New Issue
Block a user