first commit

This commit is contained in:
2025-11-24 16:30:37 +00:00
commit 843e93a274
114 changed files with 25585 additions and 0 deletions

View File

@@ -0,0 +1,555 @@
# 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`)
```sql
CREATE TABLE settings (
key TEXT PRIMARY KEY,
value TEXT NOT NULL,
updated_at TEXT DEFAULT CURRENT_TIMESTAMP
);
```
**SMTP-Config als JSON in `settings.value`:**
```json
{
"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`)
```sql
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:
```env
# 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`)
```typescript
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:**
```typescript
// 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`:
```typescript
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:
```tsx
<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`
```typescript
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:**
```typescript
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:**
```env
SMTP_HOST=smtp.ethereal.email
SMTP_PORT=587
SMTP_USER=<ethereal-user>
SMTP_PASS=<ethereal-pass>
```
**React Email Dev-Server:**
```bash
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`:
```javascript
// 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
```json
{
"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)
```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