Compare commits

..

3 Commits

Author SHA1 Message Date
Bastian Wagner
e5ba292815 scss 2026-07-31 22:12:38 +02:00
Bastian Wagner
ed5a00a577 docs: correct mail design spec after deeper env/infra investigation
.env already exists locally with valid creds (the deleted repo-root
.env was an unused duplicate), and no MailDev infra exists for the
e2e mail tests, so the plan shifts to a MailService unit test plus
one manual send instead of touching .env or standing up e2e infra.
2026-07-31 22:09:59 +02:00
Bastian Wagner
539d073de4 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.
2026-07-31 22:04:16 +02:00
2 changed files with 156 additions and 0 deletions

View File

@@ -0,0 +1,155 @@
# 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. Ursache (nach genauerer Prüfung — korrigiert
gegenüber einer ersten Analyse):
- `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. **Das ist die alleinige Ursache.**
Korrektur: die im Repo-Root gelöschte `.env` (Commit `1d4546d "styling"`, korrekt aus dem
Git-Tracking entfernt und zu `.gitignore` hinzugefügt) ist eine andere, von der App nicht
genutzte Datei. Die tatsächlich verwendete `myteamwallet_backend/.env` (das Arbeitsverzeichnis
der NestJS-App) existiert bereits lokal, ist bereits in `myteamwallet_backend/.gitignore`
ausgeschlossen und enthält bereits gültige `MAIL_*`/`DATABASE_*` Werte (Strato-SMTP,
lokale MySQL-Instanz läuft). Es ist **keine Aktion an `.env` nötig.**
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**: bleiben unangetastet — `myteamwallet_backend/.env` existiert bereits
lokal mit gültigen Strato-Werten (keine Rotation im Rahmen dieser Änderung).
- **Testing-Infrastruktur**: kein lokaler MailDev-Container/e2e-Aufbau in diesem Zug (siehe
Testing-Abschnitt) — Unit-Test + einmalige manuelle Verifikation mit echtem Versand
stattdessen.
- **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<T>` 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`
Keine Aktion nötig — `myteamwallet_backend/.env` existiert bereits lokal mit gültigen Werten
(siehe Kontext oben).
## 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
Korrektur gegenüber einer ersten Analyse: der bestehende e2e-Test
`test/user/auth.e2e-spec.ts` ("Confirm email") pollt eine MailDev-HTTP-Inbox-API
(`MAIL_HOST:MAIL_CLIENT_PORT`). Dafür existiert im Repo keine Infrastruktur (kein
`docker-compose.yml`, kein MailDev-Container), und die echte `.env` zeigt auf produktives
Strato-SMTP statt auf eine lokale Test-Mailbox — dieser Test kann unabhängig vom Bugfix nicht
grün werden, ohne separate Test-Infrastruktur aufzusetzen. Entscheidung: kein MailDev-Aufbau in
diesem Zug (zu viel Zusatzaufwand für den aktuellen Scope).
Stattdessen:
- Neuer Jest Unit-Test für `MailService` (gemockter `MailerService`/`ConfigService`), der
verifiziert, dass `userSignUp()` und `forgotPassword()` `sendMail(...)` mit den erwarteten
Parametern (`to`, `subject`, `template`, `context`) aufrufen. Dieser Test hätte den
`return;`-Bug erkannt und ist die primäre automatisierte Regression-Absicherung.
- Manuelle Verifikation: Backend lokal starten (bestehende `.env`, laufende lokale MySQL-Instanz
vorhanden), einmal Registrierung und einmal Passwort-vergessen über die echten Endpunkte
auslösen und den tatsächlichen Mailversand über Strato-SMTP mit einer echten Test-Mailadresse
prüfen.
- Der bestehende e2e-Test bleibt unverändert im Repo (nicht Teil dieses Scopes, weiterhin ohne
lokale MailDev-Instanz nicht lauffähig — vorbestehende Einschränkung, nicht durch diese Änderung
verursacht). Kein neuer e2e-Test für forgot-password (gleiche Infrastruktur-Einschränkung).
## 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.

View File

@@ -10,6 +10,7 @@ p {
} }
h1 { h1 {
font-size: clamp(2rem, 4vw, 3rem); font-size: clamp(2rem, 4vw, 3rem);
line-height: clamp(2rem, 4vw, 3rem);
margin-bottom: 8px; margin-bottom: 8px;
} }
.eyebrow { .eyebrow {