fix: Smart-Matching-Bug, Dashboard-Auth, Trainer-Exklusion & pm2/maci-Betrieb #1

Merged
andre merged 2 commits from a1/live-node-fixes-2027 into main 2026-09-26 23:35:44 +02:00
Owner

Zusammenfassung

Adressiert die kritischen/hohen Befunde aus docs/runbooks/partner-portal-audit-2026-09-26.md (Etappe 5 im Todo):

  • Kritisch: getSmartMatch() bekam nie eine geoLookup-Tabelle → /api/partners/match landete immer im generischen Fallback. Jetzt lädt der Server eine echte PLZ→Koordinaten-Tabelle (8.298 deutsche Postleitzahlen, src/data/plz-centroids.json) beim Start.
  • Kritisch: /api/partner/dashboard war unauthentifiziert abrufbar (Partner-/Umsatzdaten fremder Partner einsehbar). Jetzt per dashboard_token geschützt.
  • Hoch: /auth/verify implementiert — der bisher nie eingelöste Magic-Link-Token tauscht sich jetzt einmalig (Single-Use, gehasht) gegen den Dashboard-Token.
  • Hoch: approvePartner() verlangt jetzt einen expliziten tier-Parameter statt still vorteil_10 zu vergeben (Leitlinie 3: kein Rabatt ohne geprüfte Gegenleistung).
  • Hoch: /api/admin/partners/:id/approve ist jetzt per X-Admin-Key geschützt (war komplett offen).
  • Mittel: POST /api/partners/onboard lehnt type=trainer jetzt ab und verweist auf /trainer-werden/, damit die UI-Exklusion auch serverseitig durchgesetzt wird.
  • Bonus-Fund während der Umsetzung: processAllotmentReleases() (21-Tage-Kontingent-Verfall) wurde nirgends aufgerufen — jetzt stündlicher Check in src/index.js.
  • Schema um dashboard_token, kuerzel, tagessatz ergänzt (Trainer-Freigabe-Flow aus Pflichtenheft 7.2 vorbereitet).
  • ecosystem.config.cjs für pm2/Infisical-Betrieb auf maci nach dem bestehenden Muster (team, motokompass) ergänzt. Neues Infisical-Projekt kurven-schule-partner angelegt, runtime-all-Identity hat Viewer-Zugriff, Port 3009 reserviert (frei laut lsof-Check gegen die anderen maci-Apps).

Architekturentscheidung (siehe Chat-Verlauf mit André)

Node (src/) wird die neue Quelle der Wahrheit, betrieben via pm2/Infisical auf maci — analog zu team/motokompass. Neue öffentliche URL: partner.kurvenschule.cloud (Cloudflare-Tunnel-Eintrag folgt in separatem, nicht-code Schritt auf maci). Die bestehende PHP-Bridge (public/api.php) unter neu.kurven.schule/partner/ bleibt vorerst als Legacy-Pfad bestehen.

Test plan

  • npm test — 29/29 grün (9 neue Tests: Dashboard-Auth, echter Matching-Treffer über die PLZ-Tabelle, Trainer-Ablehnung, kompletter Approve→Verify→Dashboard-Flow, Admin-Key-Schutz, Pflicht-Tier)
  • Nach Merge: pm2 start ecosystem.config.cjs auf maci, curl https://partner.kurvenschule.cloud/health nach Cloudflared-Ingress-Ergänzung

🤖 Generated with Claude Code

