Files
location-mqtt-tracker-app/docs/plans/2025-11-17-smtp-integration-design.md
2025-11-24 16:30:37 +00:00

13 KiB

SMTP Integration Design

Datum: 2025-11-17 Feature: SMTP Integration für E-Mail-Versand (Welcome Mails & Password Reset)

Übersicht

Integration eines flexiblen SMTP-Systems in die Location Tracker App für automatische E-Mail-Benachrichtigungen. Hybrid-Ansatz mit .env-Fallback und Admin-Panel-Konfiguration.

Anforderungen

Funktionale Anforderungen

  • Welcome-E-Mails beim Erstellen neuer User
  • Password-Reset-Flow für User
  • SMTP-Konfiguration über Admin-Panel
  • Live-Vorschau aller E-Mail-Templates
  • Test-E-Mail-Versand zur Validierung
  • Fallback auf .env wenn DB-Config leer

Nicht-funktionale Anforderungen

  • Verschlüsselte Speicherung von SMTP-Passwörtern
  • Rate-Limiting für Test-E-Mails
  • Fehlertoleranz (User-Erstellung funktioniert auch bei E-Mail-Fehler)
  • Mobile-responsive Admin-UI

Architektur

1. Datenbank-Schema

Neue Tabelle: settings (in database.sqlite)

CREATE TABLE settings (
  key TEXT PRIMARY KEY,
  value TEXT NOT NULL,
  updated_at TEXT DEFAULT CURRENT_TIMESTAMP
);

SMTP-Config als JSON in settings.value:

{
  "host": "smtp.gmail.com",
  "port": 587,
  "secure": false,
  "auth": {
    "user": "user@example.com",
    "pass": "encrypted_password_here"
  },
  "from": {
    "email": "noreply@example.com",
    "name": "Location Tracker"
  },
  "replyTo": "support@example.com",
  "timeout": 10000
}

Neue Tabelle: password_reset_tokens (in database.sqlite)

CREATE TABLE password_reset_tokens (
  token TEXT PRIMARY KEY,
  user_id INTEGER NOT NULL,
  expires_at TEXT NOT NULL,
  used INTEGER DEFAULT 0,
  created_at TEXT DEFAULT CURRENT_TIMESTAMP,
  FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);

2. Umgebungsvariablen (.env)

Neue Variablen für Fallback-Config:

# SMTP Configuration (Fallback)
SMTP_HOST=smtp.example.com
SMTP_PORT=587
SMTP_SECURE=false
SMTP_USER=user@example.com
SMTP_PASS=password
SMTP_FROM_EMAIL=noreply@example.com
SMTP_FROM_NAME=Location Tracker

# Security
ENCRYPTION_KEY=<32-byte-hex-string>

3. API-Struktur

Admin-APIs (Authentifizierung erforderlich, role: admin)

GET /api/admin/settings/smtp

  • Gibt aktuelle SMTP-Config zurück (Passwort maskiert)
  • Response: { config: SMTPConfig | null, source: 'database' | 'env' }

POST /api/admin/settings/smtp

  • Speichert SMTP-Einstellungen in Datenbank
  • Body: SMTPConfig
  • Validiert alle Felder
  • Verschlüsselt Passwort vor Speicherung

POST /api/admin/settings/smtp/test

  • Testet SMTP-Verbindung ohne zu speichern
  • Body: { config: SMTPConfig, testEmail: string }
  • Sendet Test-E-Mail an angegebene Adresse

GET /api/admin/emails/preview?template=welcome

  • Rendert E-Mail-Template als HTML
  • Query: template ("welcome" | "password-reset")
  • Verwendet Beispiel-Daten

POST /api/admin/emails/send-test

  • Sendet Test-E-Mail mit echtem Template
  • Body: { template: string, email: string }
  • Rate-Limit: 5 pro Minute

Auth-APIs (Public)

POST /api/auth/forgot-password

  • Body: { email: string }
  • Generiert Token, sendet Reset-E-Mail
  • Response: Immer Success (Security: kein User-Enumeration)

