feat: email service — SMTP + deadline reminders + invitations (t-paliad-021)

- internal/services/mail_service.go: SMTP/TLS sender (implicit TLS on 465),
  html/template rendering, branded base layout + content templates, silent
  no-op when SMTP_* unset.
- internal/services/reminder_service.go: hourly scanner for Fristen that are
  overdue / due tomorrow / due within the week (Monday digest). Dedup via
  paliad.reminder_log (24h window).
- internal/services/invite_service.go: POST /api/invite flow with domain
  whitelist, in-memory 10/day/user rate limit, audit row in
  paliad.invitations.
- internal/handlers/invite.go: POST + GET /api/invite handlers.
- Sidebar "Kolleg:in einladen" button + modal on every page.
- migration 016: paliad.reminder_log, paliad.invitations, users.lang column.
- docker-compose: SMTP_* + PALIAD_BASE_URL env vars.
- docs/feature-roadmap.md: documented Supabase auth-SMTP routing as open
  question; current pilot keeps identity mails on Supabase default sender.

Rationale: get Paliad off Supabase's best-effort outbound for the
inbox-facing stuff (reminders, invitations) and move deadline nudges from
passive dashboard to active email. Custom Supabase auth SMTP is blocked on
the shared ydb.youpc.org instance — deferred until Paliad has its own
project or GoTrue webhook relay.
This commit is contained in:
m
2026-04-20 12:34:38 +02:00
parent 45c7cf34ef
commit 11217f7bfa
26 changed files with 1808 additions and 12 deletions

View File

@@ -294,3 +294,9 @@ What Paliad is *not*:
- **External counsel access.** Bringing in an outside boutique on a specific Akte currently means adding them as a user (not possible without the HLC email domain). A future `external_collaborators` table with scoped RLS would cover it.
- **Read-only archive post-closure.** Add `is_archived` on `paliad.akten`, deny mutations via RLS. Cheap follow-on.
- **AI revisit.** The Phase H / 4.1 pause is a decision, not a technical block. When Anthropic API goes back on the table, both AI extraction (Phase H) and KI-Recherche (4.1) can be unblocked.
- **Supabase Auth SMTP routing.** Confirmation / password-reset / magic-link mails from `ydb.youpc.org` still go through Supabase's default sender. Routing them through Paliad's SMTP (`mail@paliad.de`) is a one-line GoTrue config change, but youpc's Supabase is shared with youpc.org, so the global SMTP settings can't be flipped without rebranding youpc.org's auth mails too. Resolution paths (lowest-effort first):
1. Move Paliad to its own Supabase project and configure SMTP there.
2. Wait until the youpc instance exposes per-project SMTP (Supabase Pro / self-hosted upgrade).
3. Write a custom GoTrue webhook that Paliad's Go server intercepts and re-sends via `MailService`.
For now the inbox-facing mails (reminders + invitations) go through Paliad's SMTP; identity-bootstrap mails stay on the default sender — acceptable for the current HLC pilot. Tracked as part of t-paliad-021 completion (2026-04-20).