All checks were successful
Build & Push Docker Image to Gitea Registry / build-and-push (push) Successful in 2m39s
248 lines
11 KiB
Markdown
248 lines
11 KiB
Markdown
# Brain.md — MiChess Project Memory
|
|
|
|
> Dieses Dokument ist mein aktives Gedächtnis während der Entwicklung von MiChess.
|
|
> Ich denke hier, notiere Entscheidungen, Probleme und Fortschritt.
|
|
|
|
---
|
|
|
|
## Projektziel
|
|
|
|
MiChess = Lichess-inspirierte Schachplattform, self-hosted auf einer NAS, in Docker, mit Stockfish-Engine.
|
|
|
|
---
|
|
|
|
## Tech Stack (Entschieden)
|
|
|
|
| Schicht | Technologie | Grund |
|
|
|---|---|---|
|
|
| Frontend | Next.js 14 + React + Tailwind + daisyUI | Von chessu geerbt, modern, SSR-fähig |
|
|
| Backend | Node.js + Express + Socket.io | Von chessu geerbt, real-time-ready |
|
|
| Datenbank | PostgreSQL | Von chessu geerbt, solide |
|
|
| Auth | express-session + argon2 | Bereits vorhanden, sicher |
|
|
| Schach-Logik | chess.js | Bereits vorhanden |
|
|
| Schachbrett UI | react-chessboard | Bereits vorhanden |
|
|
| Stockfish | stockfish npm-Paket | Einfache Integration im Backend |
|
|
| Container | Docker Compose | NAS-Deployment |
|
|
| Package Manager | pnpm Workspaces | Monorepo-Setup |
|
|
|
|
---
|
|
|
|
## Basis-Projekt
|
|
|
|
- **Geclont von:** `dotnize/chessu` (MIT-Lizenz)
|
|
- **Warum:** TypeScript, React/Next.js, Express, Socket.io, PostgreSQL, chess.js — perfekter Ausgangspunkt
|
|
- **Was chessu schon hat:**
|
|
- User-Accounts (name, email, password, wins/losses/draws)
|
|
- Session-Auth mit argon2-Passwort-Hashing
|
|
- Real-time Multiplayer via Socket.io
|
|
- Spiele mit PGN-Speicherung
|
|
- Public Games Liste
|
|
- Archiv (gespielte Partien)
|
|
- User-Profilseite
|
|
|
|
---
|
|
|
|
## Was ich bauen muss (Delta zu chessu)
|
|
|
|
1. **Stockfish AI-Gegner** — verschiedene Schwierigkeitsgrade (Level 1-8, ELO-basiert)
|
|
2. **Admin-Panel** — Nutzerverwaltung, Git-Pull-Button, Statistiken
|
|
3. **Freundessystem** — Freundschaftsanfragen, Freundesliste, Freunde herausfordern
|
|
4. **Branding** — Alles auf "MiChess" umbenennen
|
|
5. **Docker NAS-Deployment** — docker-compose für NAS, Auto-Update-Script
|
|
6. **DB-Schema Erweiterungen** — admin-Flag, friends-Tabelle, friend_requests-Tabelle
|
|
7. **E-Mail-basierte Registrierung** — (email-Feld schon da, braucht UI-Flow: email → unique username wählen)
|
|
|
|
---
|
|
|
|
## Architektur-Entscheidungen
|
|
|
|
### Stockfish im Backend
|
|
- Stockfish läuft als Child-Process auf dem Server (NAS)
|
|
- Kommunikation über UCI-Protokoll
|
|
- Schwierigkeitsgrade via `Skill Level` (0-20) und `Depth`
|
|
- Pro aktiver AI-Partie: eigener Stockfish-Prozess (oder Pool)
|
|
|
|
### Admin-Rolle
|
|
- `role` Spalte in `user`-Tabelle (default: 'user', admin: 'admin')
|
|
- Erster User mit email aus `ADMIN_EMAIL` env var wird automatisch Admin
|
|
- Admin-Middleware schützt `/v1/admin/*` Routen
|
|
|
|
### Friends-System
|
|
- `friends`-Tabelle: user_id_1, user_id_2 (symmetrisch)
|
|
- `friend_requests`-Tabelle: from_id, to_id, status (pending/accepted/rejected)
|
|
- Nutzer suchen via Username
|
|
|
|
### Git-Auto-Update (Admin-Feature)
|
|
- Admin-Route: `POST /v1/admin/update`
|
|
- Führt `git pull` im Container-Verzeichnis aus
|
|
- Triggert danach Neustart (via restart-policy des Containers)
|
|
- Update-Script wird ins Docker-Image eingebaut
|
|
|
|
### Docker NAS-Setup
|
|
- `docker-compose.yml` mit Services: `michess-app` + `postgres`
|
|
- Watchtower oder einfaches update-script für automatische Updates
|
|
- Alle Credentials via `.env`-Datei
|
|
|
|
---
|
|
|
|
## Aktueller Status
|
|
|
|
- [x] Basis-Projekt geclont (chessu)
|
|
- [x] Brain.md erstellt
|
|
- [x] Plan.md erstellt
|
|
- [ ] Git remote auf `git.mischlabs.de` gesetzt
|
|
- [ ] Projekt auf "MiChess" umbenannt
|
|
- [ ] DB-Schema erweitert (admin, friends, friend_requests)
|
|
- [ ] Stockfish integriert
|
|
- [ ] Admin-Panel gebaut
|
|
- [ ] Friends-System gebaut
|
|
- [ ] Docker NAS-Deployment finalisiert
|
|
- [ ] Alles committed & gepusht
|
|
|
|
---
|
|
|
|
## Bekannte Probleme / Offene Fragen
|
|
|
|
- chessu's Dockerfile nutzt `CMD ["start"]` — muss für MiChess angepasst werden (Server + Client separat oder zusammen)
|
|
- Stockfish binary muss im Docker-Image vorhanden sein (Alpine Linux: `apk add stockfish` oder npm-Paket)
|
|
- Admin-Git-Pull: funktioniert nur wenn Container read-write Zugriff auf sein eigenes Verzeichnis hat — besser: Host-Script via webhook triggern
|
|
|
|
---
|
|
|
|
## Lessons Learned
|
|
|
|
- **CSS-only Dropdowns vs. Autofill Extensions:** Rein CSS-basierte DaisyUI Dropdowns (`dropdown-content` getriggert über `:focus`/`tabIndex`) schließen sich unkontrolliert, wenn Passwort-Manager (z.B. Vaultwarden) Fokus-Events abfangen oder Overlays einblenden. Lösung: Vollständig React-gesteuerter Dropdown-Zustand (`useState`, `useRef`, `click-outside` via `mousedown` auf Dokumentenebene) ohne `tabIndex={0}`.
|
|
- **Docker Env Mapping Empty Strings:** Im `docker-compose.yml` sorgt die Syntax `SSO_CLIENT_ID: ${SSO_CLIENT_ID:-}` dafür, dass bei Auslassung der Variablen in der `.env`-Datei ein leerer String (`""`) in die Node.js-Umgebung gereicht wird. Node.js interpretiert `""` als *falsy*, wodurch Abfragen wie `process.env.SSO_CLIENT_ID` fehlschlagen.
|
|
|
|
---
|
|
|
|
## 🔑 Keycloak SSO Integration & UX Overhaul
|
|
|
|
### 1. Backend-Erweiterungen (Server)
|
|
- **Paket:** `openid-client` für OIDC-Standard-Interaktionen.
|
|
- **Endpunkte (`/v1/auth/sso/*`):**
|
|
- `GET /v1/auth/sso/config`: Gibt `{ enabled: boolean }` zurück. Erkennt SSO als aktiv, sobald `SSO_CLIENT_SECRET` gesetzt ist (die optionalen Variablen `SSO_CLIENT_ID` und `SSO_AUTHORITY` haben Code-Fallbacks).
|
|
- `GET /v1/auth/sso/login`: Initiiert den OIDC-Flow und leitet zur Keycloak Realm `mischlabs` weiter.
|
|
- `GET /v1/auth/sso/callback`: Verarbeitet das authorization code grant, erfragt User-Infos und führt **Just-In-Time (JIT) Provisionierung** durch.
|
|
- **Option A Permission Mapping:** JIT-provisionierte Benutzer werden automatisch zu `admin` befördert, wenn ihr Benutzername `MrDiderot` lautet oder wenn die Benutzer-Datenbank leer ist (andernfalls Rolle `user`).
|
|
|
|
### 2. Frontend-Umbau (Client)
|
|
- **Popup-Entfernung:** `AuthModal.tsx` wurde komplett gelöscht. Es gibt beim Laden der Seite kein störendes Popup mehr.
|
|
- **Avatar-Dropdown:** Einbettung von `AuthDropdownContent.tsx` in `Header.tsx` für unangekündigte Gäste. Bietet Reiter für *Gast*, *Anmelden*, *Registrieren* sowie die Keycloak-Schaltfläche (wenn aktiviert).
|
|
- **Passwort-Manager-Immunisierung:** Vollständig React-kontrolliertes Dropdown gegen vorzeitiges Schließen beim Autofill.
|
|
|
|
---
|
|
|
|
## 🔍 Fehleranalyse: SSO-Schaltfläche fehlt weiterhin
|
|
|
|
Obwohl das Frontend-Update aktiv ist (Vaultwarden-Anmeldung schließt das Fenster nicht mehr), wird die Schaltfläche `"Anmelden mit Keycloak"` nicht angezeigt. Das bedeutet, dass der Client `ssoEnabled === false` setzt, weil der API-Call `${API_URL}/v1/auth/sso/config` nicht `{ enabled: true }` zurückgibt.
|
|
|
|
### 📋 Vermutungen und Diagnose-Ansätze (für Zweit-KI / Debugging)
|
|
|
|
#### Vermutung A: Die Umgebungsvariable `SSO_CLIENT_SECRET` ist im Backend-Container nicht geladen.
|
|
- **Grund:** Die Variable wurde in `/volume2/docker/mischlabs/.env` eingetragen, aber Docker Compose hat den Container nicht vollständig mit der neuen Umgebung neu generiert.
|
|
- **Diagnose:** SSH-Zugriff auf das NAS und Ausführen von:
|
|
```bash
|
|
docker exec -it michess env | grep SSO
|
|
```
|
|
Wenn hier `SSO_CLIENT_SECRET` fehlt oder leer ist, lädt der Container die Variable nicht.
|
|
- **Behebung:** Den Container zwingend neu erstellen lassen, nicht nur updaten:
|
|
```bash
|
|
docker compose down && docker compose up -d
|
|
```
|
|
|
|
#### Vermutung B: Abweichende `.env`-Pfade oder fehlendes Volume-Mapping.
|
|
- **Grund:** Im Docker-Compose-Setup wird `SSO_CLIENT_SECRET` über `${SSO_CLIENT_SECRET:-}` zugewiesen. Wenn die `.env`-Datei nicht im selben Verzeichnis wie `docker-compose.yml` liegt oder Docker Compose sie nicht standardmäßig lädt, bleibt der Wert leer.
|
|
- **Diagnose:** Prüfen, ob die Variable auf dem Docker-Host in der `.env` existiert:
|
|
```bash
|
|
cat /volume2/docker/mischlabs/.env | grep SSO
|
|
```
|
|
|
|
#### Vermutung C: Netzwerkanfrage / CORS / SSL-Fehler beim Config-Call.
|
|
- **Grund:** Die Web-App (`https://michess.mischlabs.de`) versucht den Status von `https://michess-api.mischlabs.de/v1/auth/sso/config` abzufragen. Es könnte sein, dass dieser API-Call fehlschlägt (z.B. durch CORS-Sicherheitsrichtlinien, Cloudflare-Tunnel blockiert Requests mit Credentials, oder Mixed-Content-Fehler).
|
|
- **Diagnose:**
|
|
1. Öffne die Entwicklerkonsole des Browsers (F12) -> **Netzwerk-Tab (Network)**.
|
|
2. Lade die Seite neu und filtere nach `config` oder `sso`.
|
|
3. Prüfe den HTTP-Statuscode und die Antwort von `https://michess-api.mischlabs.de/v1/auth/sso/config`.
|
|
4. Kommt dort `{ "enabled": false }` zurück (dann ist es **Vermutung A/B**), oder scheitert der Request komplett (CORS/Netzwerkfehler)?
|
|
|
|
---
|
|
|
|
## Nächste Schritte
|
|
|
|
1. Prüfe die Browser-Konsole im Netzwerk-Tab, um zu sehen, ob die API-Antwort `{ enabled: false }` liefert oder blockiert wird.
|
|
2. Führe auf dem NAS `docker exec -it michess env | grep SSO` aus, um die Live-Umgebungsvariablen zu validieren.
|
|
3. Führe einen sauberen Docker-Neustart aus (`docker compose down && docker compose up -d`).
|
|
|
|
---
|
|
|
|
## SSO-Fix live geloest am 2026-05-21
|
|
|
|
### Final funktionierende NAS-Umgebung
|
|
|
|
In `/volume2/docker/mischlabs/.env`:
|
|
|
|
```env
|
|
SSO_AUTHORITY=https://auth.mischlabs.de/realms/mischlabs
|
|
SSO_CLIENT_ID=michess
|
|
SSO_CLIENT_SECRET=<keycloak_client_secret>
|
|
SSO_REDIRECT_URI=https://michess-api.mischlabs.de/v1/auth/sso/callback
|
|
APP_URL=https://michess.mischlabs.de
|
|
```
|
|
|
|
In `docker-compose.yml` im Service `michess` unter `environment:`:
|
|
|
|
```yaml
|
|
SSO_AUTHORITY: ${SSO_AUTHORITY:-https://auth.mischlabs.de/realms/mischlabs}
|
|
SSO_CLIENT_ID: ${SSO_CLIENT_ID:-michess}
|
|
SSO_CLIENT_SECRET: ${SSO_CLIENT_SECRET:-}
|
|
SSO_REDIRECT_URI: ${SSO_REDIRECT_URI:-https://michess-api.mischlabs.de/v1/auth/sso/callback}
|
|
APP_URL: ${APP_URL:-https://michess.mischlabs.de}
|
|
```
|
|
|
|
Nach Aenderungen:
|
|
|
|
```bash
|
|
cd /volume2/docker/mischlabs
|
|
docker compose up -d --force-recreate michess
|
|
docker exec michess env | grep SSO
|
|
curl https://michess-api.mischlabs.de/v1/auth/sso/config
|
|
```
|
|
|
|
Erwartete API-Antwort:
|
|
|
|
```json
|
|
{"enabled":true}
|
|
```
|
|
|
|
### Keycloak Client `michess`
|
|
|
|
```text
|
|
Client ID: michess
|
|
Valid redirect URI: https://michess-api.mischlabs.de/v1/auth/sso/callback
|
|
Web origin: https://michess.mischlabs.de
|
|
Client scopes: email Default, profile Default
|
|
```
|
|
|
|
### Fehlerverlauf
|
|
|
|
1. `docker exec michess env | grep SSO` war leer, obwohl `.env` SSO-Werte enthielt.
|
|
- Ursache: NAS-Compose reichte die Variablen nicht an den Container weiter.
|
|
- Fix: `SSO_*` und `APP_URL` in `docker-compose.yml` unter `environment:` ergaenzt.
|
|
|
|
2. `docker compose config` meldete YAML-Fehler.
|
|
- Ursache: falsche Einrueckung und ein abgeschnittener langer Wert in `nano`.
|
|
- Fix: Alle Environment-Zeilen mit korrekter Einrueckung im Mapping-Stil (`KEY: value`) gesetzt.
|
|
|
|
3. Compose warnte: `The "CMUe..." variable is not set`.
|
|
- Ursache: Das Secret war in Compose als `${ECHTES_SECRET}` eingetragen.
|
|
- Fix: `SSO_CLIENT_SECRET: ${SSO_CLIENT_SECRET:-}` verwenden. Das echte Secret gehoert in `.env`.
|
|
|
|
4. Button erschien, aber Callback landete auf `https://michess.mischlabs.de/v1/auth/sso/callback` und gab 404.
|
|
- Ursache: `SSO_REDIRECT_URI` zeigte auf die Frontend-Domain.
|
|
- Fix: Redirect URI auf API-Domain umgestellt.
|
|
|
|
5. `invalid_scope` wurde durch Default Scopes `email` und `profile` im Keycloak-Client geloest.
|
|
|
|
Kurzform: `SSO_REDIRECT_URI` zeigt auf `michess-api`, `APP_URL` zeigt auf `michess`.
|