mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-18 04:09:41 +02:00
refactor: restructure
monorepo with apps/ and services/ directories
This commit is contained in:
parent
25824ed0ac
commit
ff80aeec1f
4062 changed files with 2592 additions and 1278 deletions
495
apps/uload/docs/features/mail/COMPLETE-EMAIL-SETUP-GUIDE.md
Normal file
495
apps/uload/docs/features/mail/COMPLETE-EMAIL-SETUP-GUIDE.md
Normal file
|
|
@ -0,0 +1,495 @@
|
|||
# 📧 Komplette E-Mail-System Einrichtung für ulo.ad
|
||||
|
||||
## Übersicht
|
||||
|
||||
Diese Dokumentation beschreibt die vollständige Einrichtung des E-Mail-Systems für ulo.ad, inklusive Registrierung, E-Mail-Verifizierung, Passwort-Reset und zweisprachige Templates (DE/EN).
|
||||
|
||||
---
|
||||
|
||||
## 📋 Inhaltsverzeichnis
|
||||
|
||||
1. [Voraussetzungen](#voraussetzungen)
|
||||
2. [SMTP-Konfiguration](#smtp-konfiguration)
|
||||
3. [PocketBase Einstellungen](#pocketbase-einstellungen)
|
||||
4. [E-Mail-Templates einrichten](#e-mail-templates-einrichten)
|
||||
5. [Code-Anpassungen](#code-anpassungen)
|
||||
6. [Verifizierungs-Flow](#verifizierungs-flow)
|
||||
7. [Testing](#testing)
|
||||
8. [Troubleshooting](#troubleshooting)
|
||||
|
||||
---
|
||||
|
||||
## 🔧 Voraussetzungen
|
||||
|
||||
### Benötigte Services:
|
||||
|
||||
- **PocketBase** (läuft auf Port 8090)
|
||||
- **SMTP-Provider** (z.B. Brevo, SendGrid, Mailgun)
|
||||
- **SvelteKit App** (läuft auf Port 5173)
|
||||
|
||||
### Benötigte Dateien:
|
||||
|
||||
- ✅ E-Mail-Templates (`docs/mail/email-templates-bilingual.md`)
|
||||
- ✅ Verifizierungs-Route (`src/routes/verify-email/`)
|
||||
- ✅ Angepasster Register-Flow (`src/routes/register/+page.server.ts`)
|
||||
- ✅ Angepasste Login-Seite (`src/routes/login/+page.svelte`)
|
||||
|
||||
---
|
||||
|
||||
## 📮 SMTP-Konfiguration
|
||||
|
||||
### 1. SMTP-Provider einrichten (Beispiel: Brevo)
|
||||
|
||||
1. **Account erstellen** bei [Brevo](https://www.brevo.com/)
|
||||
2. **SMTP-Key generieren**:
|
||||
- Dashboard → SMTP & API → SMTP Settings
|
||||
- "Generate new SMTP key" klicken
|
||||
- Key sicher speichern (beginnt mit `xsmtpsib-`)
|
||||
|
||||
### 2. PocketBase SMTP konfigurieren
|
||||
|
||||
**In PocketBase Admin** (`http://localhost:8090/_/`):
|
||||
|
||||
1. Navigiere zu **Settings → Mail settings**
|
||||
2. Aktiviere **"Use SMTP mail server"**
|
||||
3. Trage folgende Daten ein:
|
||||
|
||||
```
|
||||
SMTP server host: smtp-relay.brevo.com
|
||||
Port: 587
|
||||
Username: [deine-email@domain.com]
|
||||
Password: [SMTP-KEY von Brevo]
|
||||
Use TLS: ✓ (aktiviert)
|
||||
```
|
||||
|
||||
4. **Sender address** eintragen:
|
||||
|
||||
```
|
||||
Sender name: ulo.ad
|
||||
Sender address: noreply@ulo.ad (oder deine Domain)
|
||||
```
|
||||
|
||||
5. **"Send test email"** klicken zum Testen
|
||||
6. **Save changes** klicken
|
||||
|
||||
### 3. Umgebungsvariablen (für Production)
|
||||
|
||||
Erstelle eine `.env.local` Datei:
|
||||
|
||||
```bash
|
||||
# .env.local
|
||||
PB_SMTP_HOST=smtp-relay.brevo.com
|
||||
PB_SMTP_PORT=587
|
||||
PB_SMTP_USER=deine-email@domain.com
|
||||
PB_SMTP_PASSWORD=xsmtpsib-[DEIN-KEY]
|
||||
PB_SMTP_TLS=true
|
||||
```
|
||||
|
||||
⚠️ **WICHTIG**: `.env.local` zu `.gitignore` hinzufügen!
|
||||
|
||||
---
|
||||
|
||||
## ⚙️ PocketBase Einstellungen
|
||||
|
||||
### 1. Application URL setzen
|
||||
|
||||
**In PocketBase Admin** → **Settings → Application**:
|
||||
|
||||
```
|
||||
Application name: ulo.ad
|
||||
Application URL: http://localhost:5173 (Development)
|
||||
https://ulo.ad (Production)
|
||||
```
|
||||
|
||||
### 2. Users Collection konfigurieren
|
||||
|
||||
Die `users` Collection wurde erweitert um:
|
||||
|
||||
- **language** Field (Select): `de`, `en`, `fr`, `es`, `it`
|
||||
- **verified** Field (Boolean): Automatisch von PocketBase verwaltet
|
||||
|
||||
### 3. Auth Settings prüfen
|
||||
|
||||
**Collections → users → Options**:
|
||||
|
||||
- ✅ Password auth enabled
|
||||
- ✅ Email/Username login
|
||||
- ✅ Verification token duration: 259200 (3 Tage)
|
||||
- ✅ Auth token duration: 604800 (7 Tage)
|
||||
|
||||
---
|
||||
|
||||
## 📝 E-Mail-Templates einrichten
|
||||
|
||||
### 1. Templates in PocketBase einfügen
|
||||
|
||||
**In PocketBase Admin** → **Collections → users → Options**:
|
||||
|
||||
Für jedes Template aus `docs/mail/email-templates-bilingual.md`:
|
||||
|
||||
#### a) Verification Template
|
||||
|
||||
- **Subject**: `Bestätige deine E-Mail / Verify your email - ulo.ad 🔗`
|
||||
- **Body**: HTML aus Datei kopieren
|
||||
- **WICHTIG**: URL anpassen von `{APP_URL}/_/#/auth/confirm-verification/{TOKEN}`
|
||||
zu `{APP_URL}/verify-email?token={TOKEN}`
|
||||
|
||||
#### b) Password Reset Template
|
||||
|
||||
- **Subject**: `Passwort zurücksetzen / Reset password - ulo.ad 🔐`
|
||||
- **Body**: HTML aus Datei kopieren
|
||||
- **URL**: `{APP_URL}/reset-password?token={TOKEN}`
|
||||
|
||||
#### c) Email Change Template
|
||||
|
||||
- **Subject**: `E-Mail-Adresse ändern / Change email address - ulo.ad 📧`
|
||||
- **Body**: HTML aus Datei kopieren
|
||||
|
||||
#### d) Auth Alert Template (Login von neuem Standort)
|
||||
|
||||
- **Subject**: `Neue Anmeldung / New login - ulo.ad 🔔`
|
||||
- **Body**: HTML aus Datei kopieren
|
||||
|
||||
#### e) OTP Template (falls aktiviert)
|
||||
|
||||
- **Subject**: `Einmal-Passwort / One-Time Password: {OTP} - ulo.ad 🔑`
|
||||
- **Body**: HTML aus Datei kopieren
|
||||
|
||||
### 2. Template-Variablen
|
||||
|
||||
Diese Platzhalter werden automatisch ersetzt:
|
||||
|
||||
| Variable | Beschreibung | Beispiel |
|
||||
| ------------- | -------------------------- | ---------------- |
|
||||
| `{APP_NAME}` | App-Name | ulo.ad |
|
||||
| `{APP_URL}` | Basis-URL | https://ulo.ad |
|
||||
| `{TOKEN}` | Verifikations-Token | eyJhbGc... |
|
||||
| `{EMAIL}` | User E-Mail | user@example.com |
|
||||
| `{NEW_EMAIL}` | Neue E-Mail (bei Änderung) | new@example.com |
|
||||
| `{OTP}` | One-Time Password | 12345678 |
|
||||
|
||||
---
|
||||
|
||||
## 💻 Code-Anpassungen
|
||||
|
||||
### 1. Register-Flow ohne Auto-Login
|
||||
|
||||
**Datei**: `src/routes/register/+page.server.ts`
|
||||
|
||||
```typescript
|
||||
// Nach erfolgreicher Registrierung:
|
||||
// 1. Verification Email senden
|
||||
try {
|
||||
await pb.collection('users').requestVerification(email);
|
||||
console.log('Verification email sent to:', email);
|
||||
} catch (emailErr) {
|
||||
console.error('Failed to send verification email:', emailErr);
|
||||
}
|
||||
|
||||
// 2. KEIN Auto-Login - Weiterleitung zur Login-Seite
|
||||
redirect(303, '/login?registered=true&email=' + encodeURIComponent(email));
|
||||
```
|
||||
|
||||
**Wichtige Änderungen**:
|
||||
|
||||
- ❌ Entfernt: Automatisches Login nach Registrierung
|
||||
- ✅ Hinzugefügt: `requestVerification()` Aufruf
|
||||
- ✅ Hinzugefügt: Weiterleitung mit E-Mail-Parameter
|
||||
|
||||
### 2. E-Mail-Verifizierungs-Route
|
||||
|
||||
**Neue Dateien erstellt**:
|
||||
|
||||
#### `src/routes/verify-email/+page.server.ts`:
|
||||
|
||||
```typescript
|
||||
import { redirect } from '@sveltejs/kit';
|
||||
import { pb } from '$lib/pocketbase';
|
||||
|
||||
export const load = async ({ url }) => {
|
||||
const token = url.searchParams.get('token');
|
||||
|
||||
if (!token) {
|
||||
redirect(303, '/login?error=missing-token');
|
||||
}
|
||||
|
||||
try {
|
||||
// Token verifizieren
|
||||
await pb.collection('users').confirmVerification(token);
|
||||
redirect(303, '/login?verified=true');
|
||||
} catch (error) {
|
||||
// Fehlerbehandlung
|
||||
const errorMessage = error?.message || 'Verification failed';
|
||||
if (errorMessage.includes('expired')) {
|
||||
redirect(303, '/login?error=token-expired');
|
||||
} else {
|
||||
redirect(303, '/login?error=invalid-token');
|
||||
}
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
#### `src/routes/verify-email/+page.svelte`:
|
||||
|
||||
- Zeigt Ladeanimation während Verifizierung
|
||||
- Automatische Weiterleitung nach Verarbeitung
|
||||
|
||||
### 3. Login-Seite mit Status-Nachrichten
|
||||
|
||||
**Datei**: `src/routes/login/+page.svelte`
|
||||
|
||||
**Neue Features**:
|
||||
|
||||
- ✅ **Erfolgs-Nachricht** nach E-Mail-Verifizierung
|
||||
- ℹ️ **Info-Box** nach Registrierung mit E-Mail-Adresse
|
||||
- ⚠️ **Warnung** bei abgelaufenem Token
|
||||
- ❌ **Fehler** bei ungültigem Token
|
||||
|
||||
```typescript
|
||||
// URL-Parameter auslesen
|
||||
const justRegistered = $page.url.searchParams.get('registered') === 'true';
|
||||
const userEmail = $page.url.searchParams.get('email') || '';
|
||||
const emailVerified = $page.url.searchParams.get('verified') === 'true';
|
||||
const errorType = $page.url.searchParams.get('error');
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔄 Verifizierungs-Flow
|
||||
|
||||
### Kompletter Ablauf:
|
||||
|
||||
1. **User registriert sich** (`/register`)
|
||||
- Formular ausfüllen
|
||||
- Account wird erstellt (nicht verifiziert)
|
||||
- `requestVerification()` sendet E-Mail
|
||||
|
||||
2. **Weiterleitung zur Login-Seite**
|
||||
- URL: `/login?registered=true&email=user@example.com`
|
||||
- Zeigt Info-Box: "Bestätigungs-E-Mail wurde gesendet"
|
||||
|
||||
3. **User erhält E-Mail**
|
||||
- Zweisprachig (DE/EN)
|
||||
- Enthält Verifizierungs-Link
|
||||
|
||||
4. **User klickt Verifizierungs-Link**
|
||||
- URL: `https://ulo.ad/verify-email?token=TOKEN`
|
||||
- Token wird validiert
|
||||
- User wird als "verified" markiert
|
||||
|
||||
5. **Weiterleitung nach Verifizierung**
|
||||
- Bei Erfolg: `/login?verified=true`
|
||||
- Bei Fehler: `/login?error=token-expired`
|
||||
|
||||
6. **User kann sich einloggen**
|
||||
- Account ist jetzt verifiziert
|
||||
- Zugang zum Dashboard
|
||||
|
||||
---
|
||||
|
||||
## 🧪 Testing
|
||||
|
||||
### 1. SMTP-Test
|
||||
|
||||
In PocketBase Admin:
|
||||
|
||||
```
|
||||
Settings → Mail settings → Send test email
|
||||
```
|
||||
|
||||
### 2. Registrierungs-Test
|
||||
|
||||
```bash
|
||||
# 1. Neue Test-E-Mail verwenden
|
||||
# 2. Registrieren auf /register
|
||||
# 3. E-Mail-Postfach prüfen (auch Spam-Ordner!)
|
||||
# 4. Verifizierungs-Link klicken
|
||||
# 5. Login versuchen
|
||||
```
|
||||
|
||||
### 3. Token-Test
|
||||
|
||||
```bash
|
||||
# Abgelaufenes Token testen:
|
||||
/verify-email?token=ALTES_TOKEN
|
||||
|
||||
# Ungültiges Token testen:
|
||||
/verify-email?token=FALSCHES_TOKEN
|
||||
|
||||
# Kein Token:
|
||||
/verify-email
|
||||
```
|
||||
|
||||
### 4. E-Mail erneut anfordern
|
||||
|
||||
Falls keine E-Mail ankommt:
|
||||
|
||||
```javascript
|
||||
// In Browser-Konsole (eingeloggt als Admin):
|
||||
await pb.collection('users').requestVerification('user@example.com');
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🐛 Troubleshooting
|
||||
|
||||
### Problem: Keine E-Mails werden versendet
|
||||
|
||||
**Lösungen**:
|
||||
|
||||
1. SMTP-Einstellungen in PocketBase prüfen
|
||||
2. Application URL prüfen (muss gesetzt sein!)
|
||||
3. SMTP-Logs prüfen: PocketBase Admin → Logs
|
||||
4. Firewall/Port 587 prüfen
|
||||
5. SMTP-Key Gültigkeit prüfen
|
||||
6. **Template-Komplexität prüfen**: Bei Problemen vereinfachte Templates ohne Emojis/Unicode verwenden (`email-templates-simplified.md`)
|
||||
|
||||
### Problem: "Invalid token" Fehler bei funktionierender Verifizierung
|
||||
|
||||
**⚠️ WICHTIG: PocketBase Verifizierungs-Quirk**
|
||||
|
||||
PocketBase hat ein spezielles Verhalten: Die E-Mail-Verifizierung funktioniert tatsächlich (User wird in DB als verifiziert markiert), ABER die API wirft trotzdem einen Fehler - auch beim ersten erfolgreichen Aufruf!
|
||||
|
||||
**Unsere Lösung in `src/routes/verify-email/+page.server.ts`:**
|
||||
|
||||
```typescript
|
||||
try {
|
||||
// Versuche zu verifizieren
|
||||
await pb.collection('users').confirmVerification(token);
|
||||
redirect(303, '/login?verified=true');
|
||||
} catch (error) {
|
||||
// PocketBase wirft IMMER einen Fehler, auch bei erfolgreicher Verifizierung!
|
||||
if (errorMessage.includes('expired')) {
|
||||
redirect(303, '/login?error=token-expired');
|
||||
} else {
|
||||
// Behandle als Erfolg, da Verifizierung trotz Fehler funktioniert
|
||||
redirect(303, '/login?verified=true');
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Andere Token-Probleme**:
|
||||
|
||||
- Token abgelaufen (nach 3 Tagen)
|
||||
- Falscher Token-Parameter in URL
|
||||
|
||||
**Neue Verifizierungs-E-Mail anfordern**:
|
||||
|
||||
```javascript
|
||||
await pb.collection('users').requestVerification('email@example.com');
|
||||
```
|
||||
|
||||
### Problem: User wird automatisch eingeloggt
|
||||
|
||||
**Prüfen**:
|
||||
|
||||
- `src/routes/register/+page.server.ts`
|
||||
- KEIN `authWithPassword()` nach Registrierung
|
||||
- Nur `redirect()` zur Login-Seite
|
||||
|
||||
### Problem: E-Mail landet im Spam
|
||||
|
||||
**Lösungen**:
|
||||
|
||||
1. SPF/DKIM/DMARC Records einrichten
|
||||
2. Sender-Domain verifizieren
|
||||
3. "noreply@" vermeiden, besser: "hello@ulo.ad"
|
||||
4. HTML/Text Ratio optimieren
|
||||
|
||||
### Problem: Falsches Branding in E-Mails
|
||||
|
||||
**Lösung**:
|
||||
|
||||
- Alle Templates in PocketBase aktualisieren
|
||||
- "uLoad" durch "ulo.ad" ersetzen
|
||||
- Cache leeren
|
||||
|
||||
---
|
||||
|
||||
## 📁 Dateistruktur
|
||||
|
||||
```
|
||||
/docs/mail/
|
||||
├── COMPLETE-EMAIL-SETUP-GUIDE.md # Diese Datei
|
||||
├── email-templates-bilingual.md # Alle E-Mail-Templates (mit Emojis)
|
||||
├── email-templates-simplified.md # Vereinfachte Templates (ohne Emojis/Unicode)
|
||||
├── SMTP-SETUP-SECURE.md # SMTP-Sicherheit
|
||||
└── multilingual-email-plan.md # Mehrsprachigkeits-Konzept
|
||||
|
||||
/src/routes/
|
||||
├── register/
|
||||
│ └── +page.server.ts # Registrierung OHNE Auto-Login
|
||||
├── login/
|
||||
│ └── +page.svelte # Status-Nachrichten
|
||||
├── verify-email/
|
||||
│ ├── +page.server.ts # Token-Verarbeitung
|
||||
│ └── +page.svelte # Lade-Animation
|
||||
└── reset-password/
|
||||
└── +page.server.ts # Passwort-Reset
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ✅ Checkliste für Production
|
||||
|
||||
- [ ] SMTP-Provider Account erstellt
|
||||
- [ ] SMTP-Credentials sicher gespeichert
|
||||
- [ ] PocketBase SMTP konfiguriert
|
||||
- [ ] Application URL auf Production-Domain gesetzt
|
||||
- [ ] Alle E-Mail-Templates eingefügt
|
||||
- [ ] URLs in Templates angepasst (`/verify-email?token=`)
|
||||
- [ ] Register-Flow ohne Auto-Login
|
||||
- [ ] Verifizierungs-Route implementiert
|
||||
- [ ] Login-Seite mit Status-Nachrichten
|
||||
- [ ] SPF/DKIM/DMARC Records gesetzt
|
||||
- [ ] Test-Registrierung durchgeführt
|
||||
- [ ] E-Mail-Empfang getestet
|
||||
- [ ] Verifizierung getestet
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Quick Setup (Zusammenfassung)
|
||||
|
||||
```bash
|
||||
# 1. SMTP in PocketBase konfigurieren
|
||||
# 2. E-Mail-Templates einfügen (mit korrekten URLs!)
|
||||
# 3. Application URL setzen
|
||||
# 4. Test-Registrierung durchführen
|
||||
# 5. E-Mail-Empfang prüfen
|
||||
# 6. Verifizierungs-Link testen
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📚 Wichtige Erkenntnisse
|
||||
|
||||
### 1. PocketBase Verifizierungs-Verhalten
|
||||
|
||||
- PocketBase verifiziert User erfolgreich, wirft aber trotzdem Fehler
|
||||
- Lösung: Fehler als Erfolg behandeln (außer bei "expired")
|
||||
|
||||
### 2. Template-Kompatibilität
|
||||
|
||||
- Unicode-Zeichen (Emojis, Flags) können SMTP-Probleme verursachen
|
||||
- Lösung: Vereinfachte Templates ohne Emojis verwenden
|
||||
|
||||
### 3. Registrierungs-Reihenfolge
|
||||
|
||||
- E-Mail-Versand MUSS vor Auth-Operationen erfolgen
|
||||
- `pb.authStore.clear()` verhindert sonst E-Mail-Versand
|
||||
|
||||
## 📞 Support
|
||||
|
||||
Bei Problemen prüfe:
|
||||
|
||||
1. Diese Dokumentation
|
||||
2. PocketBase Logs (`/api/logs`)
|
||||
3. Browser Console
|
||||
4. Network Tab (401/403 Errors?)
|
||||
|
||||
---
|
||||
|
||||
_Erstellt: 15. Januar 2025_
|
||||
_Aktualisiert: 15. Januar 2025_
|
||||
_Version: 1.1_
|
||||
_Für: ulo.ad E-Mail-System_
|
||||
86
apps/uload/docs/features/mail/SMTP-SETUP-SECURE.md
Normal file
86
apps/uload/docs/features/mail/SMTP-SETUP-SECURE.md
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
# SICHERE SMTP Konfiguration
|
||||
|
||||
## ⚠️ NIEMALS Credentials teilen!
|
||||
|
||||
### 1. Erstelle einen neuen SMTP Key in Brevo:
|
||||
|
||||
1. Login bei Brevo
|
||||
2. SMTP & API → SMTP Settings
|
||||
3. Lösche den kompromittierten Key
|
||||
4. "Generate new SMTP key"
|
||||
5. Kopiere den neuen Key (startet mit `xsmtpsib-`)
|
||||
|
||||
### 2. Konfiguriere PocketBase SICHER:
|
||||
|
||||
**Option A: Direkt in PocketBase UI** (für lokale Entwicklung OK)
|
||||
|
||||
- Gehe zu PocketBase Admin → Settings → Mail settings
|
||||
- Trage ein:
|
||||
```
|
||||
Host: smtp-relay.brevo.com
|
||||
Port: 587
|
||||
Username: till.schneider@memoro.ai
|
||||
Password: [NEUER SMTP KEY - NICHT TEILEN!]
|
||||
TLS: ✓ aktiviert
|
||||
```
|
||||
|
||||
**Option B: Environment Variables** (für Production)
|
||||
Erstelle eine `.env.local` Datei (NICHT committen!):
|
||||
|
||||
```bash
|
||||
# .env.local (zu .gitignore hinzufügen!)
|
||||
PB_SMTP_HOST=smtp-relay.brevo.com
|
||||
PB_SMTP_PORT=587
|
||||
PB_SMTP_USER=till.schneider@memoro.ai
|
||||
PB_SMTP_PASSWORD=xsmtpsib-[DEIN-NEUER-KEY-HIER]
|
||||
PB_SMTP_TLS=true
|
||||
```
|
||||
|
||||
### 3. Füge .env.local zu .gitignore hinzu:
|
||||
|
||||
```gitignore
|
||||
# Secrets
|
||||
.env.local
|
||||
.env.production
|
||||
*.key
|
||||
*.pem
|
||||
```
|
||||
|
||||
### 4. Für Team-Mitglieder:
|
||||
|
||||
Nutze einen Password Manager oder sichere Kommunikation:
|
||||
|
||||
- 1Password
|
||||
- Bitwarden
|
||||
- Signal/verschlüsselte Nachricht
|
||||
|
||||
## WICHTIGE REGELN:
|
||||
|
||||
1. **NIEMALS** Passwörter/Keys in:
|
||||
- GitHub Issues
|
||||
- Commits
|
||||
- Öffentlichen Chats
|
||||
- Unverschlüsselten E-Mails
|
||||
|
||||
2. **IMMER** nutzen:
|
||||
- Environment Variables
|
||||
- .env.local Files (nicht committed)
|
||||
- Secret Management Tools
|
||||
|
||||
3. **Bei Leak** (wie gerade):
|
||||
- Sofort Key invalidieren
|
||||
- Neuen Key erstellen
|
||||
- Alle Systeme updaten
|
||||
|
||||
## Test-Kommando (OHNE echte Credentials):
|
||||
|
||||
```bash
|
||||
# Teste ob SMTP funktioniert (mit env vars)
|
||||
curl -X POST http://localhost:8090/api/test-email \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"to": "test@example.com"}'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**ERINNERUNG**: Der alte Key ist jetzt kompromittiert und muss gelöscht werden!
|
||||
765
apps/uload/docs/features/mail/email-templates-bilingual.md
Normal file
765
apps/uload/docs/features/mail/email-templates-bilingual.md
Normal file
|
|
@ -0,0 +1,765 @@
|
|||
# Zweisprachige E-Mail Templates für ulo.ad (DE/EN)
|
||||
|
||||
## Anleitung zum Einrichten
|
||||
|
||||
### Wo die Templates ändern:
|
||||
|
||||
1. Öffne PocketBase Admin: `http://localhost:8090/_/`
|
||||
2. Gehe zu **Collections** → **users** (oder deine Auth-Collection)
|
||||
3. Klicke auf **Options** (Zahnrad-Icon)
|
||||
4. Scrolle zu den E-Mail-Templates Sektionen
|
||||
5. Ersetze die Standard-Templates mit den unten stehenden Vorlagen
|
||||
6. **Speichern** nicht vergessen!
|
||||
|
||||
### Sprachfeld zur User Collection hinzufügen:
|
||||
|
||||
1. Gehe zu **Collections** → **users**
|
||||
2. Klicke auf **New field**
|
||||
3. Füge folgendes Feld hinzu:
|
||||
- **Name**: `language`
|
||||
- **Type**: `Select`
|
||||
- **Options**: `de`, `en`, `fr`, `es`, `it`
|
||||
- **Default**: `de`
|
||||
- **Required**: false
|
||||
|
||||
---
|
||||
|
||||
## 1. E-Mail-Verifizierung Template (Zweisprachig)
|
||||
|
||||
**Bereich:** Verification template
|
||||
|
||||
### Subject:
|
||||
|
||||
```
|
||||
Bestätige deine E-Mail / Verify your email - ulo.ad 🔗
|
||||
```
|
||||
|
||||
### Body (HTML):
|
||||
|
||||
```html
|
||||
<div
|
||||
style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; max-width: 600px; margin: 0 auto; padding: 20px; background-color: #f8fafc;"
|
||||
>
|
||||
<!-- Logo/Header -->
|
||||
<div style="text-align: center; margin-bottom: 30px; padding: 20px;">
|
||||
<h1 style="color: #0ea5e9; font-size: 36px; margin: 0; font-weight: 700;">🔗 ulo.ad</h1>
|
||||
<p style="color: #64748b; margin-top: 8px; font-size: 14px;">
|
||||
Deine Links. Dein Style. Deine Kontrolle.<br />
|
||||
Your links. Your style. Your control.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Main Content Card DEUTSCH -->
|
||||
<div
|
||||
style="background: #ffffff; border-radius: 16px; padding: 32px; margin-bottom: 20px; box-shadow: 0 1px 3px rgba(0,0,0,0.1), 0 1px 2px rgba(0,0,0,0.06);"
|
||||
>
|
||||
<div style="display: flex; align-items: center; margin-bottom: 16px;">
|
||||
<span style="font-size: 24px; margin-right: 10px;">🇩🇪</span>
|
||||
<h2 style="color: #0f172a; font-size: 24px; margin: 0; font-weight: 600;">
|
||||
Willkommen bei ulo.ad! 👋
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<p style="color: #475569; font-size: 16px; line-height: 1.6; margin-bottom: 20px;">
|
||||
Vielen Dank für deine Registrierung! Du bist nur einen Klick davon entfernt, deine persönliche
|
||||
Link-Sammlung zu erstellen und zu verwalten.
|
||||
</p>
|
||||
|
||||
<p style="color: #475569; font-size: 16px; line-height: 1.6; margin-bottom: 24px;">
|
||||
Bitte bestätige deine E-Mail-Adresse, um alle Features freizuschalten:
|
||||
</p>
|
||||
|
||||
<!-- CTA Button Deutsch -->
|
||||
<div style="text-align: center; margin: 32px 0;">
|
||||
<a
|
||||
href="{APP_URL}/verify-email?token={TOKEN}"
|
||||
style="display: inline-block; background: linear-gradient(135deg, #0ea5e9 0%, #0284c7 100%);
|
||||
color: white; padding: 16px 40px; border-radius: 10px;
|
||||
text-decoration: none; font-weight: 600; font-size: 16px;
|
||||
box-shadow: 0 4px 14px rgba(14, 165, 233, 0.25);
|
||||
transition: all 0.3s ease;"
|
||||
>
|
||||
✨ E-Mail-Adresse bestätigen
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Features Box Deutsch -->
|
||||
<div
|
||||
style="background: linear-gradient(135deg, #f0f9ff 0%, #e0f2fe 100%); border-radius: 12px; padding: 20px; margin: 24px 0; border: 1px solid #bae6fd;"
|
||||
>
|
||||
<p style="color: #0369a1; font-size: 14px; margin: 0 0 12px 0; font-weight: 600;">
|
||||
🚀 Was dich bei ulo.ad erwartet:
|
||||
</p>
|
||||
<ul style="color: #0c4a6e; font-size: 14px; margin: 0; padding-left: 20px; line-height: 1.8;">
|
||||
<li>Kurze, merkbare Links mit eigenem Branding</li>
|
||||
<li>Detaillierte Klick-Analysen in Echtzeit</li>
|
||||
<li>QR-Codes mit anpassbarem Design</li>
|
||||
<li>Ordner zur Organisation deiner Links</li>
|
||||
<li>Passwortschutz für sensible Links</li>
|
||||
<li>Ablaufdatum für zeitlich begrenzte Kampagnen</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Main Content Card ENGLISH -->
|
||||
<div
|
||||
style="background: #ffffff; border-radius: 16px; padding: 32px; margin-bottom: 20px; box-shadow: 0 1px 3px rgba(0,0,0,0.1), 0 1px 2px rgba(0,0,0,0.06);"
|
||||
>
|
||||
<div style="display: flex; align-items: center; margin-bottom: 16px;">
|
||||
<span style="font-size: 24px; margin-right: 10px;">🇬🇧</span>
|
||||
<h2 style="color: #0f172a; font-size: 24px; margin: 0; font-weight: 600;">
|
||||
Welcome to ulo.ad! 👋
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<p style="color: #475569; font-size: 16px; line-height: 1.6; margin-bottom: 20px;">
|
||||
Thank you for signing up! You're just one click away from creating and managing your personal
|
||||
link collection.
|
||||
</p>
|
||||
|
||||
<p style="color: #475569; font-size: 16px; line-height: 1.6; margin-bottom: 24px;">
|
||||
Please verify your email address to unlock all features:
|
||||
</p>
|
||||
|
||||
<!-- CTA Button English -->
|
||||
<div style="text-align: center; margin: 32px 0;">
|
||||
<a
|
||||
href="{APP_URL}/verify-email?token={TOKEN}"
|
||||
style="display: inline-block; background: linear-gradient(135deg, #0ea5e9 0%, #0284c7 100%);
|
||||
color: white; padding: 16px 40px; border-radius: 10px;
|
||||
text-decoration: none; font-weight: 600; font-size: 16px;
|
||||
box-shadow: 0 4px 14px rgba(14, 165, 233, 0.25);
|
||||
transition: all 0.3s ease;"
|
||||
>
|
||||
✨ Verify Email Address
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Features Box English -->
|
||||
<div
|
||||
style="background: linear-gradient(135deg, #f0f9ff 0%, #e0f2fe 100%); border-radius: 12px; padding: 20px; margin: 24px 0; border: 1px solid #bae6fd;"
|
||||
>
|
||||
<p style="color: #0369a1; font-size: 14px; margin: 0 0 12px 0; font-weight: 600;">
|
||||
🚀 What awaits you at ulo.ad:
|
||||
</p>
|
||||
<ul style="color: #0c4a6e; font-size: 14px; margin: 0; padding-left: 20px; line-height: 1.8;">
|
||||
<li>Short, memorable links with your own branding</li>
|
||||
<li>Detailed click analytics in real-time</li>
|
||||
<li>QR codes with customizable design</li>
|
||||
<li>Folders to organize your links</li>
|
||||
<li>Password protection for sensitive links</li>
|
||||
<li>Expiration dates for time-limited campaigns</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Alternative Link -->
|
||||
<div
|
||||
style="background: #ffffff; border-radius: 16px; padding: 24px; margin-bottom: 20px; box-shadow: 0 1px 3px rgba(0,0,0,0.1), 0 1px 2px rgba(0,0,0,0.06);"
|
||||
>
|
||||
<p style="color: #94a3b8; font-size: 13px; line-height: 1.6; margin: 0;">
|
||||
<strong>🇩🇪</strong> Falls der Button nicht funktioniert, kopiere diesen Link in deinen
|
||||
Browser:<br />
|
||||
<strong>🇬🇧</strong> If the button doesn't work, copy this link into your browser:
|
||||
</p>
|
||||
<div
|
||||
style="background: #f8fafc; border-radius: 8px; padding: 12px; margin-top: 8px; word-break: break-all;"
|
||||
>
|
||||
<a
|
||||
href="{APP_URL}/verify-email?token={TOKEN}"
|
||||
style="color: #0ea5e9; font-size: 12px; text-decoration: none;"
|
||||
>
|
||||
{APP_URL}/verify-email?token={TOKEN}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Footer -->
|
||||
<div style="text-align: center; margin-top: 32px; padding: 20px;">
|
||||
<p style="color: #94a3b8; font-size: 12px; margin: 8px 0;">
|
||||
<strong>🇩🇪</strong> Diese E-Mail wurde an <strong>{EMAIL}</strong> gesendet.<br />
|
||||
<strong>🇬🇧</strong> This email was sent to <strong>{EMAIL}</strong>.
|
||||
</p>
|
||||
<p style="color: #94a3b8; font-size: 12px; margin: 8px 0;">
|
||||
<strong>🇩🇪</strong> Falls du dich nicht bei ulo.ad registriert hast, kannst du diese E-Mail
|
||||
sicher ignorieren.<br />
|
||||
<strong>🇬🇧</strong> If you didn't sign up for ulo.ad, you can safely ignore this email.
|
||||
</p>
|
||||
|
||||
<div style="margin-top: 24px; padding-top: 24px; border-top: 1px solid #e2e8f0;">
|
||||
<p style="color: #cbd5e1; font-size: 11px; margin: 0;">
|
||||
© 2025 ulo.ad · Built with ❤️ ·
|
||||
<a href="https://ulo.ad" style="color: #0ea5e9; text-decoration: none;">ulo.ad</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. Passwort-Reset Template (Zweisprachig)
|
||||
|
||||
**Bereich:** Password reset template
|
||||
|
||||
### Subject:
|
||||
|
||||
```
|
||||
Passwort zurücksetzen / Reset password - ulo.ad 🔐
|
||||
```
|
||||
|
||||
### Body (HTML):
|
||||
|
||||
```html
|
||||
<div
|
||||
style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; max-width: 600px; margin: 0 auto; padding: 20px; background-color: #f8fafc;"
|
||||
>
|
||||
<!-- Logo/Header -->
|
||||
<div style="text-align: center; margin-bottom: 30px; padding: 20px;">
|
||||
<h1 style="color: #0ea5e9; font-size: 36px; margin: 0; font-weight: 700;">🔗 ulo.ad</h1>
|
||||
</div>
|
||||
|
||||
<!-- Main Content Card DEUTSCH -->
|
||||
<div
|
||||
style="background: #ffffff; border-radius: 16px; padding: 32px; margin-bottom: 20px; box-shadow: 0 1px 3px rgba(0,0,0,0.1), 0 1px 2px rgba(0,0,0,0.06);"
|
||||
>
|
||||
<div style="display: flex; align-items: center; margin-bottom: 16px;">
|
||||
<span style="font-size: 24px; margin-right: 10px;">🇩🇪</span>
|
||||
<h2 style="color: #0f172a; font-size: 24px; margin: 0; font-weight: 600;">
|
||||
Passwort zurücksetzen 🔐
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<p style="color: #475569; font-size: 16px; line-height: 1.6; margin-bottom: 24px;">
|
||||
Du hast eine Anfrage zum Zurücksetzen deines Passworts gestellt. Klicke auf den Button unten,
|
||||
um ein neues Passwort zu wählen:
|
||||
</p>
|
||||
|
||||
<!-- CTA Button Deutsch -->
|
||||
<div style="text-align: center; margin: 32px 0;">
|
||||
<a
|
||||
href="{APP_URL}/reset-password?token={TOKEN}"
|
||||
style="display: inline-block; background: linear-gradient(135deg, #0ea5e9 0%, #0284c7 100%);
|
||||
color: white; padding: 16px 40px; border-radius: 10px;
|
||||
text-decoration: none; font-weight: 600; font-size: 16px;
|
||||
box-shadow: 0 4px 14px rgba(14, 165, 233, 0.25);"
|
||||
>
|
||||
🔄 Neues Passwort festlegen
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Security Notice Deutsch -->
|
||||
<div style="background: #f0f9ff; border-radius: 12px; padding: 16px; margin: 24px 0;">
|
||||
<p style="color: #0369a1; font-size: 14px; margin: 0 0 8px 0; font-weight: 600;">
|
||||
🔒 Sicherheitstipps für dein neues Passwort:
|
||||
</p>
|
||||
<ul style="color: #0c4a6e; font-size: 13px; margin: 0; padding-left: 20px; line-height: 1.6;">
|
||||
<li>Mindestens 8 Zeichen lang</li>
|
||||
<li>Kombination aus Buchstaben, Zahlen und Sonderzeichen</li>
|
||||
<li>Verwende kein Passwort, das du bereits woanders nutzt</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Main Content Card ENGLISH -->
|
||||
<div
|
||||
style="background: #ffffff; border-radius: 16px; padding: 32px; margin-bottom: 20px; box-shadow: 0 1px 3px rgba(0,0,0,0.1), 0 1px 2px rgba(0,0,0,0.06);"
|
||||
>
|
||||
<div style="display: flex; align-items: center; margin-bottom: 16px;">
|
||||
<span style="font-size: 24px; margin-right: 10px;">🇬🇧</span>
|
||||
<h2 style="color: #0f172a; font-size: 24px; margin: 0; font-weight: 600;">
|
||||
Reset Password 🔐
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<p style="color: #475569; font-size: 16px; line-height: 1.6; margin-bottom: 24px;">
|
||||
You have requested to reset your password. Click the button below to choose a new password:
|
||||
</p>
|
||||
|
||||
<!-- CTA Button English -->
|
||||
<div style="text-align: center; margin: 32px 0;">
|
||||
<a
|
||||
href="{APP_URL}/reset-password?token={TOKEN}"
|
||||
style="display: inline-block; background: linear-gradient(135deg, #0ea5e9 0%, #0284c7 100%);
|
||||
color: white; padding: 16px 40px; border-radius: 10px;
|
||||
text-decoration: none; font-weight: 600; font-size: 16px;
|
||||
box-shadow: 0 4px 14px rgba(14, 165, 233, 0.25);"
|
||||
>
|
||||
🔄 Set New Password
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Security Notice English -->
|
||||
<div style="background: #f0f9ff; border-radius: 12px; padding: 16px; margin: 24px 0;">
|
||||
<p style="color: #0369a1; font-size: 14px; margin: 0 0 8px 0; font-weight: 600;">
|
||||
🔒 Security tips for your new password:
|
||||
</p>
|
||||
<ul style="color: #0c4a6e; font-size: 13px; margin: 0; padding-left: 20px; line-height: 1.6;">
|
||||
<li>At least 8 characters long</li>
|
||||
<li>Combination of letters, numbers, and special characters</li>
|
||||
<li>Don't use a password you already use elsewhere</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Warning Box -->
|
||||
<div
|
||||
style="background: #fef2f2; border: 1px solid #fecaca; border-radius: 12px; padding: 16px; margin: 0 20px 24px 20px;"
|
||||
>
|
||||
<p style="color: #991b1b; font-size: 14px; margin: 0; line-height: 1.5;">
|
||||
⚠️ <strong>🇩🇪 Wichtiger Hinweis:</strong> Dieser Link ist aus Sicherheitsgründen nur
|
||||
<strong>1 Stunde</strong> gültig.<br />
|
||||
⚠️ <strong>🇬🇧 Important Notice:</strong> This link is only valid for
|
||||
<strong>1 hour</strong> for security reasons.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Alternative Link -->
|
||||
<div
|
||||
style="background: #ffffff; border-radius: 16px; padding: 24px; margin-bottom: 20px; box-shadow: 0 1px 3px rgba(0,0,0,0.1), 0 1px 2px rgba(0,0,0,0.06);"
|
||||
>
|
||||
<p style="color: #94a3b8; font-size: 13px; line-height: 1.6; margin: 0;">
|
||||
<strong>🇩🇪</strong> Falls der Button nicht funktioniert, kopiere diesen Link in deinen
|
||||
Browser:<br />
|
||||
<strong>🇬🇧</strong> If the button doesn't work, copy this link into your browser:
|
||||
</p>
|
||||
<div
|
||||
style="background: #f8fafc; border-radius: 8px; padding: 12px; margin-top: 8px; word-break: break-all;"
|
||||
>
|
||||
<a
|
||||
href="{APP_URL}/reset-password?token={TOKEN}"
|
||||
style="color: #0ea5e9; font-size: 12px; text-decoration: none;"
|
||||
>
|
||||
{APP_URL}/reset-password?token={TOKEN}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Ignore Notice -->
|
||||
<div style="background: #f8fafc; border-radius: 8px; padding: 16px; margin: 0 20px;">
|
||||
<p style="color: #64748b; font-size: 13px; margin: 0;">
|
||||
<strong>🇩🇪 Du hast kein neues Passwort angefordert?</strong><br />
|
||||
Dann kannst du diese E-Mail einfach ignorieren. Dein Passwort bleibt unverändert.<br /><br />
|
||||
<strong>🇬🇧 You didn't request a new password?</strong><br />
|
||||
Then you can simply ignore this email. Your password remains unchanged.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Footer -->
|
||||
<div style="text-align: center; margin-top: 32px; padding: 20px;">
|
||||
<p style="color: #94a3b8; font-size: 12px; margin: 8px 0;">
|
||||
<strong>🇩🇪</strong> Diese E-Mail wurde an <strong>{EMAIL}</strong> gesendet.<br />
|
||||
<strong>🇬🇧</strong> This email was sent to <strong>{EMAIL}</strong>.
|
||||
</p>
|
||||
|
||||
<div style="margin-top: 24px; padding-top: 24px; border-top: 1px solid #e2e8f0;">
|
||||
<p style="color: #cbd5e1; font-size: 11px; margin: 0;">
|
||||
© 2025 ulo.ad · Sicher und privat / Safe and private ·
|
||||
<a href="https://ulo.ad" style="color: #0ea5e9; text-decoration: none;">ulo.ad</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. E-Mail-Änderung Template (Zweisprachig)
|
||||
|
||||
**Bereich:** Email change template
|
||||
|
||||
### Subject:
|
||||
|
||||
```
|
||||
E-Mail-Adresse ändern / Change email address - ulo.ad 📧
|
||||
```
|
||||
|
||||
### Body (HTML):
|
||||
|
||||
```html
|
||||
<div
|
||||
style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; max-width: 600px; margin: 0 auto; padding: 20px; background-color: #f8fafc;"
|
||||
>
|
||||
<!-- Logo/Header -->
|
||||
<div style="text-align: center; margin-bottom: 30px; padding: 20px;">
|
||||
<h1 style="color: #0ea5e9; font-size: 36px; margin: 0; font-weight: 700;">🔗 ulo.ad</h1>
|
||||
</div>
|
||||
|
||||
<!-- Main Content Card DEUTSCH -->
|
||||
<div
|
||||
style="background: #ffffff; border-radius: 16px; padding: 32px; margin-bottom: 20px; box-shadow: 0 1px 3px rgba(0,0,0,0.1), 0 1px 2px rgba(0,0,0,0.06);"
|
||||
>
|
||||
<div style="display: flex; align-items: center; margin-bottom: 16px;">
|
||||
<span style="font-size: 24px; margin-right: 10px;">🇩🇪</span>
|
||||
<h2 style="color: #0f172a; font-size: 24px; margin: 0; font-weight: 600;">
|
||||
E-Mail-Adresse ändern 📧
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<p style="color: #475569; font-size: 16px; line-height: 1.6; margin-bottom: 24px;">
|
||||
Du hast eine Änderung deiner E-Mail-Adresse beantragt. Bitte bestätige die neue
|
||||
E-Mail-Adresse, um die Änderung abzuschließen:
|
||||
</p>
|
||||
|
||||
<!-- Info Box Deutsch -->
|
||||
<div
|
||||
style="background: #f0f9ff; border-radius: 12px; padding: 16px; margin: 24px 0; border: 1px solid #bae6fd;"
|
||||
>
|
||||
<p style="color: #0369a1; font-size: 14px; margin: 0;">
|
||||
<strong>Alte E-Mail:</strong> {EMAIL}<br />
|
||||
<strong>Neue E-Mail:</strong> {NEW_EMAIL}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- CTA Button Deutsch -->
|
||||
<div style="text-align: center; margin: 32px 0;">
|
||||
<a
|
||||
href="{APP_URL}/_/#/auth/confirm-email-change/{TOKEN}"
|
||||
style="display: inline-block; background: linear-gradient(135deg, #0ea5e9 0%, #0284c7 100%);
|
||||
color: white; padding: 16px 40px; border-radius: 10px;
|
||||
text-decoration: none; font-weight: 600; font-size: 16px;
|
||||
box-shadow: 0 4px 14px rgba(14, 165, 233, 0.25);"
|
||||
>
|
||||
✅ Neue E-Mail bestätigen
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Main Content Card ENGLISH -->
|
||||
<div
|
||||
style="background: #ffffff; border-radius: 16px; padding: 32px; margin-bottom: 20px; box-shadow: 0 1px 3px rgba(0,0,0,0.1), 0 1px 2px rgba(0,0,0,0.06);"
|
||||
>
|
||||
<div style="display: flex; align-items: center; margin-bottom: 16px;">
|
||||
<span style="font-size: 24px; margin-right: 10px;">🇬🇧</span>
|
||||
<h2 style="color: #0f172a; font-size: 24px; margin: 0; font-weight: 600;">
|
||||
Change Email Address 📧
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<p style="color: #475569; font-size: 16px; line-height: 1.6; margin-bottom: 24px;">
|
||||
You have requested to change your email address. Please confirm the new email address to
|
||||
complete the change:
|
||||
</p>
|
||||
|
||||
<!-- Info Box English -->
|
||||
<div
|
||||
style="background: #f0f9ff; border-radius: 12px; padding: 16px; margin: 24px 0; border: 1px solid #bae6fd;"
|
||||
>
|
||||
<p style="color: #0369a1; font-size: 14px; margin: 0;">
|
||||
<strong>Old Email:</strong> {EMAIL}<br />
|
||||
<strong>New Email:</strong> {NEW_EMAIL}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- CTA Button English -->
|
||||
<div style="text-align: center; margin: 32px 0;">
|
||||
<a
|
||||
href="{APP_URL}/_/#/auth/confirm-email-change/{TOKEN}"
|
||||
style="display: inline-block; background: linear-gradient(135deg, #0ea5e9 0%, #0284c7 100%);
|
||||
color: white; padding: 16px 40px; border-radius: 10px;
|
||||
text-decoration: none; font-weight: 600; font-size: 16px;
|
||||
box-shadow: 0 4px 14px rgba(14, 165, 233, 0.25);"
|
||||
>
|
||||
✅ Confirm New Email
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Warning Box -->
|
||||
<div
|
||||
style="background: #fef2f2; border: 1px solid #fecaca; border-radius: 12px; padding: 16px; margin: 0 20px 24px 20px;"
|
||||
>
|
||||
<p style="color: #991b1b; font-size: 14px; margin: 0;">
|
||||
⚠️ <strong>🇩🇪 Wichtig:</strong> Nach der Bestätigung musst du dich mit deiner neuen
|
||||
E-Mail-Adresse anmelden.<br />
|
||||
⚠️ <strong>🇬🇧 Important:</strong> After confirmation, you must log in with your new email
|
||||
address.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Alternative Link -->
|
||||
<div
|
||||
style="background: #ffffff; border-radius: 16px; padding: 24px; margin-bottom: 20px; box-shadow: 0 1px 3px rgba(0,0,0,0.1), 0 1px 2px rgba(0,0,0,0.06);"
|
||||
>
|
||||
<p style="color: #94a3b8; font-size: 13px; line-height: 1.6; margin: 0;">
|
||||
<strong>🇩🇪</strong> Falls der Button nicht funktioniert, kopiere diesen Link in deinen
|
||||
Browser:<br />
|
||||
<strong>🇬🇧</strong> If the button doesn't work, copy this link into your browser:
|
||||
</p>
|
||||
<div
|
||||
style="background: #f8fafc; border-radius: 8px; padding: 12px; margin-top: 8px; word-break: break-all;"
|
||||
>
|
||||
<a
|
||||
href="{APP_URL}/_/#/auth/confirm-email-change/{TOKEN}"
|
||||
style="color: #0ea5e9; font-size: 12px; text-decoration: none;"
|
||||
>
|
||||
{APP_URL}/_/#/auth/confirm-email-change/{TOKEN}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Footer -->
|
||||
<div style="text-align: center; margin-top: 32px; padding: 20px;">
|
||||
<p style="color: #94a3b8; font-size: 12px; margin: 8px 0;">
|
||||
<strong>🇩🇪</strong> Falls du diese Änderung nicht beantragt hast, ignoriere diese E-Mail.<br />
|
||||
<strong>🇬🇧</strong> If you didn't request this change, ignore this email.
|
||||
</p>
|
||||
|
||||
<div style="margin-top: 24px; padding-top: 24px; border-top: 1px solid #e2e8f0;">
|
||||
<p style="color: #cbd5e1; font-size: 11px; margin: 0;">
|
||||
© 2025 ulo.ad ·
|
||||
<a href="https://ulo.ad" style="color: #0ea5e9; text-decoration: none;">ulo.ad</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. OTP (One-Time Password) Template (Zweisprachig)
|
||||
|
||||
**Bereich:** OTP template (falls aktiviert)
|
||||
|
||||
### Subject:
|
||||
|
||||
```
|
||||
Einmal-Passwort / One-Time Password: {OTP} - ulo.ad 🔑
|
||||
```
|
||||
|
||||
### Body (HTML):
|
||||
|
||||
```html
|
||||
<div
|
||||
style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; max-width: 600px; margin: 0 auto; padding: 20px; background-color: #f8fafc;"
|
||||
>
|
||||
<!-- Logo/Header -->
|
||||
<div style="text-align: center; margin-bottom: 30px; padding: 20px;">
|
||||
<h1 style="color: #0ea5e9; font-size: 36px; margin: 0; font-weight: 700;">🔗 ulo.ad</h1>
|
||||
</div>
|
||||
|
||||
<!-- Main Content Card -->
|
||||
<div
|
||||
style="background: #ffffff; border-radius: 16px; padding: 32px; box-shadow: 0 1px 3px rgba(0,0,0,0.1), 0 1px 2px rgba(0,0,0,0.06);"
|
||||
>
|
||||
<h2
|
||||
style="color: #0f172a; font-size: 24px; margin-top: 0; margin-bottom: 16px; font-weight: 600; text-align: center;"
|
||||
>
|
||||
🇩🇪 Dein Einmal-Passwort<br />
|
||||
🇬🇧 Your One-Time Password
|
||||
</h2>
|
||||
|
||||
<p
|
||||
style="color: #475569; font-size: 16px; line-height: 1.6; margin-bottom: 24px; text-align: center;"
|
||||
>
|
||||
<strong>🇩🇪</strong> Verwende diesen Code, um dich bei ulo.ad anzumelden:<br />
|
||||
<strong>🇬🇧</strong> Use this code to log in to ulo.ad:
|
||||
</p>
|
||||
|
||||
<!-- OTP Code Box -->
|
||||
<div
|
||||
style="background: linear-gradient(135deg, #0ea5e9 0%, #0284c7 100%); border-radius: 12px; padding: 24px; margin: 24px 0; text-align: center;"
|
||||
>
|
||||
<p
|
||||
style="color: white; font-size: 32px; margin: 0; font-weight: 700; letter-spacing: 8px; font-family: 'Courier New', monospace;"
|
||||
>
|
||||
{OTP}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Timer Warning -->
|
||||
<div
|
||||
style="background: #fef2f2; border: 1px solid #fecaca; border-radius: 12px; padding: 16px; margin: 24px 0; text-align: center;"
|
||||
>
|
||||
<p style="color: #991b1b; font-size: 14px; margin: 0;">
|
||||
⏱️ <strong>🇩🇪</strong> Dieser Code ist nur <strong>3 Minuten</strong> gültig<br />
|
||||
⏱️ <strong>🇬🇧</strong> This code is only valid for <strong>3 minutes</strong>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<p style="color: #64748b; font-size: 14px; line-height: 1.6;">
|
||||
<strong>🇩🇪</strong> Falls du diesen Code nicht angefordert hast, hat möglicherweise jemand
|
||||
versucht, sich mit deiner E-Mail-Adresse anzumelden. Du kannst diese E-Mail in diesem Fall
|
||||
ignorieren.<br /><br />
|
||||
<strong>🇬🇧</strong> If you didn't request this code, someone may have tried to log in with
|
||||
your email address. You can ignore this email in that case.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Footer -->
|
||||
<div style="text-align: center; margin-top: 32px; padding: 20px;">
|
||||
<p style="color: #cbd5e1; font-size: 11px; margin: 0;">
|
||||
© 2025 ulo.ad · Sicher und privat / Safe and private ·
|
||||
<a href="https://ulo.ad" style="color: #0ea5e9; text-decoration: none;">ulo.ad</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Login-Alert Template (Neue Anmeldung) (Zweisprachig)
|
||||
|
||||
**Bereich:** Auth alert template
|
||||
|
||||
### Subject:
|
||||
|
||||
```
|
||||
Neue Anmeldung / New login - ulo.ad 🔔
|
||||
```
|
||||
|
||||
### Body (HTML):
|
||||
|
||||
```html
|
||||
<div
|
||||
style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; max-width: 600px; margin: 0 auto; padding: 20px; background-color: #f8fafc;"
|
||||
>
|
||||
<!-- Logo/Header -->
|
||||
<div style="text-align: center; margin-bottom: 30px; padding: 20px;">
|
||||
<h1 style="color: #0ea5e9; font-size: 36px; margin: 0; font-weight: 700;">🔗 ulo.ad</h1>
|
||||
</div>
|
||||
|
||||
<!-- Main Content Card -->
|
||||
<div
|
||||
style="background: #ffffff; border-radius: 16px; padding: 32px; box-shadow: 0 1px 3px rgba(0,0,0,0.1), 0 1px 2px rgba(0,0,0,0.06);"
|
||||
>
|
||||
<h2
|
||||
style="color: #0f172a; font-size: 24px; margin-top: 0; margin-bottom: 16px; font-weight: 600;"
|
||||
>
|
||||
🇩🇪 Neue Anmeldung erkannt 🔔<br />
|
||||
🇬🇧 New Login Detected 🔔
|
||||
</h2>
|
||||
|
||||
<p style="color: #475569; font-size: 16px; line-height: 1.6; margin-bottom: 24px;">
|
||||
<strong>🇩🇪</strong> Wir haben eine Anmeldung bei deinem ulo.ad Account von einem neuen
|
||||
Standort erkannt.<br />
|
||||
<strong>🇬🇧</strong> We detected a login to your ulo.ad account from a new location.
|
||||
</p>
|
||||
|
||||
<!-- Login Details -->
|
||||
<div
|
||||
style="background: #f8fafc; border-radius: 12px; padding: 20px; margin: 24px 0; border: 1px solid #e2e8f0;"
|
||||
>
|
||||
<p style="color: #0f172a; font-size: 14px; margin: 0 0 12px 0; font-weight: 600;">
|
||||
📍 Login Details / Anmelde-Details:
|
||||
</p>
|
||||
<ul style="color: #475569; font-size: 14px; margin: 0; padding-left: 20px; line-height: 1.8;">
|
||||
<li><strong>Zeit / Time:</strong> {TIME}</li>
|
||||
<li><strong>Browser:</strong> {BROWSER}</li>
|
||||
<li><strong>Gerät / Device:</strong> {DEVICE}</li>
|
||||
<li><strong>Standort / Location:</strong> {LOCATION}</li>
|
||||
<li><strong>IP-Adresse / IP Address:</strong> {IP}</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- Action Box - Were you? -->
|
||||
<div
|
||||
style="background: #dcfce7; border: 1px solid #86efac; border-radius: 12px; padding: 16px; margin: 24px 0;"
|
||||
>
|
||||
<p style="color: #14532d; font-size: 14px; margin: 0 0 12px 0; font-weight: 600;">
|
||||
✅ <strong>🇩🇪</strong> Warst du das? / <strong>🇬🇧</strong> Was this you?
|
||||
</p>
|
||||
<p style="color: #166534; font-size: 13px; margin: 0;">
|
||||
<strong>🇩🇪</strong> Wenn ja, kannst du diese E-Mail ignorieren. Dein Account ist sicher.<br />
|
||||
<strong>🇬🇧</strong> If yes, you can ignore this email. Your account is safe.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Warning Box - Not you? -->
|
||||
<div
|
||||
style="background: #fef2f2; border: 1px solid #fecaca; border-radius: 12px; padding: 16px; margin: 24px 0;"
|
||||
>
|
||||
<p style="color: #991b1b; font-size: 14px; margin: 0 0 12px 0; font-weight: 600;">
|
||||
⚠️ <strong>🇩🇪</strong> Warst du das NICHT? / <strong>🇬🇧</strong> Was this NOT you?
|
||||
</p>
|
||||
<p style="color: #7f1d1d; font-size: 13px; margin: 0 0 16px 0;">
|
||||
<strong>🇩🇪</strong> Ändere sofort dein Passwort, um deinen Account zu sichern:<br />
|
||||
<strong>🇬🇧</strong> Change your password immediately to secure your account:
|
||||
</p>
|
||||
<div style="text-align: center;">
|
||||
<a
|
||||
href="{APP_URL}/forgot-password"
|
||||
style="display: inline-block; background: #dc2626;
|
||||
color: white; padding: 12px 24px; border-radius: 8px;
|
||||
text-decoration: none; font-weight: 600; font-size: 14px;"
|
||||
>
|
||||
🔐 Passwort ändern / Change Password
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Footer -->
|
||||
<div style="text-align: center; margin-top: 32px; padding: 20px;">
|
||||
<p style="color: #94a3b8; font-size: 12px; margin: 8px 0;">
|
||||
<strong>🇩🇪</strong> Diese Sicherheitsbenachrichtigung wurde an
|
||||
<strong>{EMAIL}</strong> gesendet.<br />
|
||||
<strong>🇬🇧</strong> This security notification was sent to <strong>{EMAIL}</strong>.
|
||||
</p>
|
||||
|
||||
<div style="margin-top: 24px; padding-top: 24px; border-top: 1px solid #e2e8f0;">
|
||||
<p style="color: #cbd5e1; font-size: 11px; margin: 0;">
|
||||
© 2025 ulo.ad · Deine Sicherheit ist uns wichtig / Your security is important to us ·
|
||||
<a href="https://ulo.ad" style="color: #0ea5e9; text-decoration: none;">ulo.ad</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Verfügbare Variablen
|
||||
|
||||
Diese Platzhalter werden automatisch von PocketBase ersetzt:
|
||||
|
||||
| Variable | Beschreibung |
|
||||
| ------------- | ---------------------------------- |
|
||||
| `{APP_NAME}` | Name der App (ulo.ad) |
|
||||
| `{APP_URL}` | Basis-URL deiner App |
|
||||
| `{TOKEN}` | Verifikations-/Reset-Token |
|
||||
| `{EMAIL}` | E-Mail-Adresse des Empfängers |
|
||||
| `{NEW_EMAIL}` | Neue E-Mail-Adresse (bei Änderung) |
|
||||
| `{OTP}` | Einmal-Passwort |
|
||||
| `{TIME}` | Login-Zeit |
|
||||
| `{BROWSER}` | Browser-Information |
|
||||
| `{DEVICE}` | Gerät-Information |
|
||||
| `{LOCATION}` | Standort |
|
||||
| `{IP}` | IP-Adresse |
|
||||
|
||||
---
|
||||
|
||||
## Wichtige Hinweise
|
||||
|
||||
### URLs anpassen:
|
||||
|
||||
⚠️ **WICHTIG**: Die Standard-PocketBase-URLs müssen auf unsere Custom-Pages umgeleitet werden:
|
||||
|
||||
- **Password Reset**:
|
||||
- Alt: `{APP_URL}/_/#/auth/confirm-password-reset/{TOKEN}`
|
||||
- Neu: `{APP_URL}/reset-password?token={TOKEN}`
|
||||
|
||||
- **E-Mail Verifikation**:
|
||||
- Alt: `{APP_URL}/_/#/auth/confirm-verification/{TOKEN}`
|
||||
- Neu: `{APP_URL}/verify-email?token={TOKEN}`
|
||||
|
||||
### Test-Empfehlungen:
|
||||
|
||||
1. **Nach dem Einrichten**: Sende Test-Mails an dich selbst
|
||||
2. **Verschiedene E-Mail-Clients**: Teste in Gmail, Outlook, Apple Mail
|
||||
3. **Mobile Ansicht**: Prüfe die Darstellung auf Smartphones
|
||||
4. **Spam-Check**: Stelle sicher, dass E-Mails nicht im Spam landen
|
||||
|
||||
### Design-Konsistenz:
|
||||
|
||||
- **Farben**: Verwende die ulo.ad-Farben (#0ea5e9 für Primary)
|
||||
- **Schriften**: System-Fonts für beste Kompatibilität
|
||||
- **Logo**: Emoji 🔗 als einfaches Branding-Element
|
||||
- **Responsive**: Alle Templates sind mobile-optimiert
|
||||
- **Zweisprachig**: Alle E-Mails enthalten deutsche und englische Texte
|
||||
|
||||
---
|
||||
|
||||
_Erstellt: 15. Januar 2025_
|
||||
_Für: ulo.ad E-Mail-System (Zweisprachig)_
|
||||
_Version: 2.0_
|
||||
470
apps/uload/docs/features/mail/email-templates-simplified.md
Normal file
470
apps/uload/docs/features/mail/email-templates-simplified.md
Normal file
|
|
@ -0,0 +1,470 @@
|
|||
# Vereinfachte E-Mail Templates für ulo.ad (ohne Emojis/Flags)
|
||||
|
||||
## Wichtig: Diese Templates funktionieren garantiert!
|
||||
|
||||
Falls die komplexen Templates mit Emojis und Flags Probleme machen, verwende diese vereinfachten Versionen. Sie enthalten denselben Inhalt, aber ohne problematische Sonderzeichen.
|
||||
|
||||
---
|
||||
|
||||
## 1. E-Mail-Verifizierung Template (Vereinfacht)
|
||||
|
||||
**Bereich:** Verification template
|
||||
|
||||
### Subject:
|
||||
|
||||
```
|
||||
Bestätige deine E-Mail / Verify your email - ulo.ad
|
||||
```
|
||||
|
||||
### Body (HTML):
|
||||
|
||||
```html
|
||||
<div
|
||||
style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto; padding: 20px; background-color: #f8fafc;"
|
||||
>
|
||||
<!-- Header -->
|
||||
<div style="text-align: center; margin-bottom: 30px;">
|
||||
<h1 style="color: #0ea5e9; font-size: 36px; margin: 0;">ulo.ad</h1>
|
||||
<p style="color: #64748b; margin-top: 8px; font-size: 14px;">
|
||||
Deine Links. Dein Style. Deine Kontrolle.<br />
|
||||
Your links. Your style. Your control.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Main Content -->
|
||||
<div
|
||||
style="background: #ffffff; border-radius: 12px; padding: 30px; box-shadow: 0 2px 4px rgba(0,0,0,0.1);"
|
||||
>
|
||||
<!-- German Section -->
|
||||
<div style="margin-bottom: 30px;">
|
||||
<h2 style="color: #0f172a; font-size: 24px; margin: 0 0 16px 0;">
|
||||
[DE] Willkommen bei ulo.ad!
|
||||
</h2>
|
||||
<p style="color: #475569; font-size: 16px; line-height: 1.6;">
|
||||
Vielen Dank für deine Registrierung! Bitte bestätige deine E-Mail-Adresse, um alle Features
|
||||
freizuschalten.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- English Section -->
|
||||
<div style="margin-bottom: 30px;">
|
||||
<h2 style="color: #0f172a; font-size: 24px; margin: 0 0 16px 0;">[EN] Welcome to ulo.ad!</h2>
|
||||
<p style="color: #475569; font-size: 16px; line-height: 1.6;">
|
||||
Thank you for signing up! Please verify your email address to unlock all features.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- CTA Button -->
|
||||
<div style="text-align: center; margin: 40px 0;">
|
||||
<a
|
||||
href="{APP_URL}/verify-email?token={TOKEN}"
|
||||
style="display: inline-block; background: #0ea5e9; color: white;
|
||||
padding: 16px 40px; border-radius: 8px; text-decoration: none;
|
||||
font-weight: 600; font-size: 16px;"
|
||||
>
|
||||
E-Mail bestätigen / Verify Email
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Alternative Link -->
|
||||
<div style="margin-top: 30px; padding-top: 20px; border-top: 1px solid #e2e8f0;">
|
||||
<p style="color: #94a3b8; font-size: 13px; margin-bottom: 8px;">
|
||||
[DE] Falls der Button nicht funktioniert, kopiere diesen Link:<br />
|
||||
[EN] If the button doesn't work, copy this link:
|
||||
</p>
|
||||
<p
|
||||
style="background: #f8fafc; padding: 12px; border-radius: 6px; word-break: break-all; font-size: 12px; color: #0ea5e9;"
|
||||
>
|
||||
{APP_URL}/verify-email?token={TOKEN}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Footer -->
|
||||
<div style="text-align: center; margin-top: 30px;">
|
||||
<p style="color: #94a3b8; font-size: 12px;">
|
||||
Diese E-Mail wurde an {EMAIL} gesendet.<br />
|
||||
This email was sent to {EMAIL}.
|
||||
</p>
|
||||
<p style="color: #cbd5e1; font-size: 11px; margin-top: 20px;">© 2025 ulo.ad</p>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. Passwort-Reset Template (Vereinfacht)
|
||||
|
||||
**Bereich:** Password reset template
|
||||
|
||||
### Subject:
|
||||
|
||||
```
|
||||
Passwort zurücksetzen / Reset password - ulo.ad
|
||||
```
|
||||
|
||||
### Body (HTML):
|
||||
|
||||
```html
|
||||
<div
|
||||
style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto; padding: 20px; background-color: #f8fafc;"
|
||||
>
|
||||
<!-- Header -->
|
||||
<div style="text-align: center; margin-bottom: 30px;">
|
||||
<h1 style="color: #0ea5e9; font-size: 36px; margin: 0;">ulo.ad</h1>
|
||||
</div>
|
||||
|
||||
<!-- Main Content -->
|
||||
<div
|
||||
style="background: #ffffff; border-radius: 12px; padding: 30px; box-shadow: 0 2px 4px rgba(0,0,0,0.1);"
|
||||
>
|
||||
<!-- German Section -->
|
||||
<div style="margin-bottom: 30px;">
|
||||
<h2 style="color: #0f172a; font-size: 24px; margin: 0 0 16px 0;">
|
||||
[DE] Passwort zurücksetzen
|
||||
</h2>
|
||||
<p style="color: #475569; font-size: 16px; line-height: 1.6;">
|
||||
Du hast eine Anfrage zum Zurücksetzen deines Passworts gestellt. Klicke auf den Button
|
||||
unten, um ein neues Passwort zu wählen.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- English Section -->
|
||||
<div style="margin-bottom: 30px;">
|
||||
<h2 style="color: #0f172a; font-size: 24px; margin: 0 0 16px 0;">[EN] Reset Password</h2>
|
||||
<p style="color: #475569; font-size: 16px; line-height: 1.6;">
|
||||
You have requested to reset your password. Click the button below to choose a new password.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- CTA Button -->
|
||||
<div style="text-align: center; margin: 40px 0;">
|
||||
<a
|
||||
href="{APP_URL}/reset-password?token={TOKEN}"
|
||||
style="display: inline-block; background: #0ea5e9; color: white;
|
||||
padding: 16px 40px; border-radius: 8px; text-decoration: none;
|
||||
font-weight: 600; font-size: 16px;"
|
||||
>
|
||||
Neues Passwort festlegen / Set New Password
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Warning -->
|
||||
<div
|
||||
style="background: #fef2f2; border: 1px solid #fecaca; border-radius: 8px; padding: 16px; margin: 20px 0;"
|
||||
>
|
||||
<p style="color: #991b1b; font-size: 14px; margin: 0;">
|
||||
<strong>[DE]</strong> Dieser Link ist nur 1 Stunde gültig.<br />
|
||||
<strong>[EN]</strong> This link is only valid for 1 hour.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Alternative Link -->
|
||||
<div style="margin-top: 30px; padding-top: 20px; border-top: 1px solid #e2e8f0;">
|
||||
<p style="color: #94a3b8; font-size: 13px; margin-bottom: 8px;">
|
||||
[DE] Falls der Button nicht funktioniert, kopiere diesen Link:<br />
|
||||
[EN] If the button doesn't work, copy this link:
|
||||
</p>
|
||||
<p
|
||||
style="background: #f8fafc; padding: 12px; border-radius: 6px; word-break: break-all; font-size: 12px; color: #0ea5e9;"
|
||||
>
|
||||
{APP_URL}/reset-password?token={TOKEN}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Footer -->
|
||||
<div style="text-align: center; margin-top: 30px;">
|
||||
<p style="color: #94a3b8; font-size: 12px;">
|
||||
[DE] Du hast kein neues Passwort angefordert? Ignoriere diese E-Mail.<br />
|
||||
[EN] You didn't request a new password? Ignore this email.
|
||||
</p>
|
||||
<p style="color: #cbd5e1; font-size: 11px; margin-top: 20px;">© 2025 ulo.ad</p>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. E-Mail-Änderung Template (Vereinfacht)
|
||||
|
||||
**Bereich:** Email change template
|
||||
|
||||
### Subject:
|
||||
|
||||
```
|
||||
E-Mail-Adresse ändern / Change email address - ulo.ad
|
||||
```
|
||||
|
||||
### Body (HTML):
|
||||
|
||||
```html
|
||||
<div
|
||||
style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto; padding: 20px; background-color: #f8fafc;"
|
||||
>
|
||||
<!-- Header -->
|
||||
<div style="text-align: center; margin-bottom: 30px;">
|
||||
<h1 style="color: #0ea5e9; font-size: 36px; margin: 0;">ulo.ad</h1>
|
||||
</div>
|
||||
|
||||
<!-- Main Content -->
|
||||
<div
|
||||
style="background: #ffffff; border-radius: 12px; padding: 30px; box-shadow: 0 2px 4px rgba(0,0,0,0.1);"
|
||||
>
|
||||
<!-- German Section -->
|
||||
<div style="margin-bottom: 30px;">
|
||||
<h2 style="color: #0f172a; font-size: 24px; margin: 0 0 16px 0;">
|
||||
[DE] E-Mail-Adresse ändern
|
||||
</h2>
|
||||
<p style="color: #475569; font-size: 16px; line-height: 1.6;">
|
||||
Du hast eine Änderung deiner E-Mail-Adresse beantragt. Bitte bestätige die neue
|
||||
E-Mail-Adresse.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- English Section -->
|
||||
<div style="margin-bottom: 30px;">
|
||||
<h2 style="color: #0f172a; font-size: 24px; margin: 0 0 16px 0;">
|
||||
[EN] Change Email Address
|
||||
</h2>
|
||||
<p style="color: #475569; font-size: 16px; line-height: 1.6;">
|
||||
You have requested to change your email address. Please confirm the new email address.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Info Box -->
|
||||
<div style="background: #f0f9ff; border-radius: 8px; padding: 16px; margin: 20px 0;">
|
||||
<p style="color: #0369a1; font-size: 14px; margin: 0;">
|
||||
<strong>Old Email:</strong> {EMAIL}<br />
|
||||
<strong>New Email:</strong> {NEW_EMAIL}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- CTA Button -->
|
||||
<div style="text-align: center; margin: 40px 0;">
|
||||
<a
|
||||
href="{APP_URL}/_/#/auth/confirm-email-change/{TOKEN}"
|
||||
style="display: inline-block; background: #0ea5e9; color: white;
|
||||
padding: 16px 40px; border-radius: 8px; text-decoration: none;
|
||||
font-weight: 600; font-size: 16px;"
|
||||
>
|
||||
Neue E-Mail bestätigen / Confirm New Email
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Alternative Link -->
|
||||
<div style="margin-top: 30px; padding-top: 20px; border-top: 1px solid #e2e8f0;">
|
||||
<p style="color: #94a3b8; font-size: 13px; margin-bottom: 8px;">
|
||||
[DE] Falls der Button nicht funktioniert, kopiere diesen Link:<br />
|
||||
[EN] If the button doesn't work, copy this link:
|
||||
</p>
|
||||
<p
|
||||
style="background: #f8fafc; padding: 12px; border-radius: 6px; word-break: break-all; font-size: 12px; color: #0ea5e9;"
|
||||
>
|
||||
{APP_URL}/_/#/auth/confirm-email-change/{TOKEN}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Footer -->
|
||||
<div style="text-align: center; margin-top: 30px;">
|
||||
<p style="color: #cbd5e1; font-size: 11px;">© 2025 ulo.ad</p>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. OTP (One-Time Password) Template (Vereinfacht)
|
||||
|
||||
**Bereich:** OTP template
|
||||
|
||||
### Subject:
|
||||
|
||||
```
|
||||
Einmal-Passwort / One-Time Password: {OTP} - ulo.ad
|
||||
```
|
||||
|
||||
### Body (HTML):
|
||||
|
||||
```html
|
||||
<div
|
||||
style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto; padding: 20px; background-color: #f8fafc;"
|
||||
>
|
||||
<!-- Header -->
|
||||
<div style="text-align: center; margin-bottom: 30px;">
|
||||
<h1 style="color: #0ea5e9; font-size: 36px; margin: 0;">ulo.ad</h1>
|
||||
</div>
|
||||
|
||||
<!-- Main Content -->
|
||||
<div
|
||||
style="background: #ffffff; border-radius: 12px; padding: 30px; box-shadow: 0 2px 4px rgba(0,0,0,0.1);"
|
||||
>
|
||||
<h2 style="color: #0f172a; font-size: 24px; margin: 0 0 16px 0; text-align: center;">
|
||||
[DE] Dein Einmal-Passwort<br />
|
||||
[EN] Your One-Time Password
|
||||
</h2>
|
||||
|
||||
<p style="color: #475569; font-size: 16px; text-align: center;">
|
||||
[DE] Verwende diesen Code für die Anmeldung:<br />
|
||||
[EN] Use this code to log in:
|
||||
</p>
|
||||
|
||||
<!-- OTP Code -->
|
||||
<div
|
||||
style="background: #0ea5e9; border-radius: 8px; padding: 20px; margin: 30px 0; text-align: center;"
|
||||
>
|
||||
<p
|
||||
style="color: white; font-size: 32px; margin: 0; font-weight: bold; letter-spacing: 4px; font-family: monospace;"
|
||||
>
|
||||
{OTP}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Warning -->
|
||||
<div
|
||||
style="background: #fef2f2; border: 1px solid #fecaca; border-radius: 8px; padding: 16px; text-align: center;"
|
||||
>
|
||||
<p style="color: #991b1b; font-size: 14px; margin: 0;">
|
||||
[DE] Dieser Code ist nur 3 Minuten gültig<br />
|
||||
[EN] This code is only valid for 3 minutes
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Footer -->
|
||||
<div style="text-align: center; margin-top: 30px;">
|
||||
<p style="color: #cbd5e1; font-size: 11px;">© 2025 ulo.ad</p>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Login-Alert Template (Vereinfacht)
|
||||
|
||||
**Bereich:** Auth alert template
|
||||
|
||||
### Subject:
|
||||
|
||||
```
|
||||
Neue Anmeldung / New login - ulo.ad
|
||||
```
|
||||
|
||||
### Body (HTML):
|
||||
|
||||
```html
|
||||
<div
|
||||
style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto; padding: 20px; background-color: #f8fafc;"
|
||||
>
|
||||
<!-- Header -->
|
||||
<div style="text-align: center; margin-bottom: 30px;">
|
||||
<h1 style="color: #0ea5e9; font-size: 36px; margin: 0;">ulo.ad</h1>
|
||||
</div>
|
||||
|
||||
<!-- Main Content -->
|
||||
<div
|
||||
style="background: #ffffff; border-radius: 12px; padding: 30px; box-shadow: 0 2px 4px rgba(0,0,0,0.1);"
|
||||
>
|
||||
<h2 style="color: #0f172a; font-size: 24px; margin: 0 0 16px 0;">
|
||||
[DE] Neue Anmeldung erkannt<br />
|
||||
[EN] New Login Detected
|
||||
</h2>
|
||||
|
||||
<p style="color: #475569; font-size: 16px; line-height: 1.6;">
|
||||
[DE] Wir haben eine Anmeldung von einem neuen Standort erkannt.<br />
|
||||
[EN] We detected a login from a new location.
|
||||
</p>
|
||||
|
||||
<!-- Login Details -->
|
||||
<div style="background: #f8fafc; border-radius: 8px; padding: 20px; margin: 20px 0;">
|
||||
<p style="color: #0f172a; font-size: 14px; margin: 0 0 12px 0; font-weight: 600;">
|
||||
Login Details:
|
||||
</p>
|
||||
<ul style="color: #475569; font-size: 14px; margin: 0; padding-left: 20px;">
|
||||
<li>Time: {TIME}</li>
|
||||
<li>Browser: {BROWSER}</li>
|
||||
<li>Device: {DEVICE}</li>
|
||||
<li>Location: {LOCATION}</li>
|
||||
<li>IP: {IP}</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- Action Box -->
|
||||
<div
|
||||
style="background: #dcfce7; border: 1px solid #86efac; border-radius: 8px; padding: 16px; margin: 20px 0;"
|
||||
>
|
||||
<p style="color: #166534; font-size: 14px; margin: 0;">
|
||||
<strong>[DE]</strong> Warst du das? Dann kannst du diese E-Mail ignorieren.<br />
|
||||
<strong>[EN]</strong> Was this you? Then you can ignore this email.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Warning Box -->
|
||||
<div
|
||||
style="background: #fef2f2; border: 1px solid #fecaca; border-radius: 8px; padding: 16px; margin: 20px 0;"
|
||||
>
|
||||
<p style="color: #991b1b; font-size: 14px; margin: 0 0 12px 0;">
|
||||
<strong>[DE]</strong> Warst du das NICHT? Ändere sofort dein Passwort!<br />
|
||||
<strong>[EN]</strong> Was this NOT you? Change your password immediately!
|
||||
</p>
|
||||
<div style="text-align: center; margin-top: 16px;">
|
||||
<a
|
||||
href="{APP_URL}/forgot-password"
|
||||
style="display: inline-block; background: #dc2626; color: white;
|
||||
padding: 12px 24px; border-radius: 6px; text-decoration: none;
|
||||
font-weight: 600; font-size: 14px;"
|
||||
>
|
||||
Passwort ändern / Change Password
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Footer -->
|
||||
<div style="text-align: center; margin-top: 30px;">
|
||||
<p style="color: #94a3b8; font-size: 12px;">
|
||||
Diese Sicherheitsbenachrichtigung wurde an {EMAIL} gesendet.<br />
|
||||
This security notification was sent to {EMAIL}.
|
||||
</p>
|
||||
<p style="color: #cbd5e1; font-size: 11px; margin-top: 20px;">© 2025 ulo.ad</p>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Wichtige Hinweise
|
||||
|
||||
### Warum vereinfachte Templates?
|
||||
|
||||
1. **Keine Emojis/Flags**: Manche SMTP-Server haben Probleme mit Unicode-Zeichen wie 🇩🇪, 🇬🇧, 🔗, etc.
|
||||
2. **Einfacheres HTML**: Weniger verschachtelte Divs und Styles
|
||||
3. **Kleinere Dateigröße**: Reduzierte HTML-Größe für bessere Zustellbarkeit
|
||||
4. **Bessere Kompatibilität**: Funktioniert mit allen E-Mail-Clients
|
||||
|
||||
### Verwendung
|
||||
|
||||
1. **In PocketBase Admin** (`http://localhost:8090/_/`)
|
||||
2. **Collections → users → Options**
|
||||
3. **Jeweiliges Template-Feld** suchen
|
||||
4. **HTML aus dieser Datei** kopieren und einfügen
|
||||
5. **Speichern** und testen
|
||||
|
||||
### Test-Reihenfolge
|
||||
|
||||
1. **Zuerst**: Vereinfachtes Template verwenden
|
||||
2. **Wenn es funktioniert**: Problem lag an den Emojis/komplexem HTML
|
||||
3. **Optional**: Schrittweise Emojis wieder hinzufügen und testen
|
||||
|
||||
### URLs beachten
|
||||
|
||||
- **Verification**: `{APP_URL}/verify-email?token={TOKEN}`
|
||||
- **Password Reset**: `{APP_URL}/reset-password?token={TOKEN}`
|
||||
- **Email Change**: Standard PocketBase URL (nicht ändern)
|
||||
|
||||
---
|
||||
|
||||
_Erstellt: 15. Januar 2025_
|
||||
_Für: ulo.ad E-Mail-System (Fallback-Templates)_
|
||||
_Version: 1.0 Simplified_
|
||||
584
apps/uload/docs/features/mail/email-templates.md
Normal file
584
apps/uload/docs/features/mail/email-templates.md
Normal file
|
|
@ -0,0 +1,584 @@
|
|||
# E-Mail Templates für uLoad
|
||||
|
||||
## Anleitung zum Einrichten
|
||||
|
||||
### Wo die Templates ändern:
|
||||
|
||||
1. Öffne PocketBase Admin: `http://localhost:8090/_/`
|
||||
2. Gehe zu **Collections** → **users** (oder deine Auth-Collection)
|
||||
3. Klicke auf **Options** (Zahnrad-Icon)
|
||||
4. Scrolle zu den E-Mail-Templates Sektionen
|
||||
5. Ersetze die Standard-Templates mit den unten stehenden Vorlagen
|
||||
6. **Speichern** nicht vergessen!
|
||||
|
||||
---
|
||||
|
||||
## 1. E-Mail-Verifizierung Template
|
||||
|
||||
**Bereich:** Verification template
|
||||
|
||||
### Subject:
|
||||
|
||||
```
|
||||
Bestätige deine E-Mail-Adresse für uLoad 🔗
|
||||
```
|
||||
|
||||
### Body (HTML):
|
||||
|
||||
```html
|
||||
<div
|
||||
style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; max-width: 600px; margin: 0 auto; padding: 20px; background-color: #f8fafc;"
|
||||
>
|
||||
<!-- Logo/Header -->
|
||||
<div style="text-align: center; margin-bottom: 30px; padding: 20px;">
|
||||
<h1 style="color: #0ea5e9; font-size: 36px; margin: 0; font-weight: 700;">🔗 uLoad</h1>
|
||||
<p style="color: #64748b; margin-top: 8px; font-size: 14px;">
|
||||
Deine Links. Dein Style. Deine Kontrolle.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Main Content Card -->
|
||||
<div
|
||||
style="background: #ffffff; border-radius: 16px; padding: 32px; box-shadow: 0 1px 3px rgba(0,0,0,0.1), 0 1px 2px rgba(0,0,0,0.06);"
|
||||
>
|
||||
<h2
|
||||
style="color: #0f172a; font-size: 24px; margin-top: 0; margin-bottom: 16px; font-weight: 600;"
|
||||
>
|
||||
Willkommen bei uLoad! 👋
|
||||
</h2>
|
||||
|
||||
<p style="color: #475569; font-size: 16px; line-height: 1.6; margin-bottom: 20px;">
|
||||
Vielen Dank für deine Registrierung! Du bist nur einen Klick davon entfernt, deine persönliche
|
||||
Link-Sammlung zu erstellen und zu verwalten.
|
||||
</p>
|
||||
|
||||
<p style="color: #475569; font-size: 16px; line-height: 1.6; margin-bottom: 24px;">
|
||||
Bitte bestätige deine E-Mail-Adresse, um alle Features freizuschalten:
|
||||
</p>
|
||||
|
||||
<!-- CTA Button -->
|
||||
<div style="text-align: center; margin: 32px 0;">
|
||||
<a
|
||||
href="{APP_URL}/_/#/auth/confirm-verification/{TOKEN}"
|
||||
style="display: inline-block; background: linear-gradient(135deg, #0ea5e9 0%, #0284c7 100%);
|
||||
color: white; padding: 16px 40px; border-radius: 10px;
|
||||
text-decoration: none; font-weight: 600; font-size: 16px;
|
||||
box-shadow: 0 4px 14px rgba(14, 165, 233, 0.25);
|
||||
transition: all 0.3s ease;"
|
||||
>
|
||||
✨ E-Mail-Adresse bestätigen
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Features Box -->
|
||||
<div
|
||||
style="background: linear-gradient(135deg, #f0f9ff 0%, #e0f2fe 100%); border-radius: 12px; padding: 20px; margin: 24px 0; border: 1px solid #bae6fd;"
|
||||
>
|
||||
<p style="color: #0369a1; font-size: 14px; margin: 0 0 12px 0; font-weight: 600;">
|
||||
🚀 Was dich bei uLoad erwartet:
|
||||
</p>
|
||||
<ul style="color: #0c4a6e; font-size: 14px; margin: 0; padding-left: 20px; line-height: 1.8;">
|
||||
<li>Kurze, merkbare Links mit eigenem Branding</li>
|
||||
<li>Detaillierte Klick-Analysen in Echtzeit</li>
|
||||
<li>QR-Codes mit anpassbarem Design</li>
|
||||
<li>Ordner zur Organisation deiner Links</li>
|
||||
<li>Passwortschutz für sensible Links</li>
|
||||
<li>Ablaufdatum für zeitlich begrenzte Kampagnen</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- Alternative Link -->
|
||||
<div style="margin-top: 24px; padding-top: 24px; border-top: 1px solid #e2e8f0;">
|
||||
<p style="color: #94a3b8; font-size: 13px; line-height: 1.6; margin: 0;">
|
||||
Falls der Button nicht funktioniert, kopiere diesen Link in deinen Browser:
|
||||
</p>
|
||||
<div
|
||||
style="background: #f8fafc; border-radius: 8px; padding: 12px; margin-top: 8px; word-break: break-all;"
|
||||
>
|
||||
<a
|
||||
href="{APP_URL}/_/#/auth/confirm-verification/{TOKEN}"
|
||||
style="color: #0ea5e9; font-size: 12px; text-decoration: none;"
|
||||
>
|
||||
{APP_URL}/_/#/auth/confirm-verification/{TOKEN}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Footer -->
|
||||
<div style="text-align: center; margin-top: 32px; padding: 20px;">
|
||||
<p style="color: #94a3b8; font-size: 12px; margin: 8px 0;">
|
||||
Diese E-Mail wurde an <strong>{EMAIL}</strong> gesendet.
|
||||
</p>
|
||||
<p style="color: #94a3b8; font-size: 12px; margin: 8px 0;">
|
||||
Falls du dich nicht bei uLoad registriert hast, kannst du diese E-Mail sicher ignorieren.
|
||||
</p>
|
||||
|
||||
<div style="margin-top: 24px; padding-top: 24px; border-top: 1px solid #e2e8f0;">
|
||||
<p style="color: #cbd5e1; font-size: 11px; margin: 0;">
|
||||
© 2025 uLoad · Built with ❤️ ·
|
||||
<a href="https://ulo.ad" style="color: #0ea5e9; text-decoration: none;">ulo.ad</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. Passwort-Reset Template
|
||||
|
||||
**Bereich:** Password reset template
|
||||
|
||||
### Subject:
|
||||
|
||||
```
|
||||
Passwort zurücksetzen für uLoad 🔐
|
||||
```
|
||||
|
||||
### Body (HTML):
|
||||
|
||||
```html
|
||||
<div
|
||||
style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; max-width: 600px; margin: 0 auto; padding: 20px; background-color: #f8fafc;"
|
||||
>
|
||||
<!-- Logo/Header -->
|
||||
<div style="text-align: center; margin-bottom: 30px; padding: 20px;">
|
||||
<h1 style="color: #0ea5e9; font-size: 36px; margin: 0; font-weight: 700;">🔗 uLoad</h1>
|
||||
</div>
|
||||
|
||||
<!-- Main Content Card -->
|
||||
<div
|
||||
style="background: #ffffff; border-radius: 16px; padding: 32px; box-shadow: 0 1px 3px rgba(0,0,0,0.1), 0 1px 2px rgba(0,0,0,0.06);"
|
||||
>
|
||||
<h2
|
||||
style="color: #0f172a; font-size: 24px; margin-top: 0; margin-bottom: 16px; font-weight: 600;"
|
||||
>
|
||||
Passwort zurücksetzen 🔐
|
||||
</h2>
|
||||
|
||||
<p style="color: #475569; font-size: 16px; line-height: 1.6; margin-bottom: 24px;">
|
||||
Du hast eine Anfrage zum Zurücksetzen deines Passworts gestellt. Klicke auf den Button unten,
|
||||
um ein neues Passwort zu wählen:
|
||||
</p>
|
||||
|
||||
<!-- CTA Button -->
|
||||
<div style="text-align: center; margin: 32px 0;">
|
||||
<a
|
||||
href="{APP_URL}/reset-password?token={TOKEN}"
|
||||
style="display: inline-block; background: linear-gradient(135deg, #0ea5e9 0%, #0284c7 100%);
|
||||
color: white; padding: 16px 40px; border-radius: 10px;
|
||||
text-decoration: none; font-weight: 600; font-size: 16px;
|
||||
box-shadow: 0 4px 14px rgba(14, 165, 233, 0.25);"
|
||||
>
|
||||
🔄 Neues Passwort festlegen
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Warning Box -->
|
||||
<div
|
||||
style="background: #fef2f2; border: 1px solid #fecaca; border-radius: 12px; padding: 16px; margin: 24px 0;"
|
||||
>
|
||||
<p style="color: #991b1b; font-size: 14px; margin: 0; line-height: 1.5;">
|
||||
⚠️ <strong>Wichtiger Hinweis:</strong><br />
|
||||
Dieser Link ist aus Sicherheitsgründen nur <strong>1 Stunde</strong> gültig. Danach musst du
|
||||
eine neue Anfrage stellen.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Security Notice -->
|
||||
<div style="background: #f0f9ff; border-radius: 12px; padding: 16px; margin: 24px 0;">
|
||||
<p style="color: #0369a1; font-size: 14px; margin: 0 0 8px 0; font-weight: 600;">
|
||||
🔒 Sicherheitstipps für dein neues Passwort:
|
||||
</p>
|
||||
<ul style="color: #0c4a6e; font-size: 13px; margin: 0; padding-left: 20px; line-height: 1.6;">
|
||||
<li>Mindestens 8 Zeichen lang</li>
|
||||
<li>Kombination aus Buchstaben, Zahlen und Sonderzeichen</li>
|
||||
<li>Verwende kein Passwort, das du bereits woanders nutzt</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- Alternative Link -->
|
||||
<div style="margin-top: 24px; padding-top: 24px; border-top: 1px solid #e2e8f0;">
|
||||
<p style="color: #94a3b8; font-size: 13px; line-height: 1.6; margin: 0;">
|
||||
Falls der Button nicht funktioniert, kopiere diesen Link in deinen Browser:
|
||||
</p>
|
||||
<div
|
||||
style="background: #f8fafc; border-radius: 8px; padding: 12px; margin-top: 8px; word-break: break-all;"
|
||||
>
|
||||
<a
|
||||
href="{APP_URL}/reset-password?token={TOKEN}"
|
||||
style="color: #0ea5e9; font-size: 12px; text-decoration: none;"
|
||||
>
|
||||
{APP_URL}/reset-password?token={TOKEN}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Ignore Notice -->
|
||||
<div style="margin-top: 24px; padding: 16px; background: #f8fafc; border-radius: 8px;">
|
||||
<p style="color: #64748b; font-size: 13px; margin: 0;">
|
||||
<strong>Du hast kein neues Passwort angefordert?</strong><br />
|
||||
Dann kannst du diese E-Mail einfach ignorieren. Dein Passwort bleibt unverändert und niemand
|
||||
kann ohne Zugriff auf deine E-Mails dein Passwort ändern.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Footer -->
|
||||
<div style="text-align: center; margin-top: 32px; padding: 20px;">
|
||||
<p style="color: #94a3b8; font-size: 12px; margin: 8px 0;">
|
||||
Diese E-Mail wurde an <strong>{EMAIL}</strong> gesendet.
|
||||
</p>
|
||||
|
||||
<div style="margin-top: 24px; padding-top: 24px; border-top: 1px solid #e2e8f0;">
|
||||
<p style="color: #cbd5e1; font-size: 11px; margin: 0;">
|
||||
© 2025 uLoad · Sicher und privat ·
|
||||
<a href="https://ulo.ad" style="color: #0ea5e9; text-decoration: none;">ulo.ad</a>
|
||||
</p>
|
||||
<p style="color: #cbd5e1; font-size: 10px; margin-top: 8px;">
|
||||
Aus Sicherheitsgründen speichern wir deine IP-Adresse bei Passwort-Änderungen.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. E-Mail-Änderung Template
|
||||
|
||||
**Bereich:** Email change template
|
||||
|
||||
### Subject:
|
||||
|
||||
```
|
||||
Bestätige deine neue E-Mail-Adresse für uLoad 📧
|
||||
```
|
||||
|
||||
### Body (HTML):
|
||||
|
||||
```html
|
||||
<div
|
||||
style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; max-width: 600px; margin: 0 auto; padding: 20px; background-color: #f8fafc;"
|
||||
>
|
||||
<!-- Logo/Header -->
|
||||
<div style="text-align: center; margin-bottom: 30px; padding: 20px;">
|
||||
<h1 style="color: #0ea5e9; font-size: 36px; margin: 0; font-weight: 700;">🔗 uLoad</h1>
|
||||
</div>
|
||||
|
||||
<!-- Main Content Card -->
|
||||
<div
|
||||
style="background: #ffffff; border-radius: 16px; padding: 32px; box-shadow: 0 1px 3px rgba(0,0,0,0.1), 0 1px 2px rgba(0,0,0,0.06);"
|
||||
>
|
||||
<h2
|
||||
style="color: #0f172a; font-size: 24px; margin-top: 0; margin-bottom: 16px; font-weight: 600;"
|
||||
>
|
||||
E-Mail-Adresse ändern 📧
|
||||
</h2>
|
||||
|
||||
<p style="color: #475569; font-size: 16px; line-height: 1.6; margin-bottom: 24px;">
|
||||
Du hast eine Änderung deiner E-Mail-Adresse beantragt. Bitte bestätige die neue
|
||||
E-Mail-Adresse, um die Änderung abzuschließen:
|
||||
</p>
|
||||
|
||||
<!-- Info Box -->
|
||||
<div
|
||||
style="background: #f0f9ff; border-radius: 12px; padding: 16px; margin: 24px 0; border: 1px solid #bae6fd;"
|
||||
>
|
||||
<p style="color: #0369a1; font-size: 14px; margin: 0;">
|
||||
<strong>Alte E-Mail:</strong> {EMAIL}<br />
|
||||
<strong>Neue E-Mail:</strong> {NEW_EMAIL}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- CTA Button -->
|
||||
<div style="text-align: center; margin: 32px 0;">
|
||||
<a
|
||||
href="{APP_URL}/_/#/auth/confirm-email-change/{TOKEN}"
|
||||
style="display: inline-block; background: linear-gradient(135deg, #0ea5e9 0%, #0284c7 100%);
|
||||
color: white; padding: 16px 40px; border-radius: 10px;
|
||||
text-decoration: none; font-weight: 600; font-size: 16px;
|
||||
box-shadow: 0 4px 14px rgba(14, 165, 233, 0.25);"
|
||||
>
|
||||
✅ Neue E-Mail bestätigen
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Warning Box -->
|
||||
<div
|
||||
style="background: #fef2f2; border: 1px solid #fecaca; border-radius: 12px; padding: 16px; margin: 24px 0;"
|
||||
>
|
||||
<p style="color: #991b1b; font-size: 14px; margin: 0;">
|
||||
⚠️ <strong>Wichtig:</strong> Nach der Bestätigung musst du dich mit deiner neuen
|
||||
E-Mail-Adresse anmelden.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Alternative Link -->
|
||||
<div style="margin-top: 24px; padding-top: 24px; border-top: 1px solid #e2e8f0;">
|
||||
<p style="color: #94a3b8; font-size: 13px; line-height: 1.6; margin: 0;">
|
||||
Falls der Button nicht funktioniert, kopiere diesen Link in deinen Browser:
|
||||
</p>
|
||||
<div
|
||||
style="background: #f8fafc; border-radius: 8px; padding: 12px; margin-top: 8px; word-break: break-all;"
|
||||
>
|
||||
<a
|
||||
href="{APP_URL}/_/#/auth/confirm-email-change/{TOKEN}"
|
||||
style="color: #0ea5e9; font-size: 12px; text-decoration: none;"
|
||||
>
|
||||
{APP_URL}/_/#/auth/confirm-email-change/{TOKEN}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Footer -->
|
||||
<div style="text-align: center; margin-top: 32px; padding: 20px;">
|
||||
<p style="color: #94a3b8; font-size: 12px; margin: 8px 0;">
|
||||
Falls du diese Änderung nicht beantragt hast, ignoriere diese E-Mail und melde dich bei uns,
|
||||
falls du Sicherheitsbedenken hast.
|
||||
</p>
|
||||
|
||||
<div style="margin-top: 24px; padding-top: 24px; border-top: 1px solid #e2e8f0;">
|
||||
<p style="color: #cbd5e1; font-size: 11px; margin: 0;">
|
||||
© 2025 uLoad ·
|
||||
<a href="https://ulo.ad" style="color: #0ea5e9; text-decoration: none;">ulo.ad</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. OTP (One-Time Password) Template
|
||||
|
||||
**Bereich:** OTP template (falls aktiviert)
|
||||
|
||||
### Subject:
|
||||
|
||||
```
|
||||
Dein uLoad Einmal-Passwort: {OTP} 🔑
|
||||
```
|
||||
|
||||
### Body (HTML):
|
||||
|
||||
```html
|
||||
<div
|
||||
style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; max-width: 600px; margin: 0 auto; padding: 20px; background-color: #f8fafc;"
|
||||
>
|
||||
<!-- Logo/Header -->
|
||||
<div style="text-align: center; margin-bottom: 30px; padding: 20px;">
|
||||
<h1 style="color: #0ea5e9; font-size: 36px; margin: 0; font-weight: 700;">🔗 uLoad</h1>
|
||||
</div>
|
||||
|
||||
<!-- Main Content Card -->
|
||||
<div
|
||||
style="background: #ffffff; border-radius: 16px; padding: 32px; box-shadow: 0 1px 3px rgba(0,0,0,0.1), 0 1px 2px rgba(0,0,0,0.06);"
|
||||
>
|
||||
<h2
|
||||
style="color: #0f172a; font-size: 24px; margin-top: 0; margin-bottom: 16px; font-weight: 600;"
|
||||
>
|
||||
Dein Einmal-Passwort 🔑
|
||||
</h2>
|
||||
|
||||
<p style="color: #475569; font-size: 16px; line-height: 1.6; margin-bottom: 24px;">
|
||||
Verwende diesen Code, um dich bei uLoad anzumelden:
|
||||
</p>
|
||||
|
||||
<!-- OTP Code Box -->
|
||||
<div
|
||||
style="background: linear-gradient(135deg, #0ea5e9 0%, #0284c7 100%); border-radius: 12px; padding: 24px; margin: 24px 0; text-align: center;"
|
||||
>
|
||||
<p
|
||||
style="color: white; font-size: 32px; margin: 0; font-weight: 700; letter-spacing: 8px; font-family: 'Courier New', monospace;"
|
||||
>
|
||||
{OTP}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Timer Warning -->
|
||||
<div
|
||||
style="background: #fef2f2; border: 1px solid #fecaca; border-radius: 12px; padding: 16px; margin: 24px 0; text-align: center;"
|
||||
>
|
||||
<p style="color: #991b1b; font-size: 14px; margin: 0;">
|
||||
⏱️ Dieser Code ist nur <strong>3 Minuten</strong> gültig
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<p style="color: #64748b; font-size: 14px; line-height: 1.6;">
|
||||
Falls du diesen Code nicht angefordert hast, hat möglicherweise jemand versucht, sich mit
|
||||
deiner E-Mail-Adresse anzumelden. Du kannst diese E-Mail in diesem Fall ignorieren.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Footer -->
|
||||
<div style="text-align: center; margin-top: 32px; padding: 20px;">
|
||||
<p style="color: #cbd5e1; font-size: 11px; margin: 0;">
|
||||
© 2025 uLoad · Sicher und privat ·
|
||||
<a href="https://ulo.ad" style="color: #0ea5e9; text-decoration: none;">ulo.ad</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Login-Alert Template (Neue Anmeldung)
|
||||
|
||||
**Bereich:** Auth alert template
|
||||
|
||||
### Subject:
|
||||
|
||||
```
|
||||
Neue Anmeldung bei deinem uLoad Account 🔔
|
||||
```
|
||||
|
||||
### Body (HTML):
|
||||
|
||||
```html
|
||||
<div
|
||||
style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; max-width: 600px; margin: 0 auto; padding: 20px; background-color: #f8fafc;"
|
||||
>
|
||||
<!-- Logo/Header -->
|
||||
<div style="text-align: center; margin-bottom: 30px; padding: 20px;">
|
||||
<h1 style="color: #0ea5e9; font-size: 36px; margin: 0; font-weight: 700;">🔗 uLoad</h1>
|
||||
</div>
|
||||
|
||||
<!-- Main Content Card -->
|
||||
<div
|
||||
style="background: #ffffff; border-radius: 16px; padding: 32px; box-shadow: 0 1px 3px rgba(0,0,0,0.1), 0 1px 2px rgba(0,0,0,0.06);"
|
||||
>
|
||||
<h2
|
||||
style="color: #0f172a; font-size: 24px; margin-top: 0; margin-bottom: 16px; font-weight: 600;"
|
||||
>
|
||||
Neue Anmeldung erkannt 🔔
|
||||
</h2>
|
||||
|
||||
<p style="color: #475569; font-size: 16px; line-height: 1.6; margin-bottom: 24px;">
|
||||
Wir haben eine Anmeldung bei deinem uLoad Account von einem neuen Standort erkannt.
|
||||
</p>
|
||||
|
||||
<!-- Login Details -->
|
||||
<div
|
||||
style="background: #f8fafc; border-radius: 12px; padding: 20px; margin: 24px 0; border: 1px solid #e2e8f0;"
|
||||
>
|
||||
<p style="color: #0f172a; font-size: 14px; margin: 0 0 12px 0; font-weight: 600;">
|
||||
📍 Anmelde-Details:
|
||||
</p>
|
||||
<ul style="color: #475569; font-size: 14px; margin: 0; padding-left: 20px; line-height: 1.8;">
|
||||
<li><strong>Zeit:</strong> {TIME}</li>
|
||||
<li><strong>Browser:</strong> {BROWSER}</li>
|
||||
<li><strong>Gerät:</strong> {DEVICE}</li>
|
||||
<li><strong>Standort:</strong> {LOCATION}</li>
|
||||
<li><strong>IP-Adresse:</strong> {IP}</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- Action Box - Warst du das? -->
|
||||
<div
|
||||
style="background: #dcfce7; border: 1px solid #86efac; border-radius: 12px; padding: 16px; margin: 24px 0;"
|
||||
>
|
||||
<p style="color: #14532d; font-size: 14px; margin: 0 0 12px 0; font-weight: 600;">
|
||||
✅ Warst du das?
|
||||
</p>
|
||||
<p style="color: #166534; font-size: 13px; margin: 0;">
|
||||
Wenn ja, kannst du diese E-Mail ignorieren. Dein Account ist sicher.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Warning Box - Warst du das nicht? -->
|
||||
<div
|
||||
style="background: #fef2f2; border: 1px solid #fecaca; border-radius: 12px; padding: 16px; margin: 24px 0;"
|
||||
>
|
||||
<p style="color: #991b1b; font-size: 14px; margin: 0 0 12px 0; font-weight: 600;">
|
||||
⚠️ Warst du das NICHT?
|
||||
</p>
|
||||
<p style="color: #7f1d1d; font-size: 13px; margin: 0 0 16px 0;">
|
||||
Ändere sofort dein Passwort, um deinen Account zu sichern:
|
||||
</p>
|
||||
<div style="text-align: center;">
|
||||
<a
|
||||
href="{APP_URL}/forgot-password"
|
||||
style="display: inline-block; background: #dc2626;
|
||||
color: white; padding: 12px 24px; border-radius: 8px;
|
||||
text-decoration: none; font-weight: 600; font-size: 14px;"
|
||||
>
|
||||
🔐 Passwort jetzt ändern
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Footer -->
|
||||
<div style="text-align: center; margin-top: 32px; padding: 20px;">
|
||||
<p style="color: #94a3b8; font-size: 12px; margin: 8px 0;">
|
||||
Diese Sicherheitsbenachrichtigung wurde an <strong>{EMAIL}</strong> gesendet.
|
||||
</p>
|
||||
|
||||
<div style="margin-top: 24px; padding-top: 24px; border-top: 1px solid #e2e8f0;">
|
||||
<p style="color: #cbd5e1; font-size: 11px; margin: 0;">
|
||||
© 2025 uLoad · Deine Sicherheit ist uns wichtig ·
|
||||
<a href="https://ulo.ad" style="color: #0ea5e9; text-decoration: none;">ulo.ad</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Verfügbare Variablen
|
||||
|
||||
Diese Platzhalter werden automatisch von PocketBase ersetzt:
|
||||
|
||||
| Variable | Beschreibung |
|
||||
| ------------- | ---------------------------------- |
|
||||
| `{APP_NAME}` | Name der App (uload) |
|
||||
| `{APP_URL}` | Basis-URL deiner App |
|
||||
| `{TOKEN}` | Verifikations-/Reset-Token |
|
||||
| `{EMAIL}` | E-Mail-Adresse des Empfängers |
|
||||
| `{NEW_EMAIL}` | Neue E-Mail-Adresse (bei Änderung) |
|
||||
| `{OTP}` | Einmal-Passwort |
|
||||
| `{TIME}` | Login-Zeit |
|
||||
| `{BROWSER}` | Browser-Information |
|
||||
| `{DEVICE}` | Gerät-Information |
|
||||
| `{LOCATION}` | Standort |
|
||||
| `{IP}` | IP-Adresse |
|
||||
|
||||
---
|
||||
|
||||
## Wichtige Hinweise
|
||||
|
||||
### URLs anpassen:
|
||||
|
||||
⚠️ **WICHTIG**: Die Standard-PocketBase-URLs müssen auf unsere Custom-Pages umgeleitet werden:
|
||||
|
||||
- **Password Reset**:
|
||||
- Alt: `{APP_URL}/_/#/auth/confirm-password-reset/{TOKEN}`
|
||||
- Neu: `{APP_URL}/reset-password?token={TOKEN}`
|
||||
|
||||
- **E-Mail Verifikation**:
|
||||
- Standard: `{APP_URL}/_/#/auth/confirm-verification/{TOKEN}`
|
||||
- Kann so bleiben oder zu einer Custom-Page geändert werden
|
||||
|
||||
### Test-Empfehlungen:
|
||||
|
||||
1. **Nach dem Einrichten**: Sende Test-Mails an dich selbst
|
||||
2. **Verschiedene E-Mail-Clients**: Teste in Gmail, Outlook, Apple Mail
|
||||
3. **Mobile Ansicht**: Prüfe die Darstellung auf Smartphones
|
||||
4. **Spam-Check**: Stelle sicher, dass E-Mails nicht im Spam landen
|
||||
|
||||
### Design-Konsistenz:
|
||||
|
||||
- **Farben**: Verwende die uLoad-Farben (#0ea5e9 für Primary)
|
||||
- **Schriften**: System-Fonts für beste Kompatibilität
|
||||
- **Logo**: Emoji 🔗 als einfaches Branding-Element
|
||||
- **Responsive**: Alle Templates sind mobile-optimiert
|
||||
|
||||
---
|
||||
|
||||
_Erstellt: 15. Januar 2025_
|
||||
_Für: uLoad E-Mail-System_
|
||||
_Version: 1.0_
|
||||
529
apps/uload/docs/features/mail/multilingual-email-plan.md
Normal file
529
apps/uload/docs/features/mail/multilingual-email-plan.md
Normal file
|
|
@ -0,0 +1,529 @@
|
|||
# Plan: Mehrsprachige E-Mail-Templates für uLoad
|
||||
|
||||
## Executive Summary
|
||||
|
||||
Implementierung eines Systems für deutsche und englische E-Mail-Templates mit automatischer Spracherkennung basierend auf Nutzerpräferenzen.
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Ziele
|
||||
|
||||
1. **Primär**: Deutsche und englische E-Mail-Templates
|
||||
2. **Automatische Sprachauswahl** basierend auf Nutzerpräferenz
|
||||
3. **Fallback**: Englisch als Standard
|
||||
4. **Erweiterbar**: Einfach neue Sprachen hinzufügen
|
||||
|
||||
---
|
||||
|
||||
## 📊 Ansätze (von einfach zu komplex)
|
||||
|
||||
### Ansatz 1: PocketBase Collection-Erweiterung (⭐ Empfohlen)
|
||||
|
||||
**Konzept**: Nutzer-Sprachpräferenz in der users Collection speichern
|
||||
|
||||
#### Implementation:
|
||||
|
||||
1. **Users Collection erweitern**:
|
||||
|
||||
```javascript
|
||||
// Neues Feld in users collection
|
||||
{
|
||||
name: "language",
|
||||
type: "select",
|
||||
values: ["de", "en"],
|
||||
default: "de"
|
||||
}
|
||||
```
|
||||
|
||||
2. **Custom E-Mail-Service erstellen**:
|
||||
|
||||
```typescript
|
||||
// src/lib/server/email-service.ts
|
||||
import { pb } from '$lib/pocketbase';
|
||||
|
||||
export async function sendLocalizedEmail(
|
||||
userId: string,
|
||||
type: 'verification' | 'reset' | 'change',
|
||||
data: any
|
||||
) {
|
||||
// Nutzer-Sprache abrufen
|
||||
const user = await pb.collection('users').getOne(userId);
|
||||
const lang = user.language || 'de';
|
||||
|
||||
// Template basierend auf Sprache wählen
|
||||
const template = getTemplate(type, lang);
|
||||
|
||||
// E-Mail senden
|
||||
await sendEmail({
|
||||
to: user.email,
|
||||
subject: template.subject,
|
||||
html: template.body(data)
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
**Vorteile**:
|
||||
|
||||
- ✅ Einfache Implementation
|
||||
- ✅ Nutzer kann Sprache in Settings ändern
|
||||
- ✅ Funktioniert mit PocketBase
|
||||
|
||||
**Nachteile**:
|
||||
|
||||
- ❌ Erfordert Custom Email Handler
|
||||
- ❌ PocketBase Standard-Mails umgehen
|
||||
|
||||
---
|
||||
|
||||
### Ansatz 2: Browser-Sprache + IP-Geolocation
|
||||
|
||||
**Konzept**: Automatische Spracherkennung ohne Nutzer-Input
|
||||
|
||||
#### Implementation:
|
||||
|
||||
```typescript
|
||||
// src/routes/register/+page.server.ts
|
||||
export const actions = {
|
||||
register: async ({ request, getClientAddress }) => {
|
||||
const clientIp = getClientAddress();
|
||||
const acceptLanguage = request.headers.get('accept-language');
|
||||
|
||||
// Sprache ermitteln
|
||||
const language = detectLanguage(acceptLanguage, clientIp);
|
||||
|
||||
// Bei User-Erstellung speichern
|
||||
await pb.collection('users').create({
|
||||
email,
|
||||
password,
|
||||
language // 'de' oder 'en'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
function detectLanguage(acceptLanguage: string, ip: string): string {
|
||||
// 1. Browser-Sprache prüfen
|
||||
if (acceptLanguage?.startsWith('de')) return 'de';
|
||||
if (acceptLanguage?.startsWith('en')) return 'en';
|
||||
|
||||
// 2. IP-Geolocation (optional)
|
||||
// const country = await getCountryFromIP(ip);
|
||||
// if (['DE', 'AT', 'CH'].includes(country)) return 'de';
|
||||
|
||||
// 3. Fallback
|
||||
return 'en';
|
||||
}
|
||||
```
|
||||
|
||||
**Vorteile**:
|
||||
|
||||
- ✅ Automatisch ohne Nutzer-Aktion
|
||||
- ✅ Nutzerfreundlich
|
||||
|
||||
**Nachteile**:
|
||||
|
||||
- ❌ Nicht immer akkurat
|
||||
- ❌ Zusätzliche Geolocation-API nötig
|
||||
|
||||
---
|
||||
|
||||
### Ansatz 3: Custom PocketBase Extension (Advanced)
|
||||
|
||||
**Konzept**: PocketBase mit Go erweitern für native Unterstützung
|
||||
|
||||
#### Struktur:
|
||||
|
||||
```go
|
||||
// pb_hooks/email_localization.go
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/pocketbase/pocketbase"
|
||||
"github.com/pocketbase/pocketbase/core"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app := pocketbase.New()
|
||||
|
||||
app.OnRecordBeforeCreateRequest("users").Add(func(e *core.RecordCreateEvent) error {
|
||||
// Sprache aus Request Headers
|
||||
lang := e.HttpContext.Request().Header.Get("Accept-Language")
|
||||
e.Record.Set("language", parseLanguage(lang))
|
||||
return nil
|
||||
})
|
||||
|
||||
app.OnMailerBeforeRecordVerificationSend().Add(func(e *core.MailerRecordEvent) error {
|
||||
user := e.Record
|
||||
lang := user.GetString("language")
|
||||
|
||||
// Template basierend auf Sprache
|
||||
if lang == "de" {
|
||||
e.Message.Subject = "Bestätige deine E-Mail"
|
||||
e.Message.HTML = germanTemplate(e.Record, e.Token)
|
||||
} else {
|
||||
e.Message.Subject = "Verify your email"
|
||||
e.Message.HTML = englishTemplate(e.Record, e.Token)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
app.Start()
|
||||
}
|
||||
```
|
||||
|
||||
**Vorteile**:
|
||||
|
||||
- ✅ Native PocketBase Integration
|
||||
- ✅ Alle Standard-Features bleiben
|
||||
- ✅ Beste Performance
|
||||
|
||||
**Nachteile**:
|
||||
|
||||
- ❌ Komplexe Implementation
|
||||
- ❌ Go-Kenntnisse erforderlich
|
||||
- ❌ Custom PocketBase Build
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Empfohlene Lösung: Hybrid-Ansatz
|
||||
|
||||
### Phase 1: Quick Win (1-2 Tage)
|
||||
|
||||
1. **User Collection erweitern**:
|
||||
|
||||
```sql
|
||||
ALTER TABLE users ADD COLUMN language TEXT DEFAULT 'de';
|
||||
```
|
||||
|
||||
2. **Settings-Page Update**:
|
||||
|
||||
```svelte
|
||||
<!-- src/routes/(app)/settings/+page.svelte -->
|
||||
<select name="language" bind:value={user.language}>
|
||||
<option value="de">🇩🇪 Deutsch</option>
|
||||
<option value="en">🇬🇧 English</option>
|
||||
</select>
|
||||
```
|
||||
|
||||
3. **Template-Struktur**:
|
||||
|
||||
```
|
||||
docs/mail/templates/
|
||||
├── de/
|
||||
│ ├── verification.html
|
||||
│ ├── reset.html
|
||||
│ └── change.html
|
||||
└── en/
|
||||
├── verification.html
|
||||
├── reset.html
|
||||
└── change.html
|
||||
```
|
||||
|
||||
### Phase 2: Custom Email Handler (3-5 Tage)
|
||||
|
||||
```typescript
|
||||
// src/lib/server/email/index.ts
|
||||
import { readFileSync } from 'fs';
|
||||
import { compile } from 'handlebars';
|
||||
|
||||
export class EmailService {
|
||||
private templates: Map<string, Function> = new Map();
|
||||
|
||||
constructor() {
|
||||
this.loadTemplates();
|
||||
}
|
||||
|
||||
private loadTemplates() {
|
||||
const languages = ['de', 'en'];
|
||||
const types = ['verification', 'reset', 'change'];
|
||||
|
||||
for (const lang of languages) {
|
||||
for (const type of types) {
|
||||
const path = `./templates/${lang}/${type}.html`;
|
||||
const template = readFileSync(path, 'utf-8');
|
||||
const compiled = compile(template);
|
||||
this.templates.set(`${lang}-${type}`, compiled);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async send(userId: string, type: string, data: any) {
|
||||
const user = await pb.collection('users').getOne(userId);
|
||||
const lang = user.language || 'de';
|
||||
|
||||
const template = this.templates.get(`${lang}-${type}`);
|
||||
if (!template) throw new Error('Template not found');
|
||||
|
||||
const html = template({
|
||||
...data,
|
||||
user,
|
||||
appUrl: PUBLIC_APP_URL,
|
||||
currentYear: new Date().getFullYear()
|
||||
});
|
||||
|
||||
await this.sendViaBrevo({
|
||||
to: user.email,
|
||||
subject: this.getSubject(lang, type),
|
||||
html
|
||||
});
|
||||
}
|
||||
|
||||
private getSubject(lang: string, type: string): string {
|
||||
const subjects = {
|
||||
'de-verification': 'Bestätige deine E-Mail für uLoad 🔗',
|
||||
'en-verification': 'Verify your email for uLoad 🔗',
|
||||
'de-reset': 'Passwort zurücksetzen für uLoad 🔐',
|
||||
'en-reset': 'Reset your password for uLoad 🔐'
|
||||
// ...
|
||||
};
|
||||
return subjects[`${lang}-${type}`];
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Phase 3: API Routes (1 Tag)
|
||||
|
||||
```typescript
|
||||
// src/routes/api/auth/verify/+server.ts
|
||||
import { EmailService } from '$lib/server/email';
|
||||
|
||||
export async function POST({ request }) {
|
||||
const { userId, token } = await request.json();
|
||||
|
||||
const emailService = new EmailService();
|
||||
await emailService.send(userId, 'verification', { token });
|
||||
|
||||
return json({ success: true });
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🌍 Sprach-Detection Strategien
|
||||
|
||||
### 1. Bei Registrierung:
|
||||
|
||||
```typescript
|
||||
// Prioritäten:
|
||||
1. URL-Parameter: ?lang=de
|
||||
2. Browser Accept-Language Header
|
||||
3. IP-Geolocation
|
||||
4. Default: de (für DACH-Region)
|
||||
```
|
||||
|
||||
### 2. Implementation:
|
||||
|
||||
```typescript
|
||||
export function detectUserLanguage(request: Request): 'de' | 'en' {
|
||||
// 1. Check URL param
|
||||
const url = new URL(request.url);
|
||||
const urlLang = url.searchParams.get('lang');
|
||||
if (urlLang === 'de' || urlLang === 'en') return urlLang;
|
||||
|
||||
// 2. Check browser language
|
||||
const acceptLang = request.headers.get('accept-language') || '';
|
||||
const browserLang = acceptLang.split(',')[0].split('-')[0].toLowerCase();
|
||||
|
||||
if (browserLang === 'de') return 'de';
|
||||
if (browserLang === 'en') return 'en';
|
||||
|
||||
// 3. Check cookie (if user changed language before)
|
||||
const cookies = parseCookies(request.headers.get('cookie'));
|
||||
if (cookies.lang === 'de' || cookies.lang === 'en') return cookies.lang;
|
||||
|
||||
// 4. Default
|
||||
return 'de'; // DACH-focused app
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📁 Datei-Struktur
|
||||
|
||||
```
|
||||
src/
|
||||
├── lib/
|
||||
│ ├── server/
|
||||
│ │ ├── email/
|
||||
│ │ │ ├── index.ts # EmailService class
|
||||
│ │ │ ├── templates.ts # Template loader
|
||||
│ │ │ └── brevo.ts # Brevo API wrapper
|
||||
│ │ └── i18n/
|
||||
│ │ ├── detector.ts # Language detection
|
||||
│ │ └── translations.ts # Email translations
|
||||
│ └── stores/
|
||||
│ └── language.ts # Client-side language store
|
||||
├── routes/
|
||||
│ ├── api/
|
||||
│ │ ├── user/language/+server.ts # Update language preference
|
||||
│ │ └── email/send/+server.ts # Custom email sender
|
||||
│ └── (app)/
|
||||
│ └── settings/
|
||||
│ └── +page.svelte # Language selector
|
||||
└── hooks.server.ts # Language detection on request
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔄 Migration Plan
|
||||
|
||||
### Woche 1:
|
||||
|
||||
- [ ] Users Collection um `language` Feld erweitern
|
||||
- [ ] Settings-Page mit Sprachauswahl
|
||||
- [ ] Deutsche & englische Templates erstellen
|
||||
|
||||
### Woche 2:
|
||||
|
||||
- [ ] EmailService Klasse implementieren
|
||||
- [ ] API Routes für Custom Emails
|
||||
- [ ] Auto-Detection bei Registrierung
|
||||
|
||||
### Woche 3:
|
||||
|
||||
- [ ] Testing mit verschiedenen Sprachen
|
||||
- [ ] Fallback-Mechanismen
|
||||
- [ ] Documentation
|
||||
|
||||
---
|
||||
|
||||
## 💡 Quick Start (Minimal Version)
|
||||
|
||||
Für einen schnellen Start ohne große Änderungen:
|
||||
|
||||
### 1. Zwei PocketBase Instanzen:
|
||||
|
||||
```bash
|
||||
# Deutsche Version
|
||||
PB_LANG=de ./pocketbase serve --http=127.0.0.1:8090
|
||||
|
||||
# Englische Version
|
||||
PB_LANG=en ./pocketbase serve --http=127.0.0.1:8091
|
||||
```
|
||||
|
||||
### 2. Nginx Proxy:
|
||||
|
||||
```nginx
|
||||
server {
|
||||
listen 80;
|
||||
server_name api.ulo.ad;
|
||||
|
||||
# Route basierend auf Accept-Language
|
||||
if ($http_accept_language ~* "^de") {
|
||||
proxy_pass http://127.0.0.1:8090;
|
||||
}
|
||||
|
||||
proxy_pass http://127.0.0.1:8091; # Default EN
|
||||
}
|
||||
```
|
||||
|
||||
**Vorteil**: Keine Code-Änderungen nötig
|
||||
**Nachteil**: Doppelte Wartung
|
||||
|
||||
---
|
||||
|
||||
## 🎨 Template Management
|
||||
|
||||
### Option 1: Handlebars Templates
|
||||
|
||||
```handlebars
|
||||
<!-- templates/de/verification.hbs -->
|
||||
<h1>Willkommen {{user.name}}!</h1>
|
||||
<p>Bitte bestätige deine E-Mail:</p>
|
||||
<a href='{{appUrl}}/verify?token={{token}}'>Bestätigen</a>
|
||||
```
|
||||
|
||||
### Option 2: React Email (Modern)
|
||||
|
||||
```tsx
|
||||
// emails/Verification.tsx
|
||||
export function VerificationEmail({ user, token, lang }) {
|
||||
const t = translations[lang];
|
||||
|
||||
return (
|
||||
<Html>
|
||||
<Head />
|
||||
<Body>
|
||||
<Container>
|
||||
<Heading>
|
||||
{t.welcome} {user.name}!
|
||||
</Heading>
|
||||
<Text>{t.pleaseVerify}</Text>
|
||||
<Button href={`${APP_URL}/verify?token=${token}`}>{t.verifyButton}</Button>
|
||||
</Container>
|
||||
</Body>
|
||||
</Html>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Option 3: JSON-basierte Templates
|
||||
|
||||
```json
|
||||
{
|
||||
"de": {
|
||||
"verification": {
|
||||
"subject": "Bestätige deine E-Mail",
|
||||
"heading": "Willkommen {name}!",
|
||||
"body": "Bitte bestätige deine E-Mail-Adresse.",
|
||||
"button": "E-Mail bestätigen"
|
||||
}
|
||||
},
|
||||
"en": {
|
||||
"verification": {
|
||||
"subject": "Verify your email",
|
||||
"heading": "Welcome {name}!",
|
||||
"body": "Please verify your email address.",
|
||||
"button": "Verify Email"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🚦 Entscheidungsmatrix
|
||||
|
||||
| Kriterium | Ansatz 1 | Ansatz 2 | Ansatz 3 |
|
||||
| -------------- | ---------- | -------- | ---------- |
|
||||
| Aufwand | ⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
|
||||
| Flexibilität | ⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
|
||||
| Wartbarkeit | ⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐ |
|
||||
| Performance | ⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
|
||||
| Zukunftssicher | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐ |
|
||||
|
||||
**Empfehlung**: Starte mit Ansatz 1, plane für Ansatz 3
|
||||
|
||||
---
|
||||
|
||||
## ✅ Nächste Schritte
|
||||
|
||||
1. **Sofort (Tag 1)**:
|
||||
- [ ] Language Feld zu users Collection hinzufügen
|
||||
- [ ] Settings-Page um Sprachauswahl erweitern
|
||||
2. **Kurzfristig (Woche 1)**:
|
||||
- [ ] Templates in DE/EN erstellen
|
||||
- [ ] Template-Loader implementieren
|
||||
3. **Mittelfristig (Woche 2-3)**:
|
||||
- [ ] EmailService mit Sprach-Support
|
||||
- [ ] Auto-Detection bei Registration
|
||||
4. **Langfristig (Monat 2)**:
|
||||
- [ ] Weitere Sprachen (FR, ES, IT)
|
||||
- [ ] A/B Testing für Templates
|
||||
- [ ] Analytics pro Sprache
|
||||
|
||||
---
|
||||
|
||||
## 📚 Ressourcen
|
||||
|
||||
- [PocketBase Hooks Documentation](https://pocketbase.io/docs/hooks)
|
||||
- [Accept-Language Parser](https://www.npmjs.com/package/accept-language-parser)
|
||||
- [React Email i18n](https://react.email/docs/integrations/i18n)
|
||||
- [Handlebars i18n Helper](https://github.com/handlebars-lang/handlebars.js/issues/1646)
|
||||
|
||||
---
|
||||
|
||||
_Erstellt: 15. Januar 2025_
|
||||
_Status: Bereit zur Diskussion_
|
||||
_Priorität: Mittel_
|
||||
_Geschätzter Aufwand: 5-10 Tage für vollständige Implementation_
|
||||
Loading…
Add table
Add a link
Reference in a new issue