## Zusammenfassung Adressiert die kritischen/hohen Befunde aus `docs/runbooks/partner-portal-audit-2026-09-26.md` (Etappe 5 im Todo): - **Kritisch:** `getSmartMatch()` bekam nie eine `geoLookup`-Tabelle → `/api/partners/match` landete immer im generischen Fallback. Jetzt lädt der Server eine echte PLZ→Koordinaten-Tabelle (8.298 deutsche Postleitzahlen, `src/data/plz-centroids.json`) beim Start. - **Kritisch:** `/api/partner/dashboard` war unauthentifiziert abrufbar (Partner-/Umsatzdaten fremder Partner einsehbar). Jetzt per `dashboard_token` geschützt. - **Hoch:** `/auth/verify` implementiert — der bisher nie eingelöste Magic-Link-Token tauscht sich jetzt einmalig (Single-Use, gehasht) gegen den Dashboard-Token. - **Hoch:** `approvePartner()` verlangt jetzt einen expliziten `tier`-Parameter statt still `vorteil_10` zu vergeben (Leitlinie 3: kein Rabatt ohne geprüfte Gegenleistung). - **Hoch:** `/api/admin/partners/:id/approve` ist jetzt per `X-Admin-Key` geschützt (war komplett offen). - **Mittel:** `POST /api/partners/onboard` lehnt `type=trainer` jetzt ab und verweist auf `/trainer-werden/`, damit die UI-Exklusion auch serverseitig durchgesetzt wird. - **Bonus-Fund während der Umsetzung:** `processAllotmentReleases()` (21-Tage-Kontingent-Verfall) wurde nirgends aufgerufen — jetzt stündlicher Check in `src/index.js`. - Schema um `dashboard_token`, `kuerzel`, `tagessatz` ergänzt (Trainer-Freigabe-Flow aus Pflichtenheft 7.2 vorbereitet). - `ecosystem.config.cjs` für pm2/Infisical-Betrieb auf maci nach dem bestehenden Muster (`team`, `motokompass`) ergänzt. Neues Infisical-Projekt `kurven-schule-partner` angelegt, `runtime-all`-Identity hat Viewer-Zugriff, Port `3009` reserviert (frei laut `lsof`-Check gegen die anderen maci-Apps). ## Architekturentscheidung (siehe Chat-Verlauf mit André) Node (`src/`) wird die neue Quelle der Wahrheit, betrieben via pm2/Infisical auf maci — analog zu `team`/`motokompass`. Neue öffentliche URL: `partner.kurvenschule.cloud` (Cloudflare-Tunnel-Eintrag folgt in separatem, nicht-code Schritt auf maci). Die bestehende PHP-Bridge (`public/api.php`) unter `neu.kurven.schule/partner/` bleibt vorerst als Legacy-Pfad bestehen. ## Test plan - [x] `npm test` — 29/29 grün (9 neue Tests: Dashboard-Auth, echter Matching-Treffer über die PLZ-Tabelle, Trainer-Ablehnung, kompletter Approve→Verify→Dashboard-Flow, Admin-Key-Schutz, Pflicht-Tier) - [ ] Nach Merge: `pm2 start ecosystem.config.cjs` auf maci, `curl https://partner.kurvenschule.cloud/health` nach Cloudflared-Ingress-Ergänzung 🤖 Generated with [Claude Code](https://claude.com/claude-code)
fix: Smart-Matching-Bug, Dashboard-Auth, Trainer-Exklusion & pm2/maci-Betrieb
All checks were successful
Unit & Contract Tests / Node.js Contract Tests (pull_request) Successful in 8s
5ae4062156
Adressiert die kritischen Befunde aus dem Ganzheitsaudit
(docs/runbooks/partner-portal-audit-2026-09-26.md, Etappe 5):

- getSmartMatch() erhielt nie eine geoLookup-Tabelle, dadurch landete
  /api/partners/match immer im Fallback. Echte PLZ-Koordinaten (8.298
  deutsche Postleitzahlen, src/data/plz-centroids.json) werden jetzt
  beim Serverstart geladen und durchgereicht.
- /api/partner/dashboard verlangt jetzt einen partnerspezifischen
  dashboard_token statt anonym per ?id= erreichbar zu sein.
- /auth/verify läust den Magic-Link-Token einmalig gegen den
  Dashboard-Token ein (Single-Use, Hash-Vergleich, kein Klartext im
  audit_log).
- approvePartner() verlangt jetzt einen expliziten tier-Parameter statt
  still 'vorteil_10' zu vergeben (Leitlinie 3: kein Rabatt ohne Prüfung).
- /api/admin/partners/:id/approve ist jetzt per X-Admin-Key geschützt.
- POST /api/partners/onboard lehnt type=trainer ab und verweist auf
  /trainer-werden/, damit die UI-Exklusion auch serverseitig gilt.
- processAllotmentReleases() wird jetzt stündlich aus src/index.js
  aufgerufen (lief vorher nie, 21-Tage-Verfall war totes Feature).
- Schema um dashboard_token, kuerzel, tagessatz ergänzt.
- ecosystem.config.cjs für pm2/Infisical-Betrieb auf maci nach dem
  bestehenden Muster (team, motokompass) ergänzt.

29/29 Tests grün (9 neue Tests für Auth, Matching-Fix, Trainer-
Exklusion, Magic-Link-Flow, Admin-Schutz).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
fix: DeepSeek-R1-Review-Funde aus PR #1 behoben
All checks were successful
Unit & Contract Tests / Node.js Contract Tests (pull_request) Successful in 8s
0341eb0ec8
- /api/partner/dashboard prüft jetzt zusätzlich partners.status — ein
  widerrufener/pausierter Partner verliert sofort den Zugriff, auch
  wenn sein dashboard_token nie rotiert wurde.
- Timing-sichere Vergleiche (crypto.timingSafeEqual) für X-Admin-Key
  und Dashboard-Token statt einfachem !==.
- /auth/verify gibt nach außen nur noch eine generische Fehlermeldung
  aus (statt NO_APPROVAL_FOUND/TOKEN_ALREADY_USED/PARTNER_NOT_FOUND
  einzeln), damit sich Partner-Existenz und Freigabe-Status nicht per
  Enumeration erraten lassen.
- Single-Use-Markierung des Magic-Link-Tokens ist jetzt eine atomare
  bedingte SQL-UPDATE (json_set/json_extract) statt Read-Modify-Write —
  race-sicher auch bei mehreren Prozessen, nicht nur zufällig sicher
  durch Node-Single-Thread-Timing.

30/30 Tests grün (1 neuer Test für den Status-Check).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
andre merged commit 0b33070be8 into main 2026-09-26 23:35:44 +02:00
andre deleted branch a1/live-node-fixes-2027 2026-09-26 23:35:44 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
andre/kurven-schule-partner!1
No description provided.