Profile (3 Iterationen):
- app/profile/index.tsx + components/profile/* (Header, StatsBar, Approved,
Streak, UrgeStats, Demographics, DigaMissionBanner)
- echte Live-Daten via useMe-Hook (Avatar/Nickname/Plan/Email/Provider-Pill)
- Demographics mit echten Inputs (TextInput + Bottom-Sheet-Selects),
debounced auto-save, Pro-Trial-Reward-Banner, Mikro-Why-Texte
- Approved Domains als plain integer (KEIN Plan-Slot/Cap)
- Friendly Hint-Text statt Progress-Bar (alignSelf:'stretch' Pattern)
- StatsBar zentriert mit 3 prominenten Cards (vertikale Dividers)
- Cooldown-Timeline als Liste mit 1px-Rail
- ApprovedDomainsList: Collapse-Chevron rechts in Title-Row (Pattern-Fix)
- Eigene vs fremde Profile-Ansicht streng getrennt (DSGVO/Anonymität)
Header-Dropdown (kein 3-Punkte-Icon):
- Avatar als Trigger im AppHeader (User-Wunsch)
- Custom-Modal beide Plattformen, Card-Style
- SOS prominent oben (nur Wort 'SOS' rot, Tagline 'wir sind für dich da' klein darunter)
- Profile/Settings/Games/Debug(__DEV__)/Logout
- Logout neutral (nicht rot — Recovery-tonal)
- AppHeader: neue showBack + title Props für Sub-Routes
Routes (Stub bis Phase C):
- app/profile/[userId].tsx — anonym (nur public-Stats)
- app/settings.tsx — Coming-Soon-Skeleton
- app/games.tsx — Standalone Games-Page mit GameCard-Grid
- app/debug.tsx — __DEV__-only
Game-Picker (Migration aus Nuxt):
- components/games/{GameCard, StarRating, GameRatingStars}
- 2x2 Grid, 56pt SVG-Icons (inline aus components/urge/gameSvgs.ts)
- Live-Backend /api/games/ratings (silent-fail)
- Re-use UrgeGames.tsx ohne TTS/Cooldown-Loop
UI-Pattern-Fixes (alle aus screenshot-User-Feedback 2026-05-07):
- Snake-Bug (food-pellet React-18-StrictMode-Reducer-double-call) gefixt
- Snake-Buttons platform-native (iOS-blue / Android-ripple)
- Tetris-Margins (16px paddingHorizontal)
- PostCard-Buttons Apple-44pt-Hit-Area (Image-Select, Image-Remove,
Cancel, Share-Pill — via hitSlop)
- ProfileHeader Demographics-Hint: alignSelf:'stretch' Pattern
- ApprovedDomainsList Collapse: Title flex:1 + Chevron rechts
- ProtectionDetailsSheet FAQ-Items: alignSelf:'stretch' defensive
- AppHeader Back-Button: neue showBack-Prop + chevron-back
Memory + Plan-Docs:
- 17 Memory-Files dokumentieren System-Wissen + Patterns
- ops/{CUTOVER, UI_MIGRATION, PROFILE_PAGE, WEBHOOK, GAMES_1V1,
RELEASE_READINESS, TESTING_STATE, MAESTRO_HOSTING}_*.md
Backend bleibt unverändert (Tier-LLM + Nickname + sort:latency
sind seit gestern deployed).
12 KiB
Webhook-Migration: Standalone Listener → Nitro-Endpoint (Trucko-Pattern)
Owner: Backyard
Erstellt: 2026-05-07
Status: PLAN — keine Implementierung in dieser Session. User-Direktive: „langsam starten".
Ziel: Migration des GitHub-Webhook-Receivers vom standalone scripts/deploy-webhook/server.mjs zu einem Nitro-Endpoint im Backend (backend/server/api/webhook/github.post.ts) — analog zum Trucko-Monorepo.
1. Status quo (Stand 2026-05-07)
1.1 Aktuelle Pipeline
GitHub push → nginx (staging.rebreak.org/webhook)
→ 127.0.0.1:9000 (pm2: rebreak-webhook)
→ scripts/deploy-webhook/server.mjs (HMAC-Verify + Queue)
→ spawn bash scripts/deploy.sh
→ git fetch + reset --hard origin/main
→ pnpm install --frozen-lockfile (workspace-root)
→ cd backend && pnpm --filter rebreak-backend build
→ .output → .output-staging (atomisch via tmp)
→ pm2 restart rebreak-staging --update-env
→ pm2 restart rebreak-imap-staging / rebreak-idle-staging / dns-* (best-effort)
1.2 Beteiligte Dateien
| Datei | Rolle |
|---|---|
/srv/rebreak/scripts/deploy-webhook/server.mjs |
Standalone Node-HTTP-Server auf Port 9000, HMAC-Verify, spawn deploy.sh |
/srv/rebreak/scripts/deploy.sh |
Pull → install → build → atomic-deploy → pm2 restart |
/srv/rebreak/ecosystem.config.js |
pm2-Service rebreak-webhook (cluster mode, 1 instance) |
/etc/nginx/sites-enabled/staging.rebreak.org |
location /webhook → proxy_pass http://127.0.0.1:9000/webhook |
/etc/environment |
GITHUB_WEBHOOK_SECRET (via Infisical-bootstrap), INFISICAL_CLIENT_ID/SECRET |
1.3 Beobachtete Probleme
- Push-Detection unzuverlässig: Manche Pushes triggern Deploy nicht (Listener empfängt Event nicht oder schweigt) — Symptom unklar (timing/network/hängender vorheriger spawn?).
- deploy.sh exit code 1: Vergangene Logs zeigen
cd apps/rebreak: No such file or directory(alter Pre-Cutover-Pfad in alten Logs; aktueller deploy.sh ist aufbackend/korrekt umgestellt — Symptom nicht mehr reproduzierbar, aber Vertrauensverlust). - Kein Healthcheck:
/webhookantwortet nur auf POST mit valider Sig. KeinGET /webhook/healtho.ä. — manuelles Debug nur viapm2 logs rebreak-webhook. - Kein Retry: Wenn
git fetchnetzwerkbedingt failt, bleibt Deploy hängen / aborts ohne Rescheduling. - Logs verstreut: Webhook-Logs in
pm2 logs rebreak-webhook, Deploy-Output in dieselben Logs gestreamt aber mit[deploy] /[deploy:err]-Prefixen — kein strukturiertes Logging, kein File-Persistence über pm2-Restart hinweg. - Single-Point-of-Failure: Wenn
rebreak-webhookpm2-Prozess crashed, gehen Pushes ins Leere — keine GitHub-Redelivery-Automatik (manuell via GitHub UI nötig).
2. Trucko-Pattern (das Vorbild)
Quelldatei: ~/mono/trucko-monorepo/backend/server/api/webhook/github.post.ts (957 Zeilen)
Test-Coverage: ~/mono/trucko-monorepo/backend/tests/unit/webhook-hmac.test.ts (HMAC-Verify isoliert testbar)
2.1 Architektur-Eigenschaften
- Webhook ist ein Nitro-API-Endpoint im Backend selbst — kein separater Listener.
Pfad:
backend/server/api/webhook/github.post.ts→ wird über das Backend-pm2 (rebreak-staging) bedient. - HMAC-Sig-Verify identisch wie unsere
server.mjs(sha256,timingSafeEqual). - Reads
useRuntimeConfig()fürGITHUB_WEBHOOK_SECRET,GITHUB_USERNAME,GITHUB_TOKEN— Secrets kommen via Infisical → Nuxt/Nitro runtimeConfig (kein extra/etc/environment-Parsing). - Affected-Detection: Liest
payload.commits[].added/modified/removed, normalisiert Pfade, mapped auf eine Tabelleaffected = { rebreak: bool, backend: bool, nginx: bool, ... }. Nur betroffene Apps werden gebaut. - Queue-Mechanismus mit Merge:
queueDeployment()mergtaffected-Flags wenn ein zweiter Push während eines laufenden Builds reinkommt → kein Doppel-Build, aber kein Verlust. - Inline-Build-Steps via
executeCommand()-Helper: Jeder Step (git fetch / install / build / pm2 restart) ist ein einzelner spawned bash mit Timeout + structured stdout/stderr-Capture. Kein externes deploy.sh. - Token-basierter Pull: Nutzt
GITHUB_USERNAME+GITHUB_TOKENfürhttps://...@github.com/...— kein SSH-Deploy-Key-Handling im Webhook. - Atomisches Output-Swap für rebreak:
cp -r .output .output-staging-new && rm -rf .output-staging && mv .output-staging-new .output-staging(identisch zu unserem deploy.sh). - Logs in pm2-stream: Alles via
console.log("[Webhook] ...")— landet inpm2 logs backend, zentral. - Healthcheck implizit: Wenn das Backend lebt, lebt der Webhook. pm2 zeigt
backend online→ Webhook funktioniert.
2.2 Was das Trucko-Pattern besser macht
| Aspekt | Standalone-Listener (Status quo) | Nitro-Endpoint (Trucko) |
|---|---|---|
| Single Source of Truth | 2 pm2-Services (webhook + app) | 1 pm2-Service (Backend allein) |
| Secret-Handling | /etc/environment parsing manuell |
useRuntimeConfig() via Infisical |
| Affected-Logik | Nicht vorhanden, baut immer alles | Granular, nur betroffene Apps |
| Logs | 2 verschiedene pm2-Streams | 1 zentraler Stream |
| Test-Coverage | Keine Tests | webhook-hmac.test.ts (vitest) |
| Code-Lokation | scripts/ (separater Code-Pfad) |
backend/server/api/ (gleiche TS-Codebase) |
| Crash-Resilience | Eigener Prozess kann sterben ohne app | Wenn Backend lebt, lebt Webhook |
3. Migrations-Schritte (für nächste Session — NICHT jetzt)
Phase 1: Endpoint anlegen (Mac, kein Server-Change)
- Datei
backend/server/api/webhook/github.post.tserstellen — kopiere Trucko-Vorlage, passe an:- Repo-Pfad:
/srv/rebreak(statt/srv/trucko-monorepo) affected-Map: nurrebreak-native,backend,nginx,dns,imap(keinpizzabox/driver/etc.)- Build-Steps: nur Backend-Build (
pnpm --filter rebreak-backend build) + atomic-output-swap + pm2 restartrebreak-staging - Entferne Trucko-spezifische nginx-conf-Listen (die unsere ops/nginx-Files haben andere Namen)
- Repo-Pfad:
nitro.config.ts:runtimeConfigumgithubWebhookSecret,githubUsername,githubTokenergänzen (analog Trucko).- Test-File
backend/tests/unit/webhook-hmac.test.tsmitmigrieren (vitest setup falls nötig). - Lokal
pnpm --filter rebreak-backend build→ checken dass Endpoint im.output/server/chunks/routes/api/webhook/github.post.mjslandet.
Phase 2: Infisical-Secrets setzen (User-Eskalation)
- Infisical Project rebreak-staging:
GITHUB_WEBHOOK_SECRET(bereits vorhanden in /etc/environment, in Infisical spiegeln)GITHUB_USERNAME(z.B.chahinebrini)GITHUB_TOKEN(PAT mitrepo-Scope für git-pull über HTTPS) → diese Secrets werden viastart-staging.shalsNUXT_*env vars dem Backend bereitgestellt.
Phase 3: Parallel-Betrieb (Failsafe)
- Beide Endpoints aktiv halten:
- GitHub-Webhook bleibt auf
https://staging.rebreak.org/webhook(alter Listener auf Port 9000). - Neuer Endpoint zusätzlich erreichbar unter
https://staging.rebreak.org/api/webhook/github(Backend-Routing).
- GitHub-Webhook bleibt auf
- Auf GitHub: zweite Webhook-Konfig anlegen, beide aktiv. Beide verifizieren mit demselben Secret.
- Logs beide Streams beobachten — wenn neuer Endpoint zuverlässig deployt, alter wird redundant.
Phase 4: Cutover
- GitHub-Webhook umstellen: alten URL-Webhook deaktivieren, nur neuer
/api/webhook/githubaktiv. - nginx:
location /webhook→proxy_pass http://127.0.0.1:9000ENTFERNEN (oder auf Backend umrouten). pm2 stop rebreak-webhook && pm2 delete rebreak-webhook(NUR auf User-Approval — destruktiv).scripts/deploy-webhook/archivieren (nicht löschen — Reference).scripts/deploy.shentweder:- a) behalten als Fallback-Tool für manuelles
bash deploy.shauf SSH (empfohlen), oder - b) Code-Logik ins Backend-Endpoint inline-spawnen wie Trucko es tut (mehr Code, aber 100% Single-Source).
- a) behalten als Fallback-Tool für manuelles
Phase 5: Hardening
- Healthcheck-Endpoint hinzufügen:
backend/server/api/webhook/health.get.ts→ returns{ ok, lastDeploy, queueDepth }. - Persistent Deploy-Log: in
/var/log/rebreak/deploys.jsonlschreiben (statt nur pm2-stream). - Slack/Discord-Notifier on deploy-fail (optional).
4. Risk-Assessment
Top 3 Risks bei Migration
-
Self-Deploy-Loop / Race-Condition beim Restart: Der Webhook lebt im Backend-Prozess. Wenn der Webhook
pm2 restart rebreak-stagingaufruft, killt der Prozess sich selbst, BEVOR die HTTP-Response gesendet ist. → GitHub bekommt timeout, retry-storm. Mitigation: Trucko macht's auch, und es funktioniert dort. Schlüssel: pm2 restart ist async +setTimeout(2000)vor dem restart, response wurde schon vor Build-Start gesendet (return { ok }BEVORqueueDeployment()läuft im Background). Muss bei uns gleich strukturiert werden. -
Build-Failure crashed Webhook-Capability: Wenn ein Bad-Push das Backend-Build kaputt macht, hat der nächste Push keinen Webhook-Endpoint mehr (Backend offline). → Stuck-State, manuelles SSH nötig zum Recovery. Mitigation: Atomic-Output-Swap (.output → .output-staging via mv) bleibt. Alter Build überlebt im
.output-staging, Backend bleibt online auch wenn neuer Build failed. PLUS: Listener als Failsafe parallel erstmal behalten (Phase 3). -
Infisical-Secret-Loading-Lücke: Wenn
useRuntimeConfig()zur Build-Zeit aufgerufen wird (statt Runtime), landen die Secrets nicht im Endpoint. Trucko nutztruntimeConfig→ wird perNUXT_*-env bei Start aufgelöst. Unserestart-staging.shmussNUXT_GITHUB_WEBHOOK_SECRET,NUXT_GITHUB_USERNAME,NUXT_GITHUB_TOKENsetzen — sonst 401 für jeden Push. Mitigation: Phase 2 explizit verifizieren viacurl https://staging.rebreak.org/api/webhook/github -X POST -H "x-hub-signature-256: sha256=..." -d '...'mit Test-Payload BEVOR GitHub-Webhook umgestellt wird.
Weitere Risiken (lower-priority)
- GitHub-Token-Lifetime: PAT läuft ab → silent fail. Mitigation: Token mit langer Lifetime, ggf. Rotation-Reminder.
- Body-Parsing-Reihenfolge: Nitro liest body normal als JSON, aber HMAC braucht raw-body. Trucko liest manuell
event.req.on("data")— Nitro-Middleware darf body nicht vorher konsumiert haben. Ggf. Plugin-Order checken. - OOM während Build: 4 GB RAM,
NODE_OPTIONS=--max-old-space-size=1536reicht heute. Wenn Backend-Build wächst → OOM-kill mid-build → nicht-atomar.
5. Fallback-Plan
Wenn Migration scheitert:
- Listener bleibt aktiv während Migration (Phase 3 ist parallel-Betrieb).
- Rollback-Path:
- GitHub-Webhook URL zurück auf
/webhook(alter Listener). pm2 restart rebreak-webhook(falls gestoppt).- Neuer Endpoint im Backend bleibt vorhanden, antwortet aber nicht mehr aus GitHub-Pushes — kein Schaden.
- GitHub-Webhook URL zurück auf
- Total-Rollback (nur falls Backend-Endpoint Backend-Builds blockiert):
- File
backend/server/api/webhook/github.post.tslöschen, neu builden, deployen. - Cleanup:
runtimeConfig-Einträge innitro.config.tszurückrollen.
- File
6. Empfohlener erster Schritt (für nächste Session)
Read-only Investigation: Prüfen welche Infisical-Secrets aktuell im Backend-Runtime verfügbar sind (pm2 logs rebreak-staging | grep -i webhook + Infisical-CLI infisical secrets --env=staging | grep GITHUB). Damit ist klar ob Phase 2 trivial ist (Secrets bereits da) oder neue Secrets nötig sind. Erst danach Phase 1 (Endpoint anlegen) auf Mac, ohne deployen.
7. Was NICHT in Scope
- Code-Änderungen am App-Code (Nitro-Routes außerhalb
/webhook/). - Mail-Stack-Touchups (Mo's Scope).
- nginx-Routing-Änderung jetzt — erst nach Cutover-Phase 4.
- Force-Push / git history-rewrite.