Files
CaseGambling/amelioration.md
T
2026-07-25 20:04:34 +02:00

256 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
**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 sont : **odds duel cassées**, **paiements mock en prod**, **`db push --accept-data-loss` au boot**, **comparaisons argent via `Number()`**, 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 |
| Jackpot / Battle | Agrégation BigInt correcte |
| Duels 1vX | **Supprimé** (2026-07-25) |
| 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` redirigent vers `/battle`.
### 1. Paiements mock utilisables en production
**Fichiers :** `server/src/payments.js`, `routes/shop.js`, `.env.prod.example`
`PAYMENTS_PROVIDER=mock` + `POST /api/shop/osu/confirm` crédite losu **sans paiement**.
| Proposition | Détail |
|-------------|--------|
| **A** | Refuser mock si `NODE_ENV=production` sauf `ALLOW_MOCK_PAYMENTS=1` |
| B | Brancher Stripe (webhook raw body + signature) |
| C | Désactiver toute la section osu en prod tant que non prêt |
---
### 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 | Détail |
|-------------|--------|
| **A (recommandé)** | Prisma Migrate (`migrate deploy`) ; push manuel hors boot |
| B | Garder 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
Serialize, finish spin, auto-sell winner : quasi copy-paste.
| Proposition | A : module `services/battleCommon.js` · B : unifier modèle “room” (plus gros refactor) |
### 17. `serializeInv` dupliqué
`inventory.js` / `profile.js` (et variantes).
| Proposition | A : `serializeInventoryItem` partagé |
### 18. Client — listeners `socket.on('connect')` jamais retirés
Layout, BattleHub, BattleRoom, Duel*, DropFeed → fuite de handlers au remount.
| Proposition | A : cleanup `socket.off` dans chaque `useEffect` |
### 19. Battle hub incomplet
Routes 1vX existent ; hub nexpose que MultiPlayers + Coinflip stub. Copy “MultiPlayers” / redirects legacy.
| Proposition | A : cartes hub 1vX + Coinflip disabled clair · B : retirer Coinflip jusqu’à implémentation |
### 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` / duel 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 ; pas de bug “35T vendu sous 15T” si prestige max + vrai 35T instance |
| **Keys** | Progression documentée ; `keyOpenReduction` non branché |
| **Vault** | Timer partagé ; risque double settle |
| **Jackpot** | Poids valeur OK |
| **Duel** | **Poids multi-items KO** |
| **Prestige joueur** | Wipe bal/inv/keys ; keep skills/Pr/**catalog** |
| **Server prestige** | Wipe économie globale ; keep account/achievements/stats |
---
## Roadmap proposée (ordre dexécution)
```
Semaine 1 (P0)
├─ Fix duel toCentsBigInt + test
├─ Gate mock payments en prod
├─ Retirer reviveMoney Number(bigint)
├─ Allowlist uploads (no SVG)
└─ Plan migration Docker (stop accept-data-loss au boot)
Semaine 2 (P1)
├─ BigInt-safe client (ItemTile, sorts, prestige shortfall, admin inputs)
├─ Balance updates atomiques + vault settle idempotent
└─ Brancher ou supprimer prestige fields morts
Semaine 3 (P2P3)
├─ Rate limit + CORS prod + session store
├─ Refactor battle common + socket cleanup
├─ Hub 1vX + tests unitaires cents/odds
└─ 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
- Implémentation Coinflip
- Contenu (balancing drops / prix items)
---
*Document généré pour pilotage : aucune modification code associée. Prioriser P0 avant tout déploiement Docker “réel”.*