Files
CaseGambling/amelioration.md
T
2026-07-26 18:43:51 +02:00

245 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# CaseOrion — Analyse & améliorations
**Date :** 2026-07-25 · **MAJ :** 2026-07-26
**Périmètre :** analyse complète (server + client + Docker), **sans modification de code**.
**Stack :** Express / Prisma / SQLite / Socket.IO · React (Vite) · ~16k LOC client+server.
---
## Synthèse
Le projet est une API jeu cohérente en **mono-process**, avec un effort sérieux sur l’économie haute (crédits en **strings + BigInt** via `cents.js`).
Les risques les plus graves restants : **paiements mock en prod**, **`db push --accept-data-loss` au boot**, et **état in-memory** (sessions, timers battles, présence).
| Zone | État |
|------|------|
| Cases / wear / keys | Solide (roll serveur, BigInt value) |
| Auto-sell | Logique OK (`value < seuil`) ; UX/confusion catalogue vs instance |
| Battle (jackpot only) | Un seul mode ; 1vX / coinflip / duel **retirés** |
| Vault / prestige | Fonctionnel ; settle concurrent risqué |
| Shop | Mock OK en dev ; dangereux si prod |
| Docker | Marche en single-node ; pas scale-ready |
---
## P0 — Critique (corriger en premier)
### ~~1. Odds des duels faussées (multi-items)~~ — **résolu par suppression**
Le mode **1vX Case** (rooms custom / `duel.js`) a été **retiré** (2026-07-25). Il ne reste que le Battle MultiPlayers (`jackpot.js`). Les anciennes routes `/battle/1vx` et `/battle/coinflip` redirigent vers `/battle`.
### 1. Paiements mock utilisables en production — **en cours (B Stripe)**
**Fichiers :** `server/src/payments.js`, `routes/shop.js`, `index.js` (webhook raw body), `.env.prod.example`
Stripe Checkout + webhook `checkout.session.completed` branchés. En prod Docker : `PAYMENTS_PROVIDER=stripe` + `STRIPE_*` keys + `STRIPE_WEBHOOK_SECRET` + URL publique HTTPS vers `/api/shop/osu/webhook`.
Reste : désactiver mock en prod (A) si quelquun oublie `PAYMENTS_PROVIDER` ; bascule live keys quand le compte est vérifié.
---
### 3. `prisma db push --accept-data-loss` à chaque démarrage Docker
**Fichiers :** `Dockerfile`, `docker-compose.yml`
Peut **détruire / tronquer** des colonnes au restart. Inadapté à une vraie prod.
| Proposition | A (recommandé) : Prisma Migrate (`migrate deploy`) ; push manuel hors boot · B : push **sans** `--accept-data-loss` + fail loud · C : job one-shot de migration séparé du `CMD` app |
---
### ~~4. `db.js` coerce tous les BigInt → `Number`~~ — **corrigé**
`reviveMoney` **supprimé**. Choix retenu : argent en **strings** + `toCentsBigInt` (pas de plafond `Number`), BigInt Prisma éventuels laissés intacts jusquau JSON (`toString` dans `index.js`). Pas de conversion globale silencieuse.
---
### ~~5. Uploads SVG → XSS stocké~~ — **corrigé**
Allowlist **PNG / JPEG / WebP / GIF** (MIME + extension + vérif `sharp` du format réel). SVG et autres `image/*` refusés (admin + avatar).
---
## P1 — Économie & précision (haute priorité)
### ~~6. Finir la migration argent BigInt-safe (bout en bout)~~ — **corrigé (A)**
Helpers client dans `api.js` : `toCentsBigInt`, `compareCents`, `centsJson` (miroir serveur). Branchés sur ItemTile, CasePage, Dashboard, Battle, Prestige shortfall, Admin sorts/inputs. Affichage via les mêmes helpers.
---
### ~~7. Courses sur le solde (TOCTOU)~~ — **corrigé (A)**
`spendUserBalance` / `creditUserBalance` / `adjustUserBalance` dans `cents.js` : update SQL atomique `balance = balance ± x WHERE balance >= cost` (INTEGER SQLite) ou CAS string pour montants > Int64. Branché open case, prestige, sell/auto-sell, vault, shop, jackpot.
Même famille restante : double settle vault (login + socket + intervalle) — le crédit est atomique, mais le calcul de ticks peut encore se chevaucher.
---
### ~~8. Auto-sell — clarté & robustesse~~ — **partiel (A)**
Won banner case : **toujours** wear + valeur dinstance ; si auto-sold, ajoute aussi le payout.
Reste ouvert : B immédiat serveur · C TTL claim · D confirm prestige.
---
### ~~9. Colonnes prestige mortes / non branchées~~ — **corrigé (B)**
Supprimées du schema + reset server-prestige : `prestigeKeyOpenReduction`, `prestigeStartBonus`, `prestigeAdBonus`, `prestigeAdCooldownReduction`, `prestigeShopDiscount`, `prestigeStartOsu`, `prestigeDropValueBonus`, `prestigeCaseDiscount`.
---
## P2 — Sécurité & prod
### 10. Sessions MemoryStore
Perdues au restart ; incompatible multi-replicas.
| Proposition | A : SQLite/Redis store · B : sticky single node documenté · C : JWT (changement plus large) |
### 11. CORS trop permissif
`index.js` autorise toujours localhost + LAN HTTP, même “prod”.
| Proposition | A : en prod, **uniquement** `CLIENT_ORIGIN` + `CORS_ORIGINS` |
### 12. Rate limiting absent
Login / register / open / ad / shop confirm non limités.
| Proposition | A : `express-rate-limit` par IP+user · B : captcha register · C : cooldowns serveur déjà partiels (ad) à généraliser |
### 13. Admin via `session.role` sans re-check DB
Élévation stale possible.
| Proposition | A : `requireAdmin` recharge le user · B : claim `roleVersion` en session |
### 14. Secrets & config
`SESSION_SECRET=dev-secret` par défaut ; pas de `server/.env.example` local (seulement README + `.env.prod.example`).
| Proposition | A : fail boot si secret faible en prod · B : ajouter `.env.example` |
### 15. Mots de passe faibles
Min 6 caractères seulement.
| Proposition | A : min 8 + zxcvbn soft · B : garder simple (jeu privé) mais documenter |
---
## P3 — Qualité code / refactor
### ~~16. Duplication jackpot ↔ duel~~ — **obsolète**
`duel.js` / 1vX / coinflip **retirés** ; un seul type de battle (`services/jackpot.js`). Plus de duplication cross-mode à factoriser.
| Ancienne prop. | A : `services/battleCommon.js` · B : unifier modèle “room” — **non pertinent** tant quil ny a quun mode |
Si un 2ᵉ mode battle revient plus tard, reprendre **A** dabord (helpers serialize / finish / auto-sell), **B** seulement si les rooms divergent vraiment.
### 17. `serializeInv` dupliqué
`inventory.js` / `profile.js` (et variantes).
| Proposition | **A (retenu)** : `serializeInventoryItem` partagé (module commun serveur) |
### 18. Client — listeners `socket.on('connect')` jamais retirés
**Fichiers :** `Layout.jsx`, `BattleHub.jsx`, `BattleRoomPage.jsx`, `DropFeed.jsx`
(les pages Duel* nexistent plus)
Les `useEffect` font souvent `socket.off` des events métier, mais **pas** du handler `connect` → fuite au remount / Strict Mode.
| Proposition | **A (retenu)** : nommer le handler `onConnect` et `socket.off('connect', onConnect)` dans chaque cleanup |
### ~~19. Battle hub incomplet~~ — **résolu / simplifié**
Hub = **un seul** mode Battle (jackpot MultiPlayers). 1vX et Coinflip **nexistent plus** (redirects legacy vers `/battle`). Plus de cartes stub / copy multi-modes à aligner.
---
### 20. Guards auth copiés partout
Chaque page : loading / !user / admin redirect.
| Proposition | A : `<RequireAuth>` / `<RequirePlayer>` · B : loader React Router |
### 21. CasePage — complexité
State machine respin/auto-sell/announce via refs : difficile à maintenir, mais globalement correcte.
| Proposition | A : extraire hook `useCaseOpenFlow` · B : machine XState · C : laisser tel quel + tests dintégration |
### 22. Admin UX
- Inputs prix en cents bruts + `Number`
- Pas d’édition packs crédits / ads (seulement osu packs)
- Adjust player sans confirm
- Pas de lien “voir le site joueur”
| Proposition | A : champs crédits + confirm adjust · B : admin shop packs crédits |
### 23. Tests absents
Aucun test automatisé visible sur la logique critique (odds, cents, auto-sell, vault).
| Proposition | A : Vitest unitaire `cents` / `wear` / `autoSell` / jackpot weights · B : smoke API Playwright |
### 24. Observabilité
`console.error` local ; pas de métriques / tracing.
| Proposition | A : structured logs (pino) + request id · B : health DB ready |
---
## P4 — Produit / design (nice to have)
| # | Sujet | Proposition |
|---|--------|-------------|
| 25 | Prestige sans confirm | Dialog “tu perds inventaire + keys” |
| 26 | Catalog conservé au prestige | Documenter (déjà le cas) ou option reset |
| 27 | Favoris bettables | Clarifier UI ou bloquer en battle |
| 28 | Feed montre parfois FT | Afficher valeur dinstance |
| 29 | Lazy routes / code-split | `React.lazy` admin vs player |
| 30 | SQLite scale | Postgres si charge / multi-instance |
| 31 | Achievements / shop ads hardcodés | Config admin |
| 32 | Animation CaseReel perf | Lazy images ; réduire slots si `animReduction` |
---
## Vérification logique (résumé métier)
| Système | Verdict |
|---------|---------|
| **Drop case** | Serveur fait foi ; luck = catalog chapters + Fortune |
| **Wear → prix** | Correct (FT = ref) ; Polish/Quality biaisent vers le haut |
| **Auto-sell** | `instance < seuil` ; Yield après coup ; respin + auto-sell : keepers (≥ seuil) conservés |
| **Keys** | Progression documentée ; reset keys au prestige joueur, **totalOpens conservé** |
| **Vault** | Timer partagé ; risque double settle |
| **Battle / Jackpot** | Seul mode battle ; poids valeur OK |
| **1vX / Duel / Coinflip** | **Retirés** |
| **Prestige joueur** | Wipe bal/inv/keys ; keep skills/Pr/catalog/**opens-by-case** |
| **Server prestige** | Wipe économie globale ; keep account/achievements/stats |
---
## Roadmap proposée (ordre dexécution)
```
Semaine 1 (P0 restant)
├─ Gate mock payments en prod
└─ Plan migration Docker (stop accept-data-loss au boot)
Semaine 2 (P2P3)
├─ Rate limit + CORS prod + session store
├─ serializeInventoryItem partagé
├─ socket.off('connect', onConnect) (Layout, Battle*, DropFeed)
├─ Tests unitaires cents / wear / autoSell / jackpot
└─ Confirms UX (prestige, admin adjust)
```
---
## Alternatives darchitecture (si le projet grossit)
| Option | Quand | Coût |
|--------|-------|------|
| **Rester mono SQLite** | LAN / petits effectifs | Faible — documenter single-node |
| **Postgres + Redis** (sessions, presence, timers) | Multi-instances / uptime | Moyen |
| **Queue économie** (BullMQ) | Anti race balance/vault | Moyen |
| **Séparer worker** (spins, vault ticks) | Charge Socket.IO | Élevé |
| **Stripe live** | Monétisation réelle | Moyen + conformité |
---
## Hors scope de cette analyse
- Audit pentest complet / dépendances CVE détaillées
- Refonte UI/UX visuelle
- Réintroduction Coinflip / 1vX (non prévu)
- Contenu (balancing drops / prix items)
---
*Document de pilotage. Prioriser P0 restant avant tout déploiement Docker “réel”.*