Dev-Idle Terminal Game — Un jeu idle/clicker sur le thème du développement, avec un backend NestJS distribué (BullMQ, Redis, NATS, PostgreSQL) et deux frontends : une app Electron desktop et un frontend web Next.js.
Documentation architecture complète : docs/ARCHITECTURE.md
- Architecture
- Stack Technique
- Ports & Services
- Installation
- Lancement Complet
- API Reference
- Configuration (.env)
- Sécurité & Anti-Cheat
┌─────────────────────────────────────────────────────────────────────┐
│ CLIENTS │
│ │
│ ┌──────────────────┐ ┌──────────────────────────────────┐ │
│ │ Desktop (Electron)│ │ Web (Next.js :3001) │ │
│ │ :4000 (Vite dev) │ │ - Landing / Login │ │
│ │ - Keylogger local│ │ - Dashboard (stats réelles) │ │
│ │ - Boutique │ │ - Jeu (Socket.IO) │ │
│ │ - Leaderboard │ │ - Classement │ │
│ └────────┬──────────┘ └──────────────┬───────────────────┘ │
└───────────┼──────────────────────────────────┼─────────────────────┘
│ HTTP REST + WebSocket │ HTTP REST + WebSocket
▼ ▼
┌─────────────────────────────────────────────────────────────────────┐
│ API-GATEWAY :3000 │
│ ┌────────────────┐ ┌─────────────────┐ ┌──────────────────────┐ │
│ │ Auth JWT │ │ WebSocket /game│ │ REST Ingest │ │
│ │ /auth/login │ │ KEY_PRESS │ │ POST /ingest/key │ │
│ │ /auth/register │ │ CLICK_PROCESSED│ │ (keylogger desktop) │ │
│ │ /auth/me │ │ LEADERBOARD │ └──────────────────────┘ │
│ └────────────────┘ └─────────────────┘ │
│ ┌──────────────────────────────────────────────────────────────┐ │
│ │ REST Progression : /progression/me /progression/leaderboard│ │
│ └──────────────────────────────────────────────────────────────┘ │
└─────────────────────────┬───────────────────────────────────────────┘
│ NATS (publish/subscribe + request/reply)
┌───────────────┼────────────────────────────┐
▼ ▼ ▼
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────────┐
│ svc-user- │ │ worker-game-loop │ │ svc-payment │
│ progression │ │ (BullMQ workers) │ │ :3003 │
│ NATS microservice│ │ - click-buffer │ │ - Stripe Webhook │
│ - getProgression │ │ - program-proc. │ │ - Provisioning │
│ - updateBalance │ │ - offline-calc. │ │ (LoC/items/boosts) │
│ - addExperience │ └────────┬─────────┘ └──────────────────────┘
│ - addItem │ │ NATS
│ - leaderboard │ ←────────┘
└──────────┬───────┘
│ Prisma
▼
┌─────────────────┐ ┌──────────────────┐
│ PostgreSQL │ │ Redis │
│ :5432 │ │ :6379 │
│ Users │ │ - Click buffers │
│ Progressions │ │ - Leaderboards │
│ OwnedItems │ │ - Sessions JWT │
│ Transactions │ │ - Boosts actifs │
└─────────────────┘ │ - Abonnements │
│ - BullMQ queues │
└──────────────────┘
| Composant | Technologie |
|---|---|
| Framework backend | NestJS (monorepo Turborepo) |
| Base de données | PostgreSQL 16 + Prisma ORM |
| Cache & Message Bus | Redis 7 (Pub/Sub, Sorted Sets, BullMQ) |
| Microservices | NATS 2 |
| File d'attente | BullMQ |
| WebSocket | Socket.IO |
| Auth | JWT + Passport |
| Frontend web | Next.js 16 (App Router) |
| Frontend desktop | Electron 35 + Vite + React |
| Paiements | Stripe |
| Containers | Docker + Docker Compose |
| Package Manager | pnpm 8 + Turborepo |
| Service | Port | Description |
|---|---|---|
| API Gateway | 3000 |
HTTP REST + WebSocket /game |
| svc-user-progression | NATS only | Microservice NATS (pas d'HTTP en dev) |
| worker-game-loop | — | BullMQ workers (pas d'HTTP) |
| svc-payment | 3003 |
HTTP REST + Stripe Webhook |
| Web (Next.js) | 3001 |
Frontend web (dev) |
| Desktop (Vite) | 4000 |
Renderer Electron (dev) |
| PostgreSQL | 5432 |
Base de données |
| Redis | 6379 |
Cache + BullMQ + Pub/Sub |
| NATS | 4222 |
Message bus inter-services |
| NATS monitoring | 8222 |
Dashboard NATS (HTTP) |
| Redis Commander | 8081 |
GUI Redis (profil dev) |
- Node.js 18+
- pnpm 8+ (
npm install -g pnpm@8) - Docker Desktop
git clone <repository-url>
cd Timeless-Heroes
# Installer les dépendances
pnpm installWindows uniquement : Si
pnpm installéchoue avecEBUSY: resource busy or lockedsurnode_modules/electron/dist/resources/default_app.asar, ferme tous les processus Electron et réessaie. Si le problème persiste, l'antivirus Windows bloque parfois ce fichier temporairement.
cp .env.example .envÉdite .env et remplace au minimum :
JWT_SECRET=un-secret-de-32-caracteres-minimum-iciLes autres valeurs fonctionnent par défaut pour le développement local.
# Démarrer PostgreSQL
docker-compose up -d postgres
# Générer le client Prisma
pnpm db:generate
# Appliquer les migrations
pnpm db:migrateIl y a deux modes : tout en Docker (recommandé pour tester) ou infra Docker + services locaux (pour développer).
Ce mode démarre tout le backend dans Docker, puis les frontends localement.
# Depuis la racine du projet
docker-compose up -dCela démarre :
- PostgreSQL
:5432 - Redis
:6379 - NATS
:4222+ monitoring:8222 - API Gateway
:3000 - svc-user-progression (NATS)
- worker-game-loop (BullMQ)
- svc-payment
:3003
Vérifier que tout est healthy :
docker-compose psOptionnel — activer Redis Commander (GUI) :
docker-compose --profile dev up -d redis-commander
# Accessible sur http://localhost:8081Dans un nouveau terminal :
pnpm -F web dev
# ou
cd apps/web && pnpm devAccessible sur http://localhost:3001
Dans un nouveau terminal :
cd apps/desktop && pnpm devCela lance :
- Le serveur Vite sur http://localhost:4000 (renderer React)
- L'app Electron qui charge
localhost:4000
L'app apparaît dans la barre des tâches Windows sous forme de widget compact.
Pour le hot-reload sur tous les services backend :
pnpm infra:up
# équivalent de : docker-compose up -d postgres redis natspnpm devTurbo démarre en parallèle :
api-gatewaysur:3000svc-user-progression(NATS)worker-game-loop(BullMQ)svc-paymentsur:3003websur:3001desktopsur:4000(Vite) + Electron
Attention : En mode
pnpm dev,svc-user-progressionutilise le port 3001 comme port NATS d'écoute interne. Il n'y a pas de conflit avec Next.js car le service progression ne monte pas de serveur HTTP en dev.
Un script batch est disponible à la racine :
START-GAME.bat
Il :
- Vérifie que Docker est lancé
- Démarre tout le backend via
docker-compose up -d - Ouvre l'app Electron dans un nouveau terminal
curl -X POST http://localhost:3000/api/v1/auth/register \
-H "Content-Type: application/json" \
-d '{"email":"player@example.com","password":"secret123","username":"Player1"}'Réponse : { "access_token": "eyJ..." }
curl -X POST http://localhost:3000/api/v1/auth/login \
-H "Content-Type: application/json" \
-d '{"email":"player@example.com","password":"secret123"}'- Aller sur http://localhost:3001
- Créer un compte ou se connecter
- Redirection automatique vers le dashboard
- Cliquer sur "Jouer" pour accéder au jeu Socket.IO
- Lancer START-GAME.bat ou
cd apps/desktop && pnpm dev - Cliquer sur l'icône du widget dans la barre des tâches
- Aller dans l'onglet "Connexion" et entrer ses identifiants
- Les frappes clavier sont automatiquement envoyées au backend
POST /api/v1/auth/register
Body: { "email": string, "password": string, "username": string }
Response: { "access_token": string }
POST /api/v1/auth/login
Body: { "email": string, "password": string }
Response: { "access_token": string }
GET /api/v1/auth/me
Headers: Authorization: Bearer <token>
Response: { "userId": string, "email": string, "username": string }GET /api/v1/progression/me
Headers: Authorization: Bearer <token>
Response: {
"userId": string,
"linesOfCode": string,
"level": number,
"experience": string,
"clickMultiplier": number,
"passiveMultiplier": number,
"criticalChance": number,
"criticalMultiplier": number
}
GET /api/v1/progression/leaderboard?type=GLOBAL
Headers: Authorization: Bearer <token>
Query: type = GLOBAL | WEEKLY | DAILY
Response: {
"success": true,
"data": {
"type": "GLOBAL",
"entries": [{ "userId": string, "score": number, "rank": number }]
}
}POST /api/v1/ingest/auth
Headers: Authorization: Bearer <token>
Body: {}
Response: { "sessionId": string }
POST /api/v1/ingest/key
Headers: Authorization: Bearer <token>
Body: {
"sessionId": string,
"keyCategory": "CHAR" | "ENTER" | "SPACE" | "TAB" | "MODIFIER" | "FUNCTION" | "NAVIGATION" | "BACKSPACE" | "UNKNOWN",
"timestamp": number
}// Connexion avec auth JWT
const socket = io('http://localhost:3000/game', {
auth: { token: 'eyJ...' }
});
// Envoyer une frappe
socket.emit('KEY_PRESS', { timestamp: Date.now() });
// Écouter les mises à jour
socket.on('CLICK_PROCESSED', (data) => {
// { finalValue, newBalance, isCritical, multiplier }
});
socket.on('BALANCE_UPDATE', (data) => {
// { linesOfCode, level, experience }
});
socket.on('LEADERBOARD_UPDATE', (data) => {
// [{ userId, username, score, rank, level }]
});
socket.on('OFFLINE_REWARDS', (data) => {
// { locEarned, xpEarned, durationSeconds }
});| Variable | Description | Défaut |
|---|---|---|
DATABASE_URL |
URL PostgreSQL complète | postgresql://timeless:timeless_secret@localhost:5432/timeless_heroes |
REDIS_HOST |
Host Redis | localhost |
REDIS_PORT |
Port Redis | 6379 |
REDIS_PASSWORD |
Mot de passe Redis | redis_secret |
JWT_SECRET |
Requis — min 32 caractères | — |
JWT_EXPIRES_IN |
Durée du token | 7d |
NATS_URL |
URL du serveur NATS | nats://localhost:4222 |
MAX_CPS |
Clics max par seconde (anti-cheat) | 20 |
STRIPE_SECRET_KEY |
Clé secrète Stripe | sk_test_... |
STRIPE_WEBHOOK_SECRET |
Secret webhook Stripe | whsec_... |
- Anonymisation : seule la catégorie de touche est envoyée (CHAR, ENTER, TAB...), jamais la touche réelle ni son code
- Authentification JWT : obligatoire avant tout envoi
- Anti-cheat : détection heuristique des bots
- Max 20 CPS (configurable via
MAX_CPS) - Intervalle minimum 30ms entre deux touches
- Régularité de timing suspecte → ban temporaire
- Max 20 CPS (configurable via
Les boosts et abonnements achetés sont stockés dans Redis avec TTL :
boost:{userId}:{boostType} → JSON { multiplier, activatedAt, expiresAt } — TTL = durationSeconds
subscription:{userId} → JSON { type, activatedAt, expiresAt } — TTL = durationDays × 86400
Multiplicateurs d'abonnement : PREMIUM=1.5× · VIP=2.0× · ELITE=3.0×
Chaque webhook Stripe est traité exactement une fois grâce à une clé d'idempotence Redis :
idempotency:{key} → { status: PROCESSING | COMPLETED | FAILED, createdAt } — TTL 7 jours
lock:payment:{key} → distributed lock (30s TTL)
- Intégration gRPC pour communication inter-services
- Système de prestige
- Événements temporaires
- Achievements (infrastructure Redis déjà en place)
- Guildes / Équipes
- Mode compétitif PvP
MIT © Timeless-Heroes Team