feat: Gitea Webhook, IMAP, Settings & Deployment docs
- Webhook route and Gitea integration - IMAP service and Nextcloud/Porkbun setup docs - Settings UI improvements and API updates - SSH/Webhook fix prompt for emailsorter.webklar.com - Bootstrap, config and AI sorter updates
This commit is contained in:
183
docs/development/IMAP_IMPLEMENTATION_PLAN.md
Normal file
183
docs/development/IMAP_IMPLEMENTATION_PLAN.md
Normal file
@@ -0,0 +1,183 @@
|
||||
# Implementierungsplan: IMAP / Porkbun / Nextcloud
|
||||
|
||||
Plan, um EmailSorter um einen **IMAP-Provider** (z. B. Porkbun) zu erweitern. Dann funktioniert die Sortierung auch für Postfächer, die in Nextcloud Mail genutzt werden.
|
||||
|
||||
---
|
||||
|
||||
## Übersicht
|
||||
|
||||
| Phase | Inhalt | Aufwand (grobe Schätzung) |
|
||||
|-------|--------|----------------------------|
|
||||
| **1** | IMAP-Bibliothek + Service-Grundgerüst | 1–2 h |
|
||||
| **2** | Datenbank + Connect-Route für IMAP | 1 h |
|
||||
| **3** | Sortier-Logik für IMAP (Ordner statt Labels) | 2–3 h |
|
||||
| **4** | Frontend: IMAP-Verbindung anlegen | 1–2 h |
|
||||
| **5** | Testen, Feinschliff, Doku | 1 h |
|
||||
|
||||
---
|
||||
|
||||
## Phase 1: IMAP-Bibliothek und Service
|
||||
|
||||
**Ziel:** Backend kann sich per IMAP (z. B. Porkbun) verbinden, INBOX auflisten und E-Mails lesen.
|
||||
|
||||
### 1.1 Abhängigkeit hinzufügen
|
||||
|
||||
- **Datei:** `server/package.json`
|
||||
- **Aktion:** Dependency `imapflow` hinzufügen (moderner IMAP-Client für Node, SSL-Support).
|
||||
- **Befehl:** `npm install imapflow` im Ordner `server/`.
|
||||
|
||||
### 1.2 Neuer Service
|
||||
|
||||
- **Datei (neu):** `server/services/imap.mjs`
|
||||
- **Inhalt (Kern-Interface):**
|
||||
- **Konstruktor:** `ImapService({ host, port, secure, user, password })` – z. B. für Porkbun: `host: 'imap.porkbun.com', port: 993, secure: true`.
|
||||
- **connect()** – Verbindung aufbauen (login).
|
||||
- **listEmails(maxResults, fromSeq?)** – Nachrichten aus INBOX (z. B. per FETCH ENVELOPE), Rückgabe: `{ messages: [{ id, uid, ... }], nextSeq }`.
|
||||
- **getEmail(messageId)** bzw. **batchGetEmails(ids)** – eine bzw. mehrere Mails laden, Rückgabe-Format wie Gmail/Outlook: `{ id, headers: { from, subject }, snippet }`.
|
||||
- **close()** – Verbindung sauber trennen (LOGOUT).
|
||||
- **Hinweis:** IMAP nutzt oft UID oder Sequence Number als „id“; einheitlich als `id` nach außen geben (String), damit die Sortier-Route wie bei Gmail/Outlook arbeitet.
|
||||
|
||||
### 1.3 Akzeptanz Phase 1
|
||||
|
||||
- Ein kleines Test-Script (z. B. `server/scripts/test-imap.mjs`) oder ein temporärer Route-Handler liest Umgebungsvariablen (IMAP_HOST, IMAP_PORT, IMAP_USER, IMAP_PASSWORD), baut `ImapService` auf, ruft `listEmails(10)` und `getEmail(...)` auf und loggt das Ergebnis. Keine Credentials im Repo – nur `.env` / Umgebungsvariablen.
|
||||
|
||||
---
|
||||
|
||||
## Phase 2: Datenbank und Connect-Route
|
||||
|
||||
**Ziel:** Ein neuer Account-Typ „imap“ kann angelegt werden; Zugangsdaten werden gespeichert.
|
||||
|
||||
### 2.1 Datenbank (Appwrite)
|
||||
|
||||
- **Datei:** `server/bootstrap-v2.mjs` (oder separates Migrations-Script).
|
||||
- **Aktion:** In der Collection `email_accounts` optionale Attribute anlegen:
|
||||
- `imapHost` (String, optional)
|
||||
- `imapPort` (Integer, optional)
|
||||
- `imapSecure` (Boolean, optional)
|
||||
- **Alternative (einfacher für nur Porkbun):** Keine neuen Felder; Host/Port im Code fest (imap.porkbun.com, 993). Dann nur `email` + Passwort nötig; Passwort in bestehendem Feld `accessToken` speichern (semantisch „geheimer Token für IMAP“). Für spätere andere IMAP-Server die optionalen Felder nachziehen.
|
||||
|
||||
### 2.2 Connect-Route erweitern
|
||||
|
||||
- **Datei:** `server/routes/email.mjs`
|
||||
- **Route:** `POST /api/email/connect` (bzw. die Route, die Accounts anlegt).
|
||||
- **Aktionen:**
|
||||
- Im Validierungs-Schema `provider` um `'imap'` erweitern: z. B. `rules.isIn('provider', ['gmail', 'outlook', 'demo', 'imap'])`.
|
||||
- Body für IMAP: mindestens `userId`, `provider: 'imap'`, `email`, `password` (oder `accessToken` als Passwort). Optional: `imapHost`, `imapPort`, `imapSecure`.
|
||||
- Wenn `provider === 'imap'`:
|
||||
- Host/Port/Secure aus Body oder Default (Porkbun: imap.porkbun.com, 993, true).
|
||||
- Passwort nicht loggen; in DB in `accessToken` (oder neuem Feld) speichern.
|
||||
- Optional: einmalig `ImapService` instanziieren, `connect()` + `listEmails(1)` aufrufen; bei Erfolg Account anlegen, sonst Fehler zurückgeben („Ungültige Anmeldedaten“).
|
||||
- Account-Dokument anlegen mit `provider: 'imap'`, `email`, `accessToken` (= Passwort), ggf. `imapHost`, `imapPort`, `imapSecure`.
|
||||
|
||||
### 2.3 Middleware/Validierung
|
||||
|
||||
- **Datei:** `server/middleware/validate.mjs` (falls dort Regeln liegen) oder direkt in der Route.
|
||||
- **Aktion:** Für IMAP ggf. zusätzliche Felder erlauben: `imapHost`, `imapPort`, `imapSecure`, `password` (oder wie du das Feld nennst).
|
||||
|
||||
### 2.4 Akzeptanz Phase 2
|
||||
|
||||
- Per API-Client (Postman/curl) oder Frontend: POST mit `provider: 'imap'`, `email`, `password` (und optional Host/Port) an `/connect` senden. Erwartung: 201, Account in Appwrite mit `provider: 'imap'`. Bei falschem Passwort: 4xx mit verständlicher Meldung.
|
||||
|
||||
---
|
||||
|
||||
## Phase 3: Sortier-Logik für IMAP
|
||||
|
||||
**Ziel:** `POST /api/email/sort` funktioniert für Accounts mit `provider === 'imap'`: E-Mails werden per KI kategorisiert und in IMAP-Ordner verschoben.
|
||||
|
||||
### 3.1 Ordner-Mapping
|
||||
|
||||
- **Konzept:** Kategorien (z. B. `vip`, `promotions`, `newsletters`, `archive`) auf Ordner-Namen mappen. Z. B.:
|
||||
- `archive` / `archive_read` → Ordner `Archive` oder `EmailSorter/Archive`
|
||||
- `promotions` → `Promotions` oder `EmailSorter/Promotions`
|
||||
- usw.
|
||||
- **Datei:** Entweder in `server/services/imap.mjs` (Funktion `getFolderNameForCategory(category)`) oder in `server/services/ai-sorter.mjs` / Config. Einheitliche Liste (z. B. Objekt `categoryToFolder`) verwenden.
|
||||
|
||||
### 3.2 IMAP-Service erweitern
|
||||
|
||||
- **Datei:** `server/services/imap.mjs`
|
||||
- **Neue Methoden:**
|
||||
- **ensureFolder(folderName)** – Ordner anlegen (CREATE), falls nicht vorhanden; Fehler „existiert bereits“ ignorieren.
|
||||
- **moveToFolder(messageId, folderName)** – Nachricht aus INBOX in den Ordner verschieben (MOVE oder COPY + DELETE aus INBOX).
|
||||
- Optional: **markAsRead(messageId)** – falls „archive_read“ = verschieben + als gelesen markieren.
|
||||
|
||||
### 3.3 Sortier-Route erweitern
|
||||
|
||||
- **Datei:** `server/routes/email.mjs`
|
||||
- **Stelle:** Dort, wo `account.provider === 'gmail'` und `=== 'outlook'` abgefragt werden (und Demo).
|
||||
- **Aktion:** Neuen Block `else if (account.provider === 'imap')` hinzufügen:
|
||||
1. `ImapService` aus Account-Daten instanziieren (host, port, secure, user = email, password = accessToken).
|
||||
2. `connect()`.
|
||||
3. In einer Schleife (analog Gmail/Outlook):
|
||||
- `listEmails(batchSize, nextSeq)` → Liste von Nachrichten.
|
||||
- `batchGetEmails(ids)` → From, Subject, Snippet.
|
||||
- Für jede E-Mail: KI-Kategorie ermitteln (bestehender `AISorterService`), dann `ensureFolder(categoryToFolder[category])` und `moveToFolder(id, folderName)`.
|
||||
- Bei „archive_read“ ggf. zusätzlich als gelesen markieren.
|
||||
4. Statistiken aktualisieren (wie bei Gmail/Outlook).
|
||||
5. `close()` aufrufen.
|
||||
- **Fehlerbehandlung:** Bei IMAP-Fehlern (z. B. „Invalid credentials“) sinnvolle Meldung zurückgeben und ggf. Account als „reconnect nötig“ markieren.
|
||||
|
||||
### 3.4 Akzeptanz Phase 3
|
||||
|
||||
- Ein IMAP-Account ist verbunden. Aufruf von `POST /api/email/sort` mit `userId` und `accountId`. Erwartung: E-Mails aus INBOX werden kategorisiert und in die richtigen Ordner verschoben; Response enthält z. B. `sortedCount` und Kategorie-Statistiken. In Nextcloud Mail (oder anderem IMAP-Client) erscheinen die neuen Ordner und verschobenen Mails.
|
||||
|
||||
---
|
||||
|
||||
## Phase 4: Frontend – IMAP verbinden
|
||||
|
||||
**Ziel:** Nutzer können im UI „Anderes Postfach (IMAP)“ wählen und E-Mail + Passwort eingeben.
|
||||
|
||||
### 4.1 Verbindungs-Flow
|
||||
|
||||
- **Datei(en):** Dort, wo heute Gmail/Outlook/Demo angeboten werden (z. B. Setup, Settings, „E-Mail verbinden“).
|
||||
- **Aktion:**
|
||||
- Neue Option „IMAP / anderes Postfach“ (oder „Porkbun / eigenes Postfach“).
|
||||
- Beim Klick: Formular anzeigen mit:
|
||||
- E-Mail (Pflicht)
|
||||
- Passwort / App-Passwort (Pflicht, Typ Passwort)
|
||||
- Optional (z. B. für Power-User): Host, Port, SSL (Checkbox); Defaults: imap.porkbun.com, 993, SSL an.
|
||||
- Submit: POST an Backend (z. B. `/api/email/connect`) mit `provider: 'imap'`, `email`, `password`, optional `imapHost`, `imapPort`, `imapSecure`.
|
||||
- Bei Erfolg: Erfolgsmeldung, Account-Liste aktualisieren. Bei Fehler: Meldung anzeigen (z. B. „Anmeldung fehlgeschlagen – prüfe E-Mail und Passwort“).
|
||||
|
||||
### 4.2 API-Client (Frontend)
|
||||
|
||||
- **Datei:** z. B. `client/src/lib/api.ts`
|
||||
- **Aktion:** Methode `connectImapAccount(userId, { email, password, imapHost?, imapPort?, imapSecure? })` hinzufügen, die `POST /api/email/connect` mit diesen Daten aufruft.
|
||||
|
||||
### 4.3 Akzeptanz Phase 4
|
||||
|
||||
- Im UI „IMAP verbinden“ auswählen, E-Mail + Passwort eingeben, absenden. Account erscheint in der Account-Liste. Danach „Sortieren“ auslösbar und funktioniert wie in Phase 3.
|
||||
|
||||
---
|
||||
|
||||
## Phase 5: Testen und Doku
|
||||
|
||||
- **Manuell:** Mit einem echten Porkbun-Account (oder anderem IMAP) verbinden, Sortierung ausführen, in Nextcloud prüfen, ob Ordner und Mails stimmen.
|
||||
- **Sicherheit:** Prüfen, dass Passwörter nirgends geloggt werden und nicht im Frontend gespeichert werden.
|
||||
- **Doku:** `docs/setup/IMAP_NEXTCLOUD_PORKBUN.md` ggf. um „Konfiguration Porkbun“ und „Troubleshooting“ ergänzen (z. B. App-Passwort, 2FA).
|
||||
|
||||
---
|
||||
|
||||
## Kurz-Checkliste
|
||||
|
||||
- [x] Phase 1: `imapflow` installiert, `server/services/imap.mjs` mit connect, listEmails, getEmail, close; Test mit .env-Credentials.
|
||||
- [x] Phase 2: Appwrite `email_accounts` ggf. um IMAP-Felder erweitert; Connect-Route akzeptiert `imap` und speichert Zugangsdaten; Test: Account per API anlegen.
|
||||
- [x] Phase 3: Ordner-Mapping; ImapService: ensureFolder, moveToFolder; Sortier-Route: Block für `provider === 'imap'`; Test: Sortierung für IMAP-Account.
|
||||
- [x] Phase 4: Frontend-Option „IMAP“, Formular E-Mail/Passwort, API-Anbindung; Test: End-to-End Verbindung + Sortierung aus UI.
|
||||
- [ ] Phase 5: Manueller Test mit Porkbun/Nextcloud; Sicherheits-Check; Doku aktualisiert.
|
||||
|
||||
---
|
||||
|
||||
## Dateien-Übersicht
|
||||
|
||||
| Aktion | Datei |
|
||||
|--------|--------|
|
||||
| Neu | `server/services/imap.mjs` |
|
||||
| Neu (optional) | `server/scripts/test-imap.mjs` |
|
||||
| Ändern | `server/package.json` (imapflow) |
|
||||
| Ändern | `server/bootstrap-v2.mjs` (optional: IMAP-Attribute) |
|
||||
| Ändern | `server/routes/email.mjs` (provider imap, connect + sort) |
|
||||
| Ändern | `server/middleware/validate.mjs` (falls nötig) |
|
||||
| Ändern | Frontend: Connect-UI (Setup/Settings) + `client/src/lib/api.ts` |
|
||||
| Ändern | `docs/setup/IMAP_NEXTCLOUD_PORKBUN.md` (Feinschliff) |
|
||||
|
||||
Wenn du mit Phase 1 startest, reicht zunächst: `imapflow` einbinden und `imap.mjs` mit connect + listEmails + getEmail implementieren und lokal mit Porkbun testen.
|
||||
Reference in New Issue
Block a user