115 lines
5.9 KiB
Markdown
115 lines
5.9 KiB
Markdown
# Codex-Briefing — Mail-Graveyard
|
||
|
||
Du baust ein Go-Single-Binary im Hausstil (wie WatchDove): `main.go` als
|
||
Orchestrator, nummerierte `backend/NN-*.go`, Server-gerendertes HTML + HTMX,
|
||
`config.json`, SQLite via `modernc.org/sqlite` (kein cgo). Das Gerüst steht;
|
||
fülle die `// TODO Codex`-Stellen. **Lies zuerst `Projektbeschreibung.md` – die
|
||
5 harten Regeln sind bindend.**
|
||
|
||
## Bibliotheken (lean, keine Lawine)
|
||
|
||
- IMAP: `github.com/emersion/go-imap/v2` (+ `imapclient`)
|
||
- Parsen (Message-ID, Header, Text/HTML für Viewer): `github.com/emersion/go-message`
|
||
- SQLite: `modernc.org/sqlite`
|
||
- POP3-Fallback: `github.com/knadh/go-pop3`
|
||
- SMTP-Weiterleiten: `net/smtp` (stdlib) — keine Extra-Dependency
|
||
|
||
`go mod tidy` + `go mod vendor` (Offline-Bunker, wie im Template).
|
||
|
||
## Modul-Landkarte
|
||
|
||
| Datei | Inhalt |
|
||
|---|---|
|
||
| `main.go` | Flags (`--run`, `--watch`), LoadConfig→ConnectDB→InitAuth, Web oder CLI |
|
||
| `backend/00-router.go` | Routen + `renderShell` (Outlook-2013-Dreispalter) — **da steht schon das Layout** |
|
||
| `backend/01-config.go` | `config.json` (App + Forward-SMTP). Konten NICHT hier, sondern DB |
|
||
| `backend/02-database.go` | Schema + Account-CRUD + `copied`-Cache (Idempotenz) |
|
||
| `backend/03-auth.go` | Login + `AuthMiddleware` |
|
||
| `backend/04-imap-source.go` | Quelle: Ordnerbaum + Fetch `BODY[] FLAGS INTERNALDATE` |
|
||
| `backend/05-imap-target.go` | Ziel: `EnsureFolder` + `Append` (Flags + INTERNALDATE) |
|
||
| `backend/06-mbox.go` | mbox schreiben (Umzug) **und** lesen (Viewer) |
|
||
| `backend/07-migrate.go` | Motor: pro Konto/Ordner/Mail, Dedup, Doppel-Write, Fortschritt |
|
||
| `backend/08-viewer.go` | mbox im Browser: Liste, Lesen, Weiterleiten |
|
||
| `backend/09-smtp.go` | SMTP nur fürs Weiterleiten |
|
||
| `backend/10-pop3.go` | POP3-Fallback-Quelle (nur INBOX, nie DELE) |
|
||
|
||
## DB-Schema (SQLite)
|
||
|
||
```
|
||
accounts(id, name,
|
||
src_host, src_port, src_security, src_insecure, src_user, src_pass, src_proto,
|
||
dst_host, dst_port, dst_security, dst_insecure, dst_user, dst_pass,
|
||
mbox_dir, active)
|
||
folder_map(id, account_id, src_folder, dst_folder) -- optional, Vorrang vor Rollen-Automatik
|
||
copied(account_id, folder, message_id UNIQUE) -- Delta-Cache
|
||
jobs(id, account_id, started, finished, total, done, errors, state)
|
||
```
|
||
|
||
`src_security`/`dst_security` = `"tls"` | `"starttls"` | `"none"`.
|
||
`src_insecure`/`dst_insecure` = ungültige Zerts akzeptieren.
|
||
|
||
## Der Umzugs-Kern (07-migrate.go), Schritt für Schritt
|
||
|
||
1. Quelle öffnen: `src_proto=imap` → `OpenIMAPSource`, sonst `OpenPOP3Source`.
|
||
2. `Folders()` → rekursiver Ordnerbaum.
|
||
3. Pro Ordner: Zielordner via `MapSourceToTarget` (11-folders.go) bestimmen —
|
||
Rollen-Zuordnung gegen die vorhandenen Ziel-Ordner (keine Dubletten),
|
||
Trenner umhängen, `folder_map` als Override. Dann `EnsureFolder`.
|
||
4. `Fetch(folder)` streamend. Pro Mail: `AlreadyCopied(id,folder,msgID)`?
|
||
→ ja: überspringen. Nein: **beide** schreiben — `TargetMailbox.Append`
|
||
(IMAP, mit Flags+INTERNALDATE) **und** `MboxWriter.Append` (lokal), dann
|
||
`MarkCopied`. Fehler pro Mail zählen, nicht den ganzen Lauf abbrechen.
|
||
5. `jobs` fortschreiben (Browser pollt `/migrate/status` alle 3 s).
|
||
6. `--watch`: Schleife mit Pause; dank `copied` kopiert jeder Durchlauf nur Neues.
|
||
|
||
## UI — Outlook 2013 (Vorlage)
|
||
|
||
Look sitzt schon in `frontend-js/dist/style.css` + `renderShell`:
|
||
**Ribbon** (Outlook-Blau `#0072c6`) mit Reitern *Umzug · Postfächer · Betrachten
|
||
· Einstellungen*, darunter **Dreispalter**: links Ordnerbaum (Konto → Ordner),
|
||
Mitte Nachrichtenliste (ungelesen = fett blau), rechts Lesebereich; unten
|
||
Statusleiste. Flach, Segoe UI, blaue Auswahlzeile `#cde6f7`. Panes per HTMX
|
||
nachladen (`#tree` / `#list` / `#read`). Klassen stehen im CSS: `.tree-node`,
|
||
`.msg-row(.unread/.active)`, `.read-*`, `.grid/.input/.btn`.
|
||
|
||
Reiter **Postfächer** = Konten-CRUD (Formular wie WatchDove `targetForm`, Quelle-
|
||
und Ziel-Block, „Test"-Button prüft beide Logins). Reiter **Umzug** = Konten
|
||
starten + Fortschritt. Reiter **Betrachten** = mbox-Viewer, im Lesebereich ein
|
||
„Weiterleiten"-Button (öffnet Formular → `09-smtp.go`).
|
||
|
||
`frontend-js/dist/htmx.min.js` per Bun holen (wie Template) und mit einchecken.
|
||
|
||
## Sonderfälle — die drei Umzugs-Fallen (zwingend)
|
||
|
||
1. **Unverschlüsselte Alt-Provider.** Verbinden nach `src_security`:
|
||
`tls`=DialTLS (993), `starttls`=DialStartTLS (143), `none`=Dial (reiner
|
||
Klartext). `src_insecure` → `tls.Config{InsecureSkipVerify:true}` für
|
||
kaputte/selbstsignierte Zerts. Muss ohne TLS funktionieren — alte Hoster
|
||
können oft nichts anderes. (04/05-imap-*.go, POP3 analog 110/995.)
|
||
2. **Sonderzeichen in Ordnernamen.** IMAP-Ordner sind auf der Leitung
|
||
modified-UTF-7 (`Gelöschte`→`Gel&APY-schte`). go-imap dekodiert/kodiert das —
|
||
intern IMMER mit dem Klartext-Namen arbeiten, **nie doppelt kodieren**.
|
||
Hierarchie-Trenner (`.` vs `/`) je Server aus LIST lesen und beim Umhängen
|
||
übersetzen (`11-folders.go`).
|
||
3. **Standardordner nicht doppeln.** `11-folders.go` bildet jeden Ordner auf
|
||
eine Rolle ab (SPECIAL-USE `\Sent \Drafts \Junk \Trash \Archive`, sonst
|
||
mehrsprachige Namensliste). `MapSourceToTarget` sortiert Gesendet/Entwürfe/
|
||
Spam/Papierkorb/Archiv/Posteingang in den **vorhandenen** Rollen-Ordner des
|
||
Ziels statt „Gesendete Objekte" **neben** „Gesendete Elemente" zu legen.
|
||
Manuelles `folder_map` schlägt die Automatik.
|
||
|
||
## Sicherheit
|
||
|
||
Tool hält fremde IMAP-Passwörter und kann Mails senden. Default `bind=127.0.0.1`
|
||
+ Login. Falls je remote (hinter Caddy): `/vadmin`-Mail-2FA-Muster aus dem
|
||
Web-Deploy-Kit vorschalten. `config.json`/DB nie committen (`.gitignore` steht).
|
||
|
||
## Fertig-Kriterien (Durchstich zuerst)
|
||
|
||
1. **Durchstich:** ein Konto, nur `INBOX`, Quelle→Ziel-`APPEND` + mbox,
|
||
Message-ID-Dedup, zweimal laufbar ohne Dubletten.
|
||
2. Alle Ordner rekursiv + `EnsureFolder` im Ziel.
|
||
3. Konten-CRUD + Test-Button im Browser.
|
||
4. Viewer (Liste/Lesen) + Weiterleiten.
|
||
5. `--watch`-Delta-Schleife.
|
||
6. Optional: POP3-Fallback.
|