feat(backend): mail module + send personal Gemeinde-Teamer invites
New global mail/ module mirroring the files/storage/ provider pattern: - MailProvider abstraction; default LogMailProvider only logs (no delivery), MAIL_PROVIDER=smtp switches to a nodemailer SMTP transport (SMTP_*, MAIL_FROM). - MailService.sendTeamerInvite() composes the invite email with a link built from APP_BASE_URL. TeamerService.createInvite() now mails personal invites (those with an email) best-effort and returns `emailSent`; group links are unchanged. Delivery failures are logged and swallowed, never blocking invite creation. New env: APP_BASE_URL, MAIL_PROVIDER, MAIL_FROM, SMTP_HOST/PORT/SECURE/ USER/PASS. Tests: teamer spec covers mail-on-personal-invite, no-mail-on-group-link, and transport-drop; npm test green at 56. Docs updated. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -7,7 +7,8 @@ architecture context).
|
||||
|
||||
```bash
|
||||
npm install
|
||||
cp .env.example .env # DATABASE_URL / AUTHENTIK_ISSUER_URL / AUTHENTIK_LEITUNGSTEAM_GROUP / GUEST_JWT_SECRET / TEAM_JWT_SECRET
|
||||
cp .env.example .env # DATABASE_URL / AUTHENTIK_ISSUER_URL / AUTHENTIK_LEITUNGSTEAM_GROUP /
|
||||
# GUEST_JWT_SECRET / TEAM_JWT_SECRET / APP_BASE_URL (+ MAIL_* for real email)
|
||||
npx prisma generate
|
||||
npx prisma migrate dev --name init # requires a running PostgreSQL instance
|
||||
npm run start:dev
|
||||
@@ -64,7 +65,9 @@ client's host - no separate web server is needed.
|
||||
`DELETE teamer-invites/:inviteId`. Callable by Leitungsteam (any Gemeinde)
|
||||
or a Verantwortliche/r for their own Gemeinde (enforced in `TeamerService`,
|
||||
since `RolesGuard` only scopes by `kcId`). Files/chat read endpoints accept
|
||||
`'team'` tokens too, so Teamer see non-Konfi files and chat.
|
||||
`'team'` tokens too, so Teamer see non-Konfi files and chat. A personal
|
||||
invite (with `email`) is mailed via `MailService`; the response carries
|
||||
`emailSent`. Group-link invites (no `email`) are shared by hand.
|
||||
- `onboarding/` — self-registration for Gemeinde Verantwortliche.
|
||||
`GET /onboarding/kc/:inviteCode` (public) returns the KC name + its
|
||||
Gemeinden to pick from. `POST /onboarding/verantwortliche` takes the
|
||||
@@ -74,6 +77,12 @@ client's host - no separate web server is needed.
|
||||
`GET /onboarding/requests?kcId=` and `POST /onboarding/requests/:id/approve`
|
||||
or `.../reject`. Auth strategies only load `ACTIVE` memberships, so a
|
||||
pending request grants nothing until approved.
|
||||
- `mail/` — global `MailProvider` abstraction (mirrors `files/storage/`):
|
||||
default `log` provider only logs what it would send; `MAIL_PROVIDER=smtp`
|
||||
uses a real `nodemailer` SMTP transport (`SMTP_*`, `MAIL_FROM`).
|
||||
`MailService.sendTeamerInvite()` composes the personal-invite email with a
|
||||
link built from `APP_BASE_URL`. Delivery is best-effort — failures are
|
||||
logged and swallowed, never blocking the invite.
|
||||
- `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 →
|
||||
@@ -113,7 +122,8 @@ client's host - no separate web server is needed.
|
||||
All planned backend phases are implemented. `npm test` runs Jest unit tests
|
||||
(`ZuteilungService`, `TeamAuthService`, `TeamerService`, `OnboardingService`,
|
||||
`resolveOrProvisionAuthentikUser` / `toAuthenticatedUser`; Prisma mocked).
|
||||
Remaining work: the Flutter clients (see repo root README), invite email
|
||||
delivery, push notifications, and the first real Prisma migration (only
|
||||
`schema.prisma` exists so far). Ops note: the Authentik provider must emit a
|
||||
`groups` claim in the access token for the LT check to work.
|
||||
Remaining work: the Flutter clients (see repo root README), push
|
||||
notifications, and the first real Prisma migration (only `schema.prisma`
|
||||
exists so far). Ops notes: the Authentik provider must emit a `groups` claim
|
||||
for the LT check, and `MAIL_PROVIDER=smtp` + `SMTP_*` must be set for invite
|
||||
emails to actually leave the box.
|
||||
|
||||
Reference in New Issue
Block a user