POST /api/auth/reset-password

  • Body: { token: string, newPassword: string }
  • Validiert Token, setzt neues Passwort
  • Markiert Token als "used"

4. E-Mail-Service

Zentrale Klasse: EmailService (/lib/email-service.ts)

class EmailService {
  async sendWelcomeEmail(user: User, temporaryPassword?: string): Promise<void>
  async sendPasswordReset(user: User, token: string): Promise<void>

  private async getConfig(): Promise<SMTPConfig>
  private async sendEmail(to: string, subject: string, html: string): Promise<void>
}

Funktionsweise:

  1. Lädt SMTP-Config aus DB (Key: smtp_config)
  2. Falls leer → Lädt aus .env
  3. Entschlüsselt Passwort
  4. Erstellt Nodemailer-Transport
  5. Rendert React Email Template → HTML
  6. Sendet E-Mail mit Fehlerbehandlung

5. React Email Templates

Verzeichnisstruktur:

/emails/
  ├── components/
  │   ├── email-layout.tsx      # Basis-Layout für alle E-Mails
  │   ├── email-header.tsx      # Header mit Logo
  │   └── email-footer.tsx      # Footer mit Links
  ├── welcome.tsx               # Welcome-E-Mail Template
  └── password-reset.tsx        # Password-Reset Template

Template-Props:

// welcome.tsx
interface WelcomeEmailProps {
  username: string;
  loginUrl: string;
  temporaryPassword?: string;
}

// password-reset.tsx
interface PasswordResetEmailProps {
  username: string;
  resetUrl: string;
  expiresIn: string;
}

Features:

  • Inline-Styles für maximale Kompatibilität
  • Responsive Design
  • Automatische Plain-Text-Alternative
  • Wiederverwendbare Komponenten

6. Admin-Panel UI

Neue Navigation

Erweitere /admin/layout.tsx:

const navigation = [
  { name: "Dashboard", href: "/admin" },
  { name: "Devices", href: "/admin/devices" },
  { name: "Users", href: "/admin/users" },
  { name: "Settings", href: "/admin/settings" },    // NEU
  { name: "Emails", href: "/admin/emails" },        // NEU
];

/admin/settings - SMTP-Konfiguration

Layout:

  • Tab-Navigation: "SMTP Settings" (weitere Tabs vorbereitet)
  • Formular mit Feldern:
    • Host (Text)
    • Port (Number)
    • Secure (Toggle: TLS/SSL)
    • Username (Text)
    • Password (Password, zeigt *** wenn gesetzt)
    • From Email (Email)
    • From Name (Text)
    • Reply-To (Email, optional)
    • Timeout (Number, ms)

Buttons:

  • "Test Connection" → Modal für Test-E-Mail-Adresse
  • "Save Settings" → Speichert in DB
  • "Reset to Defaults" → Lädt .env-Werte

Validierung:

  • Live-Validierung bei Eingabe
  • Port: 1-65535
  • E-Mail-Format prüfen
  • Required-Felder markieren

/admin/emails - E-Mail-Vorschau

Layout:

  • Links: Liste aller Templates
  • Rechts: Live-Vorschau (iframe mit gerendertem HTML)
  • Mobile: Gestapelt

Features:

  • Template auswählen → Vorschau aktualisiert
  • "Send Test Email" Button → Modal für E-Mail-Adresse
  • "Edit Template" Link (führt zur Datei, Info-Text)

/admin/users - Erweiterungen

Neue Buttons pro User:

  • "Resend Welcome Email"
  • "Send Password Reset"

7. User-facing Pages

/forgot-password

UI:

  • E-Mail-Eingabefeld
  • "Send Reset Link" Button
  • Link zurück zu /login

Flow:

  1. User gibt E-Mail ein
  2. POST zu /api/auth/forgot-password
  3. Success-Message (immer, auch bei ungültiger E-Mail)
  4. E-Mail mit Reset-Link wird versendet

/reset-password?token=xxx

UI:

  • Neues Passwort eingeben (2x)
  • "Reset Password" Button

