rebreak-monorepo/docs/internal/MAIL_DAEMON_DEPLOYMENT.md
chahinebrini 5d6c322129 wip: KeyboardAwareSheet migrations + Snake/Tetris UI + iron.png + useMe live-update
Sheets via neuer KeyboardAwareSheet-Composable (in Modal pattern, auto-grow
mit Tastatur, paddingBottom-Lift): EditMail, AddDomain, CreateRoom, ConnectMail.
GameOverScreen behält Spring-Slide-In, nutzt RN Keyboard.addListener für Lift.

- KeyboardAwareSheet.tsx — universal modal with sheet-grow + keyboard-padding
- react-native-keyboard-controller installiert + KeyboardProvider in Root
- Snake: time + ScoreProgressBar + useSnakeSounds (haptic, audio TODO)
- Tetris: title weg, Buttons zentriert, kein Pressable mit style-fn
- DPad-Buttons 60→48, more bg, no scale
- useMe: pub-sub listener pattern für app-weite avatar/nickname-Updates
- dm.tsx: resolveAvatar wrap (iron.png-Warning)
- Mail-error-humanizer + locales

Recovery-Doc-Update in docs/internal/RECOVERY_LOG_2026-05-10.md

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

7.5 KiB

MAIL_DAEMON_DEPLOYMENT — Backyard Handoff

Erstellt von: Mo (Mail-Architektur-Agent) Datum: 2026-05-09 Status: Bereit für Deployment — wartet auf Backyard-GO vom User

Kontext

Der rebreak-imap-idle Daemon ist ein eigenständiger Node.js-Prozess. Er hält pro aktivem MailConnection-DB-Eintrag eine persistente IMAP-IDLE-Session und triggert bei neuer Mail sofort /api/mail/scan-internal statt auf den 30min-Cron zu warten.

Der Daemon liegt unter backend/imap-idle/index.mjs und hat seine eigene package.json. Er ist KEIN Teil des Nitro-Builds — er wird direkt via node gestartet.

Was Backyard tun muss (in dieser Reihenfolge)

Schritt 1 — GH-Actions: imap-idle ins Artifact einschließen

In .github/workflows/deploy-backend.yml (oder analog) muss das backend/imap-idle/-Verzeichnis ins deploy-Artifact aufgenommen werden.

Das Artifact enthält aktuell wahrscheinlich nur backend/.output-staging/ und backend/prisma/. backend/imap-idle/ muss ebenfalls mit kopiert werden.

Konkretes Beispiel (je nach Artifact-Aufbau anpassen):

# In der scp/rsync-Step des deploy-workflows:
- name: Copy imap-idle to server
  run: |
    scp -r backend/imap-idle/ rebreak-server:/srv/rebreak/backend/imap-idle/

Schritt 2 — npm install auf Server

Nach dem Artifact-Copy muss auf dem Server npm install in backend/imap-idle/ laufen. Das installiert imapflow und pg lokal für den Daemon.

cd /srv/rebreak/backend/imap-idle && npm install --production

Diesen Schritt als deploy-Step in GH-Actions oder in deploy-from-artifact.sh ergänzen.

Schritt 3 — Zombie-Prozesse aufräumen

Vor dem ersten Start der neuen pm2-Einträge alte Stale-Einträge entfernen (falls rebreak-imap-staging oder rebreak-idle-staging aus altem Setup noch existieren):

pm2 delete rebreak-idle-staging rebreak-imap-staging 2>/dev/null || true

Schritt 4 — ecosystem.config.js erweitern

Die folgenden Einträge in /srv/rebreak/ecosystem.config.js ergänzen (unterhalb des bestehenden rebreak-staging-Eintrags einfügen):

// ─── IMAP IDLE Daemon Staging ───────────────────────────────────────────────
{
  name: "rebreak-idle-staging",
  script: "/srv/rebreak/backend/imap-idle/index.mjs",
  interpreter: "/root/.nvm/versions/node/v24.11.1/bin/node",
  cwd: "/srv/rebreak/backend/imap-idle",
  instances: 1,
  autorestart: true,
  watch: false,
  max_memory_restart: "256M",
  env: {
    NODE_ENV: "production",
    // ACHTUNG: Keine Secrets hier hinterlegen.
    // Infisical-Wrapper via start-idle-staging.sh (Schritt 5).
  },
},

// ─── IMAP IDLE Daemon Prod (auskommentiert bis Prod-Cutover) ───────────────
// {
//   name: "rebreak-idle-prod",
//   script: "/srv/rebreak/backend/imap-idle/index.mjs",
//   interpreter: "/root/.nvm/versions/node/v24.11.1/bin/node",
//   cwd: "/srv/rebreak/backend/imap-idle",
//   instances: 1,
//   autorestart: true,
//   watch: false,
//   max_memory_restart: "256M",
//   env: { NODE_ENV: "production" },
// },

Schritt 5 — start-idle-staging.sh erstellen

Der Daemon braucht die gleichen Infisical-Secrets wie das Backend. Eine eigene Start-Shell analog zu backend/start-staging.sh erstellen:

