# 1v1 Games Migration Plan (Nuxt → rebreak-native) Status: Recon abgeschlossen 2026-05-07. Read-only Analyse, kein Code-Touch. Author scope: Migration der bestehenden Nuxt-1v1-Implementierung (TicTacToe + Memory) aus `~/mono/trucko-monorepo/apps/rebreak/` in die neue React-Native-App `~/mono/rebreak-monorepo/apps/rebreak-native/`. Letzter Schritt vor finalem Nuxt-Cutover (DiGA). --- ## 1. Status quo Nuxt-Implementierung ### 1.1 Frontend (Vue/Nuxt) | Datei | Zweck | |---|---| | `~/mono/trucko-monorepo/apps/rebreak/app/pages/app/game/[challengeId].vue` (802 LOC) | Haupt-Game-Page. Lobby (Waiting), Live-Board für TicTacToe + Memory, Status, Lyra-Bubble, Tabs (History + Ranking), Rematch, Live-Share-Toggle. Subscribed Supabase-Realtime auf `rebreak.game_challenges`-Row. | | `~/mono/trucko-monorepo/apps/rebreak/app/components/sos/GameTicTacToe.vue` | Solo-Modus mit Lyra-AI. Enthält "Gegen echten Spieler"-Button (Z. 73-76) — POST `/api/games/challenge` + Redirect. | | `~/mono/trucko-monorepo/apps/rebreak/app/components/sos/GameMemory.vue` | Solo-Memory mit "Gegen echten Spieler"-Button (Z. 48-52) — POST `/api/games/challenge-memory` + Redirect. | | `~/mono/trucko-monorepo/apps/rebreak/app/components/CommunityPostCard.vue` | Rendert "Challenge annehmen"-Button für Community-Posts mit `category="challenge"` (Z. 288, 469-479). | | `~/mono/trucko-monorepo/apps/rebreak/app/stores/community.ts` | Pinia-Store, hält `challengeId` an Posts (Z. 8, 250). | **File-Count Frontend: 5 relevante Vue-Files (1 Page + 2 Solo-Game-Components mit 1v1-Hook + 1 PostCard + 1 Store).** ### 1.2 Backend (Nuxt-Server, Nitro) Backend liegt **nicht** in einem separaten trucko-backend-Service, sondern im selben Nuxt-Projekt unter `apps/rebreak/server/`. Endpoints: | Endpoint | File | |---|---| | `POST /api/games/challenge` | `server/api/games/challenge.post.ts` (38 LOC) — TicTacToe-Challenge erzeugen + Community-Post | | `POST /api/games/challenge-memory` | `server/api/games/challenge-memory.post.ts` (62 LOC) — Memory-Challenge erzeugen (16 Karten, shuffled) | | `GET /api/games/challenge/[id]` | `server/api/games/challenge/[id].get.ts` (16 LOC) — Lade Challenge-State | | `POST /api/games/challenge/[id]/accept` | `server/api/games/challenge/[id]/accept.post.ts` (35 LOC) — Gegner tritt bei, Status: OPEN → ACTIVE | | `POST /api/games/challenge/[id]/move` | `server/api/games/challenge/[id]/move.post.ts` (109 LOC) — TicTacToe-Move; Win-Check, Score-Update, Post-Cleanup | | `POST /api/games/challenge/[id]/memory-move` | `server/api/games/challenge/[id]/memory-move.post.ts` (152 LOC) — Memory-Move (Flip/Match/Mismatch) | | `POST /api/games/challenge/[id]/rematch` | `server/api/games/challenge/[id]/rematch.post.ts` (64 LOC) — Neue Challenge mit Gegner pre-set, status=ACTIVE | | `POST /api/games/challenge/[id]/live-toggle` | `server/api/games/challenge/[id]/live-toggle.post.ts` (35 LOC) — `isLive`-Flag für Spectators | | `GET /api/games/history` | `server/api/games/history.get.ts` (44 LOC) — Spielhistorie (alle, oder vs Gegner) | | `GET /api/games/ranking` | `server/api/games/ranking.get.ts` (15 LOC) — Top-Spieler-Liste | **File-Count Backend: 10 Endpoints, ~570 LOC.** ### 1.3 DB-Schema Aus `~/mono/trucko-monorepo/apps/rebreak/prisma/schema.prisma`: - `enum GameChallengeStatus` (Z. 424): `OPEN | ACTIVE | FINISHED | CANCELLED` - `model GameChallenge` (Z. 433-452, Tabelle `rebreak.game_challenges`): id, challengerId, challengerName, opponentId, opponentName, status, board (TEXT, default `---------`), currentTurn, winner, postId, gameType (default "tictactoe"), isLive, memoryState (Json), timestamps. - `model GameScore` (Z. 470, Tabelle `rebreak.game_scores`): userId PK, playerName, wins, losses, draws, points (3 für Sieg, 1 für Unentschieden). - `model GameRating` (Z. 483) und `GameHighScore` (Z. 496) — gehören zum Solo-Mode, irrelevant für 1v1, aber bereits portiert. Migrations-SQL: - `~/mono/trucko-monorepo/apps/rebreak/prisma/migrations/add_game_challenges.sql` — Enum, Table, Indexes, `ALTER PUBLICATION supabase_realtime ADD TABLE rebreak.game_challenges` - `~/mono/trucko-monorepo/apps/rebreak/prisma/migrations/add_game_challenges_rls.sql` — RLS-Policies (read/insert/update für challenger + opponent via `auth.uid()`) - Spätere Patches haben `gameType`, `isLive`, `memoryState` hinzugefügt (in den live-DB Tabelle vorhanden, kein eigenes Migration-File gefunden — Schema-Drift-Verdacht in Nuxt). ### 1.4 State-Sync-Mechanismus (1 Satz) **Server-authoritative State in Postgres (`rebreak.game_challenges`-Row); Frontend mutiert via REST-POST und subscribed parallel auf Supabase-Realtime `postgres_changes` UPDATE-Events der eigenen Row → keine Polling, keine WebSocket-Eigenbau.** ### 1.5 Datenflussdiagramm (ASCII) ``` Spieler A (Challenger) Spieler B (Opponent) ───────────────────── ───────────────────── │ │ POST /api/games/challenge │ │ │ ▼ │ ┌──────────────────────┐ │ │ game_challenges │ communityPost.challengeId │ │ status=OPEN │◀────────────────────────────────┐│ │ board=--------- │ ││ └──────────┬───────────┘ ││ │ ▼ │ GET /api/community/posts │ (sees challenge card) │ │ │ POST /api/games/challenge/[id]/accept │ │ ▼ ▼ ┌────────────────────────────────────────────────────────────┐ │ game_challenges status=ACTIVE opponent_id=B │ └──────────────────────────┬─────────────────────────────────┘ │ │ Supabase Realtime (postgres_changes) │ channel = `game::` │ filter = id=eq. ▼ ┌──────────────────────────────┐ │ both clients update UI │ └──────────┬───────────────────┘ │ loop until FINISHED: │ POST /api/games/challenge/[id]/move (or memory-move) │ ▼ ┌────────────────────────────────────────────────────────────┐ │ Server validates turn + writes new board / memoryState │ │ on win/draw → upsert game_scores, delete community post │ └──────────────────────────┬─────────────────────────────────┘ │ Realtime UPDATE → both clients ▼ ┌──────────────────────────────┐ │ FINISHED screen + Rematch │ └──────────────────────────────┘ ``` --- ## 2. Migration-Plan ### Phase A — Backend-Endpoints in rebreak-monorepo **Status: BEREITS PORTIERT.** Verifiziert per `diff`: - `~/mono/rebreak-monorepo/backend/server/api/games/challenge.post.ts` ist byte-identisch mit Nuxt. - `~/mono/rebreak-monorepo/backend/server/api/games/challenge/[id]/move.post.ts` ist byte-identisch. - Alle 10 Endpoints existieren bereits unter `~/mono/rebreak-monorepo/backend/server/api/games/`. → **Aufwand Phase A: 0 h.** Nur ein leichter Smoke-Test (curl Request mit Bearer-Token gegen den staging-Nitro) zur Bestätigung dass die Endpoints im Nitro-Prod-Build aktiv sind. ### Phase B — DB-Migrations für game_sessions **Status: BEREITS PORTIERT.** Schema verifiziert: - `enum GameChallengeStatus` in `~/mono/rebreak-monorepo/backend/prisma/schema.prisma` Z. 424 vorhanden. - `model GameChallenge`, `GameScore`, `GameRating`, `GameHighScore` alle vorhanden. **Offene Punkte (klein):** 1. SQL-Migration unter `backend/prisma/migrations/` muss verifiziert werden — sind `gameType`, `isLive`, `memoryState`-Spalten in einer eigenen Migration angelegt? Falls nein: ein konsolidiertes `add_game_challenges.sql` nachziehen. 2. RLS-Policies und `ALTER PUBLICATION supabase_realtime ADD TABLE rebreak.game_challenges` müssen am Staging-DB-Cluster bestätigt werden (gleicher DB für Nuxt + RN-Backend, also vermutlich schon aktiv). → **Aufwand Phase B: 1-2 h** (SQL-Audit + ggf. ein Catch-up-Migration-File). ### Phase C — RN-UI-Komponenten **Status: KOMPLETT NEU.** RN-App hat aktuell: - `apps/rebreak-native/components/urge/UrgeGames.tsx` (1067 LOC) — Solo-Mode für Memory/TicTacToe/Snake/Tetris. - `apps/rebreak-native/app/games.tsx` — Standalone-Games-Page (Solo). - KEIN Community-Komponent, KEIN Game-Page für 1v1. **Zu erstellen:** 1. `apps/rebreak-native/app/(app)/game/[challengeId].tsx` — Pendant zu `pages/app/game/[challengeId].vue`. RN-Expo-Router-File. Ports: - Loading + Lobby (`OPEN`-Status, Waiting-Screen mit Cancel-Button) - TicTacToe-Board (3x3 Grid, X/O-Marker, WinLine-Highlight) — `Pressable`-Cells statt `