Configuration et environnement
Vue d'ensemble
MathQuest utilise plusieurs fichiers de configuration et variables d'environnement pour gérer différents aspects de l'application. Cette section détaille toutes les options de configuration disponibles.
Variables d'environnement
Variables obligatoires
Base de données
# URL de connexion PostgreSQL
DATABASE_URL="postgresql://username:password@localhost:5432/mathquest"
Cache Redis
# URL de connexion Redis
REDIS_URL="redis://localhost:6379"
Authentification JWT
# Clé secrète pour les tokens JWT (minimum 32 caractères)
JWT_SECRET="your_super_secure_jwt_secret_key_here"
Variables recommandées
Configuration serveur
# Port d'écoute du serveur (défaut: 3007)
PORT=3007
# Niveau de log (DEBUG, INFO, WARN, ERROR)
LOG_LEVEL=INFO
# URL du frontend pour CORS (production)
FRONTEND_URL="https://yourdomain.com"
# Domaine de l'application
APP_DOMAIN="yourdomain.com"
APP_NAME="MathQuest"
Sécurité
# Configuration email (Brevo/Sendinblue)
BREVO_API_KEY="your_brevo_api_key"
BREVO_SENDER_EMAIL="noreply@yourdomain.com"
BREVO_SENDER_NAME="MathQuest"
Email et vérification
# Durée d'expiration des tokens
EMAIL_VERIFICATION_TOKEN_EXPIRY="24h"
PASSWORD_RESET_TOKEN_EXPIRY="1h"
Protection anti-abus et limites de création
# Limites de création (defaults système)
DEFAULT_GAME_TEMPLATE_LIMIT=100
DEFAULT_GAME_INSTANCE_LIMIT=50
# Limites invité (création de sessions pratique)
GUEST_PRACTICE_IP_LIMIT_PER_MINUTE=360
GUEST_PRACTICE_IP_LIMIT_PER_HOUR=1200
GUEST_PRACTICE_COOKIE_LIMIT_PER_HOUR=120
# Rate limiting REST API
ENABLE_API_RATE_LIMIT=true
ENABLE_API_RATE_LIMIT_IN_TEST=false
API_RATE_LIMIT_WINDOW_MS=60000
API_RATE_LIMIT_MAX_REQUESTS=300
API_RATE_LIMIT_KEY_PREFIX=limits:api:requests
# Cleanup quotidien des artefacts invités abandonnés
ENABLE_GUEST_ARTIFACT_CLEANUP=true
GUEST_ARTIFACT_CLEANUP_HOUR=3
GUEST_ARTIFACT_CLEANUP_MINUTE=30
GUEST_ARTIFACT_CLEANUP_STALE_HOURS=72
# Import externe MathALÉA
EXTERNAL_DRAFT_ALLOWED_ORIGINS=https://coopmaths.fr,https://www.coopmaths.fr
# Nettoyage différé des sessions MathALÉA terminées
ENABLE_MATHALEA_SESSION_CLEANUP=true
MATHALEA_SESSION_CLEANUP_POLL_MS=30000
MATHALEA_SESSION_CLEANUP_DELAY_MS=60000
Notes:
- Les limites enseignant peuvent être surchargées au niveau profil (
TeacherProfile.gameTemplateLimit,TeacherProfile.gameInstanceLimit). - Le cleanup invité est volontairement conservateur: il cible les artefacts abandonnés et ne doit pas supprimer l'historique des parties terminées.
EXTERNAL_DRAFT_ALLOWED_ORIGINSdéfinit les origines autorisées à appelerPOST /api/v1/external-drafts.MATHALEA_SESSION_CLEANUP_POLL_MScontrôle la fréquence de passage du scheduler de nettoyage des sessions importées terminées.MATHALEA_SESSION_CLEANUP_DELAY_MSdéfinit le délai minimal entre la fin de session et le nettoyage effectif, afin de laisser le temps au leaderboard de se charger.
Fichiers de configuration
PM2 Ecosystem Configuration
Le fichier ecosystem.config.js configure le déploiement avec PM2 :
module.exports = {
apps: [
{
name: "mathquest-backend",
script: "npm",
cwd: "./backend",
args: "run start",
env: {
NODE_ENV: "production",
REDIS_URL: "redis://localhost:6379",
DATABASE_URL: "postgresql://...",
JWT_SECRET: "...",
FRONTEND_URL: "https://yourdomain.com"
},
log_file: "./logs/pm2-backend.log",
out_file: "./logs/pm2-backend-out.log",
error_file: "./logs/pm2-backend-error.log",
log_date_format: "YYYY-MM-DD HH:mm:ss Z",
merge_logs: true,
max_memory_restart: "400M"
},
{
name: "mathquest-frontend",
script: "npm",
cwd: "./frontend",
args: "run start:minimal",
env: {
NODE_ENV: "production",
NEXT_TELEMETRY_DISABLED: "1"
},
log_file: "./logs/pm2-frontend.log",
out_file: "./logs/pm2-frontend-out.log",
error_file: "./logs/pm2-frontend-error.log",
log_date_format: "YYYY-MM-DD HH:mm:ss Z",
merge_logs: true,
max_memory_restart: "300M"
}
]
}
Configuration Next.js
Le fichier next.config.ts configure l'application frontend :
/** @type {import('next').NextConfig} */
const nextConfig = {
// Configuration de base
reactStrictMode: true,
// Configuration des images
images: {
domains: ['localhost'],
unoptimized: process.env.NODE_ENV === 'development'
},
// Headers de sécurité
async headers() {
return [
{
source: '/(.*)',
headers: [
{
key: 'X-Frame-Options',
value: 'DENY'
},
{
key: 'X-Content-Type-Options',
value: 'nosniff'
}
]
}
];
},
// Variables d'environnement exposées au frontend
env: {
NEXT_PUBLIC_APP_NAME: process.env.APP_NAME,
NEXT_PUBLIC_APP_DOMAIN: process.env.APP_DOMAIN
}
};
module.exports = nextConfig;
Configuration ESLint
Le fichier eslint.config.mjs définit les règles de linting :
import { defineConfig } from 'eslint-define-config';
export default defineConfig({
root: true,
env: {
browser: true,
es2021: true,
node: true
},
extends: [
'eslint:recommended',
'@typescript-eslint/recommended',
'plugin:react/recommended',
'plugin:react-hooks/recommended'
],
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaFeatures: {
jsx: true
},
ecmaVersion: 12,
sourceType: 'module'
},
plugins: ['react', '@typescript-eslint'],
rules: {
// Règles personnalisées
'react/react-in-jsx-scope': 'off',
'@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'warn'
},
settings: {
react: {
version: 'detect'
}
}
});
Configuration de la base de données
Prisma Schema
Le fichier schema.prisma définit le modèle de données :
generator client {
provider = "prisma-client-js"
output = "../src/db/generated/client"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
// Modèles de données...
Configuration de connexion
La connexion Prisma est configurée dans src/db/prisma.ts :
import { PrismaClient } from '@/db/generated/client';
// Singleton pattern pour éviter les connexions multiples
declare global {
var prisma: PrismaClient | undefined;
}
export const prisma = global.prisma || new PrismaClient({
log: process.env.NODE_ENV === 'development' ? ['query', 'error', 'warn'] : ['error'],
});
if (process.env.NODE_ENV !== 'production') {
global.prisma = prisma;
}
Configuration Redis
Connexion Redis
Le fichier src/config/redis.ts configure la connexion Redis :
import Redis from 'ioredis';
import { logger } from '../utils/logger';
const redisUrl = process.env.REDIS_URL;
if (!redisUrl) {
logger.error('REDIS_URL is not defined in environment variables.');
throw new Error('REDIS_URL is not defined.');
}
const redisClient = new Redis(redisUrl, {
maxRetriesPerRequest: null,
enableReadyCheck: false,
retryDelayOnFailover: 100,
maxRetriesPerRequest: 3,
lazyConnect: true
});
redisClient.on('connect', () => {
logger.info('Successfully connected to Redis.');
});
redisClient.on('error', (err) => {
logger.error('Redis connection error:', err);
});
export { redisClient };
Clés Redis utilisées
MathQuest utilise plusieurs patterns de clés Redis :
# États de partie
mathquest:game:{accessCode}
# Timers de questions
mathquest:timer:{accessCode}:{questionUid}
# Sessions utilisateur
mathquest:session:{userId}
# Cache des questions
mathquest:question:{questionUid}
# Leaderboards
mathquest:leaderboard:{gameId}
Configuration CORS
Middleware CORS
Le serveur configure CORS dans src/server.ts :
app.use(cors({
origin: process.env.NODE_ENV === 'production'
? process.env.FRONTEND_URL || 'https://mathquest.example.com'
: ['http://localhost:3000', 'http://localhost:3001', 'http://localhost:3008'],
methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
credentials: true,
allowedHeaders: ['Content-Type', 'Authorization', 'X-Requested-With']
}));
Configuration des logs
Logger personnalisé
Le système de logging utilise winston avec configuration dans src/utils/logger.ts :
import winston from 'winston';
const logLevel = process.env.LOG_LEVEL || 'info';
const logger = winston.createLogger({
level: logLevel,
format: winston.format.combine(
winston.format.timestamp(),
winston.format.errors({ stack: true }),
winston.format.json()
),
defaultMeta: { service: 'mathquest' },
transports: [
new winston.transports.Console({
format: winston.format.combine(
winston.format.colorize(),
winston.format.simple()
)
}),
new winston.transports.File({
filename: 'logs/error.log',
level: 'error'
}),
new winston.transports.File({
filename: 'logs/combined.log'
})
]
});
export default logger;
Configuration des tests
Jest Configuration
Le fichier jest.config.js configure les tests :
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
roots: ['<rootDir>/src', '<rootDir>/tests'],
testMatch: [
'**/__tests__/**/*.+(ts|tsx|js)',
'**/?(*.)+(spec|test).+(ts|tsx|js)'
],
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
collectCoverage: false,
clearMocks: true,
globalSetup: '<rootDir>/tests/support/globalSetup.ts',
globalTeardown: '<rootDir>/tests/support/globalTeardown.ts',
setupFiles: ['<rootDir>/tests/setupTestEnv.js'],
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/src/$1',
'^@shared/(.*)$': '<rootDir>/../shared/$1'
},
maxConcurrency: 1,
maxWorkers: 1,
forceExit: true,
testTimeout: 10000
};
Variables d'environnement par environnement
Développement
# .env
NODE_ENV=development
DATABASE_URL="postgresql://postgres:password@localhost:5432/mathquest_dev"
REDIS_URL="redis://localhost:6379"
JWT_SECRET="dev_secret_key_not_for_production"
LOG_LEVEL=DEBUG
PORT=3007
FRONTEND_URL="http://localhost:3000"
Production
# .env
NODE_ENV=production
DATABASE_URL="postgresql://user:password@prod-db-host:5432/mathquest_prod"
REDIS_URL="redis://prod-redis-host:6379"
JWT_SECRET="production_secret_key_very_secure"
LOG_LEVEL=WARN
PORT=3007
FRONTEND_URL="https://mathquest.example.com"
APP_DOMAIN="mathquest.example.com"
BREVO_API_KEY="prod_brevo_key"
Test
# .env.test
NODE_ENV=test
DATABASE_URL="postgresql://postgres:password@localhost:5432/mathquest_test"
REDIS_URL="redis://localhost:6379"
JWT_SECRET="test_secret_key"
LOG_LEVEL=ERROR
PORT=3008
Scripts de déploiement
Scripts disponibles
# Construction complète
./build-all.sh
# Construction pour VPS
./build-vps.sh
# Démarrage avec PM2
npm run pm2:start
# Redémarrage des services
npm run pm2:restart
# Arrêt des services
npm run pm2:stop
# Logs PM2
npm run pm2:logs
Script de construction complet
Le fichier build-all.sh :
#!/bin/bash
# Construction du backend
echo "Building backend..."
cd backend
npm install
npm run build
cd ..
# Construction du frontend
echo "Building frontend..."
cd frontend
npm install
npm run build
cd ..
# Construction de la documentation
echo "Building docs..."
cd vuepress
npm install
npm run build
cd ..
echo "Build completed!"
Monitoring et métriques
Endpoints de santé
# Santé générale
GET /health
# Métriques mémoire
GET /health/memory
# Métriques détaillées (authentification requise)
GET /api/v1/health/metrics
Métriques collectées
- Utilisation mémoire (heap, RSS, external)
- Uptime du processus
- Nombre de connexions Socket.IO actives
- Latence base de données
- Taux d'erreur des requêtes
- Métriques Redis (connexions, commandes/seconde)
Sécurité
Headers de sécurité
Configuration des headers dans Next.js :
async headers() {
return [
{
source: '/(.*)',
headers: [
{ key: 'X-Frame-Options', value: 'DENY' },
{ key: 'X-Content-Type-Options', value: 'nosniff' },
{ key: 'Referrer-Policy', value: 'strict-origin-when-cross-origin' },
{ key: 'Permissions-Policy', value: 'camera=(), microphone=()' }
]
}
];
}
Validation des entrées
Toutes les entrées utilisateur sont validées avec Zod :
import { z } from 'zod';
export const UserInputSchema = z.object({
email: z.string().email('Email invalide'),
password: z.string().min(8, 'Mot de passe trop court'),
username: z.string().min(3, 'Nom d\'utilisateur trop court')
});
Migration et mise à jour
Variables d'environnement critiques
Lors des mises à jour, vérifier ces variables :
DATABASE_URL- Peut changer avec la migration DBREDIS_URL- Peut changer avec le scalingJWT_SECRET- Ne jamais changer en productionBREVO_API_KEY- Peut nécessiter rotation
Commandes de migration
# Migration base de données
cd backend
npx prisma migrate deploy
# Génération du client Prisma
npx prisma generate
# Redémarrage des services
npm run pm2:restart