# 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.