From 539d073de4a074e776a02661f412a8eae841ded1 Mon Sep 17 00:00:00 2001 From: Bastian Wagner Date: Fri, 31 Jul 2026 22:04:16 +0200 Subject: [PATCH] docs: add design spec for mail sending fix and template redesign Captures the brainstorming outcome for fixing the broken mail dispatch, removing the mail-only nestjs-i18n setup in favor of hardcoded German copy, and redesigning the registration/forgot-password email templates. --- .../specs/2026-07-31-mail-versand-design.md | 137 ++++++++++++++++++ 1 file changed, 137 insertions(+) create mode 100644 docs/superpowers/specs/2026-07-31-mail-versand-design.md diff --git a/docs/superpowers/specs/2026-07-31-mail-versand-design.md b/docs/superpowers/specs/2026-07-31-mail-versand-design.md new file mode 100644 index 0000000..f9e5524 --- /dev/null +++ b/docs/superpowers/specs/2026-07-31-mail-versand-design.md @@ -0,0 +1,137 @@ +# Mailversand reparieren + Templates neu gestalten + +Status: approved +Datum: 2026-07-31 + +## Kontext + +Backend: NestJS (`myteamwallet_backend`), basierend auf `nestjs-boilerplate`. Mailversand über +`@nestjs-modules/mailer` (Nodemailer) mit Handlebars-Templates. E-Mails werden für zwei Flows +verschickt: Registrierung (Bestätigungsmail) und Passwort vergessen. + +Der Mailversand funktioniert aktuell nicht, aus zwei unabhängigen Gründen: + +1. `src/mail/mail.service.ts` — sowohl `userSignUp()` als auch `forgotPassword()` haben ein + totes `return;` als erste Anweisung, noch vor dem eigentlichen `mailerService.sendMail(...)` + Aufruf. Der Versand-Code wird nie erreicht. +2. `.env` existiert lokal nicht mehr im Arbeitsverzeichnis (im Commit `1d4546d "styling"` wurde + die Datei korrekt aus dem Git-Tracking entfernt und zu `.gitignore` hinzugefügt — das war + bereits vor diesem Spec erledigt). Ohne `.env` sind alle `MAIL_*`/`DATABASE_*`/etc. + Umgebungsvariablen `undefined`, die App kann so nicht sinnvoll laufen. + +Zusätzlich bestehende Baustellen, die in diesem Zug mit erledigt werden: + +- E-Mail-Templates (`activation.hbs`, `reset-password.hbs`) sind unverändertes, unstyled + Boilerplate aus dem `nestjs-boilerplate` Scaffold (graue Kopfzeile, generischer Button, kein + Branding). +- Die E-Mail-Texte laufen aktuell über `nestjs-i18n`, das ausschließlich für diese zwei Mails + genutzt wird und nur eine `en`-Locale hat, obwohl das Produkt (myteamwallet.de) deutschsprachig + ist. Entscheidung (siehe unten): `nestjs-i18n` komplett entfernen, Texte direkt auf Deutsch im + Code/Template. + +Das Frontend (`myteamwallet_frontend_modern`, das aktuell aktiv gebaute/deployte Frontend laut +`Dockerfile`) hat kein fertiges Bild-Logo — nur generische Angular-Default-Icons. Als Markenfarbe +dient das dort verwendete Grün `#2e7d32` (Material `theme-color`), Schriftart Roboto, +Wordmark-Schreibweise „TeamWallet". + +## Entscheidungen aus dem Brainstorming + +- **SMTP-Zugangsdaten**: das alte, aus der Git-Historie wiederhergestellte Strato-Passwort wird + vorerst weiterverwendet (keine sofortige Rotation im Rahmen dieser Änderung). +- **Sprachen**: nur Deutsch. Keine mehrsprachige i18n-Infrastruktur für Mails. +- **i18n-Mechanismus**: kein Sprachdatei-System — deutsche Texte direkt im Code/Template. + `nestjs-i18n` wird komplett entfernt (Modul-Registrierung, `src/i18n/`, Dependency, + `I18n`-Nutzung in `mail.service.ts`), da es im Backend ausschließlich für die zwei Mails + verwendet wurde. +- **Branding**: Grün `#2e7d32` (aus `myteamwallet_frontend_modern`), Textwordmark „TeamWallet" + (kein Bild-Logo), Roboto mit System-Font-Fallback, abgerundete Card-Optik passend zum + „fintech-lite" Look der App. +- **Personalisierung**: Anrede mit Vornamen („Hallo Max,"), da `firstName` an beiden Aufrufstellen + im `AuthService` bereits verfügbar ist. + +## Architektur / Komponenten + +### 1. Bugfix `mail.service.ts` + +- Die zwei toten `return;` Statements entfernen (vor `sendMail` in `userSignUp()` und + `forgotPassword()`). +- `I18n`/`I18nRequestScopeService` Constructor-Injection entfernen. +- Betreffzeilen als deutsche String-Literale direkt im Service (`'E-Mail bestätigen'`, + `'Passwort zurücksetzen'`). +- `MailData` Interface (`src/mail/interfaces/mail-data.interface.ts`) bleibt strukturell + gleich (`{ to: string; data: T }`), `T` wird pro Aufruf um `firstName?: string` erweitert: + `MailData<{ hash: string; firstName?: string }>`. + +### 2. Aufrufstellen `auth.service.ts` + +- `register()`: `dto.firstName` zusätzlich in `mailData.data` durchreichen. +- `forgotPassword()`: `user.firstName` zusätzlich in `mailData.data` durchreichen. +- Keine Änderung an Kontrollfluss, Fehlerbehandlung oder DB-Zugriffen — nur die zusätzliche + Datenübergabe. + +### 3. i18n-Entfernung + +- `app.module.ts`: `I18nModule.forRootAsync(...)` Import und Registrierung entfernen + (`HeaderResolver`-Import ebenfalls, falls sonst ungenutzt). +- `src/i18n/` Ordner komplett löschen (nur von Mails genutzt, siehe Analyse). +- `nestjs-i18n` Dependency aus `package.json` entfernen (`npm uninstall`, damit Lockfile + konsistent bleibt). +- `app.config.ts` Felder `fallbackLanguage`/`headerLanguage` (`APP_FALLBACK_LANGUAGE`, + `APP_HEADER_LANGUAGE`) werden mit entfernt: geprüft, sie werden ausschließlich in + `app.module.ts` für `I18nModule` gelesen (kein anderer Konsument im Code) — würden sonst toten + Config-Code hinterlassen. + +### 4. Templates + +- Neues Handlebars-Partial `src/mail/mail-templates/partials/layout-header.hbs` und + `layout-footer.hbs` (oder ein kombiniertes `layout.hbs`, falls das mit dem + `HandlebarsAdapter` sauberer registrierbar ist) — gemeinsamer Rahmen: Header mit + „TeamWallet"-Wordmark auf grünem/hellem Grund, Footer mit Kontakt-/Legal-Hinweis + (z. B. „Diese E-Mail wurde automatisch von TeamWallet verschickt."). +- `activation.hbs`: nutzt das Layout, Inhalt: Begrüßung mit `{{firstName}}` (Fallback ohne Namen + falls nicht vorhanden), kurzer Erklärtext, grüner CTA-Button „E-Mail bestätigen" → `{{url}}`. +- `reset-password.hbs`: nutzt das Layout, Inhalt: Begrüßung, Erklärtext, grüner CTA-Button + „Passwort zurücksetzen" → `{{url}}`, Hinweis auf zeitliche Begrenzung des Links und dass die + Mail ignoriert werden kann, falls nicht selbst angefragt. +- Styling-Konstanten: Akzent `#2e7d32`, Radius ~12px auf Card-Container, `max-width: 600px`, + Inline-CSS (kein externes Stylesheet — E-Mail-Client-Kompatibilität), tabellenbasiertes Layout + wie bisher (kein MJML — unnötige Build-Komplexität für zwei Templates), Roboto mit + Fallback-Stack (`Roboto, Helvetica, Arial, sans-serif`, da Web-Fonts in vielen Mail-Clients + nicht geladen werden). +- Kontext-Variablen, die `mail.service.ts` an Handlebars übergibt: `app_name` ("TeamWallet"), + `firstName`, `url`, ggf. `year` fürs Footer-Copyright — Rest (Texte, Button-Label, Titel) fest + im Template. + +### 5. `.env` wiederherstellen + +- Lokale, nicht getrackte `.env` Datei neu anlegen (Datei existiert nicht mehr im Working Tree, + ist aber in `.gitignore`) mit den aus `git show 7df5611:.env` wiederhergestellten Werten + (DB-, Mail-, JWT- und App-Konfiguration wie zuvor). +- Kein Commit dieser Datei — bleibt lokal/untracked, wie vom User entschieden. + +## Fehlerbehandlung + +- Kein Verhaltensunterschied zu heute beabsichtigt: `mailerService.sendMail(...)` wirft bei + SMTP-Fehlern eine Exception, die aktuell nicht speziell abgefangen wird (weder vorher noch + nachher) — das bleibt so, ist außerhalb des Scopes dieser Änderung. Falls beim Testen ein SMTP- + Fehler auftritt (z. B. Strato blockiert alte/rotierte Zugangsdaten), wird das separat + besprochen statt stillschweigend Fehlerbehandlung hinzuzufügen. + +## Testing + +- Bestehenden e2e-Test `test/user/auth.e2e-spec.ts` (Confirm-Email-Flow über MailDev-artige + HTTP-Inbox-API) laufen lassen und verifizieren, dass er jetzt grün ist (war zuvor durch den + Bug automatisch rot). +- Neuer e2e-Test für den Forgot-Password-Mail-Flow, analog zum bestehenden Confirm-Email-Test: + Passwort-vergessen anfragen, Mail über MailDev-Inbox abrufen, Hash extrahieren, gegen + `/api/v1/auth/reset/password` verifizieren. +- Kein Unit-Test-Aufbau für `MailService` vorgesehen (kein bestehendes Muster im Repo für + Mail-Unit-Tests, e2e deckt das Verhalten ausreichend ab) — YAGNI. + +## Out of Scope + +- Rotation der SMTP-Zugangsdaten (User macht das ggf. später selbst). +- Mehrsprachigkeit / weitere Locales. +- Redesign der Frontend-Flows, die auf `/confirm-email/:hash` bzw. `/password-change/:hash` + linken. +- MJML- oder Build-Pipeline-Einführung für Templates.