chahinebrini c1a66e3d07 feat(mail): connect-error tracking + IDLE-heartbeat for accurate UI status
Adds 3 fields to mail_connections so UI can distinguish between
"connection alive but no new mail" vs "connection dead" vs "auth-failed":

- last_connect_error      — text of last IMAP error (auth-fail, connect-fail)
- last_connect_error_at   — timestamp of error
- last_idle_heartbeat_at  — updated every 2min by NOOP-success in daemon

Daemon (backend/imap-idle/index.mjs):
- updateConnectionError() / clearConnectionError() / updateIdleHeartbeat()
  SQL helpers
- logError now uses err.responseText (shows "AUTHENTICATIONFAILED" instead
  of generic "Command failed")
- clearError on connect() success
- updateError on connect() catch
- updateHeartbeat in NOOP-success-path (every 2min)

API (status.get.ts): returns the 3 new fields per account.

Migration: ALTER TABLE rebreak.mail_connections ADD COLUMN ... (idempotent).

UI-side (in flight, separate task): MailAccountCard renders auth-error
banner when lastConnectError != null + heartbeat-based "live" indicator.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 23:48:11 +02:00
..

rebreak-imap-idle

Standalone IMAP IDLE Daemon für Rebreak.

Was er macht

  • Hält pro aktiver MailConnection (DB) eine persistente IMAP-IDLE-Session
  • Reagiert in Echtzeit auf EXISTS-Events (neue Mail im Postfach)
  • Feuert bei jedem Event POST /api/mail/scan-internal gegen das lokale Backend
  • Das Backend entscheidet ob und welche Mails gelöscht werden (Gambling-Keywords + Blocklist)
  • Aktualisiert alle 5 min die Connection-Liste (neue User → neue Sessions, entfernte → geschlossen)
  • IDLE wird alle 25 min erneuert (RFC 3501 Server-Timeout liegt bei 29 min)

Env-Vars

Variable Pflicht Beschreibung
DATABASE_URL ja Postgres-Connection-String (Supabase Pooler oder direkt)
ADMIN_SECRET ja Shared Secret für /api/mail/scan-internal Header
ENCRYPTION_KEY ja AES-256 Key (identisch zum Backend-Key, 32+ Zeichen)
BACKEND_URL nein Default: http://127.0.0.1:3016 (staging) / 3015 (prod)
NODE_ENV nein production → BACKEND_URL default port 3015

Lokal starten (Entwicklung)

cd backend/imap-idle
npm install
DATABASE_URL=<...> ADMIN_SECRET=<...> ENCRYPTION_KEY=<...> node index.mjs

Via Infisical (analog zu start-staging.sh):

infisical run --env=staging -- node index.mjs

PM2 (Produktion)

Wird via ecosystem.config.js gestartet — siehe docs/internal/MAIL_DAEMON_DEPLOYMENT.md.

Logs (pm2)

[idle/<email>] connected (imap.gmail.com:993)
[idle/<email>] exists-event received (new mail)
[idle/<email>] scan-triggered → scanned=12 blocked=1
[idle/<email>] idle renewing (25min threshold)
[idle/<email>] reconnecting in 5s (attempt 2)
[idle/db] refreshed — 47 active connections, 47 sessions