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>
211 lines
7.5 KiB
Markdown
211 lines
7.5 KiB
Markdown
# 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):
|
|
|
|
```yaml
|
|
# 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.
|
|
|
|
```bash
|
|
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):
|
|
|
|
```bash
|
|
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):
|
|
|
|
```js
|
|
// ─── 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`
|
|
|
|
```bash
|
|
#!/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
|
|
|
|
```bash
|
|
pm2 startOrReload /srv/rebreak/ecosystem.config.js
|
|
```
|
|
|
|
## Verifikations-Schritte nach Deployment
|
|
|
|
```bash
|
|
# 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:
|
|
|
|
```bash
|
|
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)
|