Validierung:

  • Token gültig?
  • Token nicht abgelaufen?
  • Token nicht bereits verwendet?
  • Passwort-Stärke prüfen

Flow:

  1. Token validieren (onLoad)
  2. Bei ungültig: Fehler anzeigen
  3. User gibt neues Passwort ein
  4. POST zu /api/auth/reset-password
  5. Erfolg → Redirect zu /login

/login - Erweiterung

Neuer Link unter Login-Form:

<Link href="/forgot-password">Forgot Password?</Link>

Sicherheit

Verschlüsselung

SMTP-Passwort:

  • Algorithmus: AES-256-GCM
  • Key: process.env.ENCRYPTION_KEY (32-Byte-Hex)
  • Verschlüsselt vor DB-Speicherung
  • Entschlüsselt beim Abrufen

Implementierung: /lib/crypto-utils.ts

function encrypt(text: string): string
function decrypt(encryptedText: string): string

Authentifizierung & Autorisierung

  • Alle /api/admin/* prüfen next-auth Session
  • User muss role: "admin" haben
  • Unauthorized → 401 Response

Rate-Limiting

Test-E-Mail-Versand:

  • Max. 5 Test-E-Mails pro Minute
  • Pro IP-Adresse
  • 429 Response bei Überschreitung

Input-Validierung

  • E-Mail-Adressen: RFC 5322 Format
  • Port: 1-65535
  • Timeout: > 0
  • SQL-Injection-Schutz durch Prepared Statements

Token-Sicherheit

Password-Reset-Tokens:

  • UUID v4 (kryptografisch sicher)
  • Gültigkeitsdauer: 1 Stunde
  • One-Time-Use (used-Flag)
  • Automatisches Cleanup alter Tokens (Cron-Job)

Fehlerbehandlung

E-Mail-Versand-Fehler

Strategie: Fail-Soft

  • User-Erstellung schlägt NICHT fehl bei E-Mail-Fehler
  • Fehler wird geloggt
  • Admin-Benachrichtigung im Dashboard (später)
  • "Resend" Option verfügbar

Error-Logging:

console.error('[EmailService] Failed to send email:', {
  type: 'welcome' | 'password-reset',
  recipient: user.email,
  error: error.message
});

SMTP-Verbindungsfehler

Im Admin-Panel:

  • Detaillierte Fehlermeldung anzeigen
  • Vorschläge zur Fehlerbehebung
  • Link zur SMTP-Provider-Dokumentation

Typische Fehler:

  • Authentication failed → Credentials prüfen
  • Connection timeout → Firewall/Port prüfen
  • TLS error → secure-Flag anpassen

Testing

Development

Mailtrap/Ethereal für SMTP-Tests:

SMTP_HOST=smtp.ethereal.email
SMTP_PORT=587
SMTP_USER=<ethereal-user>
SMTP_PASS=<ethereal-pass>

React Email Dev-Server:

npm run email:dev

Öffnet Browser mit Vorschau aller Templates

Admin-Panel Testing

  1. SMTP-Config eingeben
  2. "Test Connection" klicken
  3. Test-E-Mail-Adresse eingeben
  4. E-Mail empfangen & Inhalt prüfen
  5. "Save Settings" klicken
  6. Page-Reload → Config bleibt erhalten

Integration Testing

Welcome-E-Mail:

  1. Neuen User in /admin/users erstellen
  2. Welcome-E-Mail wird versendet
  3. E-Mail empfangen & prüfen

Password-Reset:

  1. /forgot-password öffnen
  2. E-Mail eingeben & absenden
  3. Reset-E-Mail empfangen
  4. Link klicken → /reset-password öffnet
  5. Neues Passwort setzen
  6. Mit neuem Passwort einloggen

Deployment

Checklist

  • npm install für neue Dependencies
  • ENCRYPTION_KEY in .env generieren
  • SMTP-Credentials in .env setzen (optional, Fallback)
  • npm run db:init:app (neue Tabellen erstellen)
  • Server-Neustart
  • SMTP im Admin-Panel konfigurieren
  • Test-E-Mail senden & empfangen
  • Password-Reset-Flow einmal komplett testen

Migration-Script

Erweitere /scripts/init-database.js:

// Neue Tabellen erstellen
db.exec(`
  CREATE TABLE IF NOT EXISTS settings (
    key TEXT PRIMARY KEY,
    value TEXT NOT NULL,
    updated_at TEXT DEFAULT CURRENT_TIMESTAMP
  );
`);

db.exec(`
  CREATE TABLE IF NOT EXISTS password_reset_tokens (
    token TEXT PRIMARY KEY,
    user_id INTEGER NOT NULL,
    expires_at TEXT NOT NULL,
    used INTEGER DEFAULT 0,
    created_at TEXT DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
  );
`);

// Index für Performance
db.exec(`
  CREATE INDEX IF NOT EXISTS idx_reset_tokens_user_id
  ON password_reset_tokens(user_id);
`);

Dependencies

NPM-Packages

{
  "dependencies": {
    "nodemailer": "^6.9.8",
    "react-email": "^2.1.0",
    "@react-email/components": "^0.0.14"
  },
  "devDependencies": {
    "@types/nodemailer": "^6.4.14"
  }
}

Scripts (package.json)

{
  "scripts": {
    "email:dev": "email dev"
  }
}

Dateistruktur

/location-tracker-app/
├── docs/
│   └── plans/
│       └── 2025-11-17-smtp-integration-design.md
├── emails/
│   ├── components/
│   │   ├── email-layout.tsx
│   │   ├── email-header.tsx
│   │   └── email-footer.tsx
│   ├── welcome.tsx
│   └── password-reset.tsx
├── lib/
│   ├── email-service.ts
│   ├── crypto-utils.ts
│   └── email-renderer.ts
├── app/
│   ├── admin/
│   │   ├── settings/
│   │   │   └── page.tsx
│   │   ├── emails/
│   │   │   └── page.tsx
│   │   └── users/
│   │       └── page.tsx (erweitern)
│   ├── forgot-password/
│   │   └── page.tsx
│   ├── reset-password/
│   │   └── page.tsx
│   └── api/
│       ├── admin/
│       │   ├── settings/
│       │   │   └── smtp/
│       │   │       ├── route.ts
│       │   │       └── test/route.ts
│       │   └── emails/
│       │       ├── preview/route.ts
│       │       └── send-test/route.ts
│       └── auth/
│           ├── forgot-password/route.ts
│           └── reset-password/route.ts
└── scripts/
    └── init-database.js (erweitern)

Zukünftige Erweiterungen

Phase 2 (Optional)

  • Weitere E-Mail-Templates (Geofence-Alerts, Reports)
  • E-Mail-Queue für Bulk-Versand
  • E-Mail-Versand-Statistiken im Dashboard
  • Attachments-Support
  • Multiple SMTP-Provider (Failover)
  • E-Mail-Template-Editor im Admin-Panel

Phase 3 (Optional)

  • Alternative zu SMTP: SendGrid/Mailgun/Resend API
  • Webhook für E-Mail-Events (Delivered, Opened, Clicked)
  • Unsubscribe-Verwaltung
  • E-Mail-Preferences pro User

Offene Fragen

  • SMTP-Parameter: Erweiterte Konfiguration gewählt
  • E-Mail-Bibliothek: Nodemailer gewählt
  • Template-Engine: React Email gewählt
  • Vorschau-Strategie: Live-Vorschau im Admin-Panel
  • Config-Speicherung: Hybrid-Ansatz (DB + .env Fallback)

Abnahmekriterien

  • Admin kann SMTP-Settings im Panel konfigurieren
  • Test-E-Mail-Versand funktioniert
  • Welcome-E-Mail wird bei User-Erstellung gesendet
  • Password-Reset-Flow funktioniert komplett
  • E-Mail-Vorschau zeigt alle Templates korrekt an
  • SMTP-Passwort wird verschlüsselt gespeichert
  • Fallback auf .env funktioniert
  • Fehlerbehandlung zeigt sinnvolle Meldungen
  • Mobile-responsive Admin-UI
  • Alle Tests erfolgreich