Pfad: /srv/rebreak/backend/imap-idle/start-idle-staging.sh

#!/bin/bash
# rebreak-imap-idle Staging — Infisical-Secret-Injection

set -euo pipefail
source /etc/environment

if [[ -z "${INFISICAL_CLIENT_ID:-}" || -z "${INFISICAL_CLIENT_SECRET:-}" ]]; then
  echo "[idle] FEHLER: INFISICAL_CLIENT_ID / SECRET nicht gesetzt" >&2
  exit 1
fi

INFISICAL_TOKEN=$(infisical login \
  --method=universal-auth \
  --client-id="${INFISICAL_CLIENT_ID}" \
  --client-secret="${INFISICAL_CLIENT_SECRET}" \
  --silent --plain 2>/dev/null)

[[ -z "$INFISICAL_TOKEN" ]] && { echo "[idle] Infisical login fehlgeschlagen" >&2; exit 1; }

NODE_BIN="/root/.nvm/versions/node/v24.11.1/bin/node"
DAEMON="/srv/rebreak/backend/imap-idle/index.mjs"

exec infisical run \
  --projectId="${INFISICAL_PROJECT_ID:-14b11b35-ef59-4b8a-a16b-398f0cc3ad93}" \
  --env=staging \
  --token="$INFISICAL_TOKEN" \
  -- bash -c '
    set -e
    export DATABASE_URL="${DATABASE_URL:-${NUXT_DATABASE_URL:-}}"
    export ADMIN_SECRET="${ADMIN_SECRET:-${NUXT_ADMIN_SECRET:-}}"
    export ENCRYPTION_KEY="${ENCRYPTION_KEY:-${NUXT_ENCRYPTION_KEY:-}}"
    export BACKEND_URL="http://127.0.0.1:3016"
    exec '"$NODE_BIN"' '"$DAEMON"'
  '

Dann chmod +x start-idle-staging.sh und in ecosystem.config.js den script-Key auf start-idle-staging.sh zeigen lassen (mit interpreter: "bash"), analog zum Pattern von rebreak-staging.

Schritt 6 — pm2 starten

pm2 startOrReload /srv/rebreak/ecosystem.config.js

Verifikations-Schritte nach Deployment

# 1. pm2-Status prüfen
pm2 list
# Erwartung: rebreak-idle-staging → status=online, restart=0

# 2. Logs der ersten 60 Sekunden ansehen
pm2 logs rebreak-idle-staging --lines 100
# Erwartung: "[idle/<email>] connected (...)" für alle aktiven Mailboxen
# "[idle/db] refreshed — N active connections, N sessions"

# 3. Test: Mail an eine verbundene Mailbox schicken (Betreff: "casino bonus")
# Innerhalb von 5 Sekunden sollte im Log erscheinen:
# "[idle/<email>] exists-event received (new mail)"
# "[idle/<email>] scan-triggered → scanned=X blocked=1"

# 4. Memory-Check nach 10 Minuten
pm2 monit
# Erwartung: < 100MB bei <20 Connections, < 200MB bei 100 Connections

Rollback-Plan

Falls der Daemon crashed oder instabil ist:

pm2 stop rebreak-idle-staging
# NICHT delete — damit Logs erhalten bleiben

Auswirkung: Mail-Scanning fällt auf den bestehenden 30min-Cron zurück. Kein Komplett-Ausfall der Mail-Schutz-Funktion. Gambling-Mails werden weiter gelöscht, nur mit bis zu 30min Verzögerung statt Echtzeit.

Bekannte Provider-Quirks

Provider IMAP-Host Port TLS Bekanntes Problem
Gmail imap.gmail.com 993 Implicit App-Password erforderlich (kein OAuth2)
iCloud imap.mail.me.com 993 Implicit App-Specific-Password in Apple-Settings
Outlook outlook.office365.com 993 Implicit IDLE-Drop nach ~20min — disableCompression
GMX imap.gmx.net 993 Implicit Stabil, kein besonderer Quirk
Web.de imap.web.de 993 Implicit Stabil
T-Online secureimap.t-online.de 993 Implicit Stabil
Posteo posteo.de 993 Implicit Stabil

Outlook-spezifisch: Der Daemon setzt disableCompression: true wenn der Host office365 enthält — verhindert partial-read-Fehler nach IDLE-Drop.

Datei-Übersicht

backend/imap-idle/
  index.mjs        — Daemon-Hauptdatei (ESM, standalone)
  package.json     — Eigene Dependencies (imapflow, pg)
  README.md        — Kurz-Doku (lokal starten, env-vars, log-format)

Abhaengigkeiten

  • imapflow ^1.2.18 — IMAP-Client-Library (bereits in backend/package.json)
  • pg ^8.16.3 — Direkter Postgres-Zugriff (kein Prisma im Daemon-Kontext)
  • Node.js >= 20 (ESM, top-level await via main())
  • Infisical-CLI auf dem Server (bereits installiert fuer rebreak-staging)