123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391 |
- // server.js - Fichier principal du serveur
- const express = require('express');
- const sqlite3 = require('sqlite3').verbose();
- const bcrypt = require('bcrypt');
- const cors = require('cors');
- const path = require('path');
- const http = require('http');
- const WebSocket = require('ws');
- const app = express();
- const server = http.createServer(app);
- const port = process.env.PORT || 3000;
- // Create a WebSocket server
- const wss = new WebSocket.Server({ server });
- // Store connected WebSocket clients
- const clients = new Set();
- // WebSocket connection handler
- wss.on('connection', (ws) => {
- console.log('Client connected');
- clients.add(ws);
-
- // Send a welcome message
- ws.send(JSON.stringify({ type: 'connection', message: 'Connected to WebSocket server' }));
-
- // Handle client disconnection
- ws.on('close', () => {
- console.log('Client disconnected');
- clients.delete(ws);
- });
-
- // Handle messages from clients (not used in this implementation, but available for future use)
- ws.on('message', (message) => {
- console.log('Received message:', message);
- });
- });
- // Helper function to broadcast messages to all connected clients
- function broadcastMessage(type, data) {
- const message = JSON.stringify({ type, data });
- clients.forEach(client => {
- if (client.readyState === WebSocket.OPEN) {
- client.send(message);
- }
- });
- }
- // Middleware
- app.use(express.json());
- app.use(cors({
- origin: ['http://localhost:3001', 'http://localhost:3000', 'http://localhost'],
- credentials: true
- }));
- app.use(express.static(path.join(__dirname, 'public')));
- // Connexion à la base de données SQLite
- const DB_PATH = process.env.DB_PATH || '/app/data/framed.db';
- console.log(`Tentative de connexion à la BD: ${DB_PATH}`);
- const db = new sqlite3.Database(DB_PATH, (err) => {
- if (err) {
- console.error('Erreur de connexion à la base de données:', err.message);
- } else {
- console.log('Connecté à la base de données SQLite');
- initDatabase();
- }
- });
- // Initialiser la base de données
- function initDatabase() {
- // Activer les clés étrangères
- db.run('PRAGMA foreign_keys = ON');
- // Créer la table des utilisateurs
- db.run(`
- CREATE TABLE IF NOT EXISTS users (
- id INTEGER PRIMARY KEY AUTOINCREMENT,
- username TEXT UNIQUE NOT NULL,
- password TEXT NOT NULL,
- created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
- )
- `);
- // Créer la table des scores
- db.run(`
- CREATE TABLE IF NOT EXISTS scores (
- id INTEGER PRIMARY KEY AUTOINCREMENT,
- user_id INTEGER NOT NULL,
- date TEXT NOT NULL,
- game_type TEXT NOT NULL,
- game_number INTEGER,
- score INTEGER NOT NULL,
- comment TEXT,
- created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
- FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE
- )
- `);
- console.log('Tables vérifiées/créées');
- }
- // Route de test simple
- app.get('/api/test', (req, res) => {
- res.json({ message: 'Le serveur fonctionne correctement!' });
- });
- // Routes d'API
- // Inscription
- app.post('/api/register', async (req, res) => {
- const { username, password } = req.body;
- if (!username || !password) {
- return res.status(400).json({ error: 'Nom d\'utilisateur et mot de passe requis' });
- }
- try {
- // Vérifier si l'utilisateur existe déjà
- db.get('SELECT * FROM users WHERE username = ?', [username], async (err, user) => {
- if (err) {
- return res.status(500).json({ error: err.message });
- }
-
- if (user) {
- return res.status(400).json({ error: 'Cet utilisateur existe déjà' });
- }
-
- // Hasher le mot de passe
- const hashedPassword = await bcrypt.hash(password, 10);
-
- // Insérer le nouvel utilisateur
- db.run('INSERT INTO users (username, password) VALUES (?, ?)',
- [username, hashedPassword],
- function(err) {
- if (err) {
- return res.status(500).json({ error: err.message });
- }
-
- res.status(201).json({
- message: 'Utilisateur créé avec succès',
- userId: this.lastID,
- username
- });
- }
- );
- });
- } catch (error) {
- res.status(500).json({ error: error.message });
- }
- });
- // Connexion
- app.post('/api/login', (req, res) => {
- const { username, password } = req.body;
- if (!username || !password) {
- return res.status(400).json({ error: 'Nom d\'utilisateur et mot de passe requis' });
- }
- db.get('SELECT * FROM users WHERE username = ?', [username], async (err, user) => {
- if (err) {
- return res.status(500).json({ error: err.message });
- }
-
- if (!user) {
- return res.status(401).json({ error: 'Identifiants invalides' });
- }
-
- // Vérifier le mot de passe
- const validPassword = await bcrypt.compare(password, user.password);
-
- if (!validPassword) {
- return res.status(401).json({ error: 'Identifiants invalides' });
- }
-
- res.json({
- userId: user.id,
- username: user.username
- });
- });
- });
- // Ajouter un score
- app.post('/api/scores', (req, res) => {
- const { userId, date, gameType, gameNumber, score, comment } = req.body;
- if (!userId || !date || !gameType || !score) {
- return res.status(400).json({ error: 'Données incomplètes' });
- }
- db.run(
- 'INSERT INTO scores (user_id, date, game_type, game_number, score, comment) VALUES (?, ?, ?, ?, ?, ?)',
- [userId, date, gameType, gameNumber, score, comment || null],
- function(err) {
- if (err) {
- return res.status(500).json({ error: err.message });
- }
-
- const newScore = {
- id: this.lastID,
- userId,
- date,
- gameType,
- gameNumber,
- score,
- comment
- };
-
- // Get username for the new score
- db.get('SELECT username FROM users WHERE id = ?', [userId], (err, user) => {
- if (!err && user) {
- newScore.username = user.username;
-
- // Broadcast the new score to all connected clients
- broadcastMessage('new-score', newScore);
- }
- });
-
- res.status(201).json(newScore);
- }
- );
- });
- // Récupérer les scores d'un utilisateur
- app.get('/api/scores/user/:userId', (req, res) => {
- const userId = req.params.userId;
- db.all('SELECT * FROM scores WHERE user_id = ? ORDER BY date DESC', [userId], (err, scores) => {
- if (err) {
- return res.status(500).json({ error: err.message });
- }
-
- res.json(scores);
- });
- });
- // Récupérer tous les scores (pour le classement)
- app.get('/api/scores', (req, res) => {
- const gameType = req.query.gameType || 'all';
-
- let query = `
- SELECT s.*, u.username
- FROM scores s
- JOIN users u ON s.user_id = u.id
- `;
-
- if (gameType !== 'all') {
- query += ` WHERE s.game_type = '${gameType}'`;
- }
-
- db.all(query, (err, scores) => {
- if (err) {
- return res.status(500).json({ error: err.message });
- }
-
- res.json(scores);
- });
- });
- // Supprimer un score
- app.delete('/api/scores/:id', (req, res) => {
- const scoreId = req.params.id;
- const userId = req.query.userId;
- if (!userId) {
- return res.status(400).json({ error: 'Utilisateur non spécifié' });
- }
- db.run('DELETE FROM scores WHERE id = ? AND user_id = ?', [scoreId, userId], function(err) {
- if (err) {
- return res.status(500).json({ error: err.message });
- }
-
- if (this.changes === 0) {
- return res.status(404).json({ error: 'Score non trouvé ou non autorisé' });
- }
-
- // Broadcast the deleted score to all connected clients
- broadcastMessage('delete-score', { id: scoreId, userId });
-
- res.json({ message: 'Score supprimé avec succès' });
- });
- });
- // Récupérer les statistiques d'un utilisateur
- app.get('/api/stats/:userId', (req, res) => {
- const userId = req.params.userId;
-
- const query = `
- SELECT
- game_type,
- COUNT(*) as total_games,
- AVG(score) as average_score,
- MIN(score) as best_score
- FROM scores
- WHERE user_id = ?
- GROUP BY game_type
- `;
-
- db.all(query, [userId], (err, stats) => {
- if (err) {
- return res.status(500).json({ error: err.message });
- }
-
- res.json(stats);
- });
- });
- // Récupérer le classement
- app.get('/api/leaderboard', (req, res) => {
- const gameType = req.query.gameType || 'all';
-
- let query = `
- SELECT
- u.id,
- u.username,
- COUNT(*) as total_games,
- AVG(s.score) as average_score,
- MIN(s.score) as best_score
- FROM scores s
- JOIN users u ON s.user_id = u.id
- `;
-
- if (gameType !== 'all') {
- query += ` WHERE s.game_type = '${gameType}'`;
- }
-
- query += `
- GROUP BY u.id
- ORDER BY average_score ASC, best_score ASC
- `;
-
- db.all(query, (err, leaderboard) => {
- if (err) {
- return res.status(500).json({ error: err.message });
- }
-
- res.json(leaderboard);
- });
- });
- // Gestion du mode de production ou développement
- if (process.env.NODE_ENV === 'production') {
- // En production, servir les fichiers statiques
- app.use(express.static(path.join(__dirname, 'public')));
-
- // Route par défaut pour servir l'application React
- app.get('*', (req, res) => {
- res.sendFile(path.join(__dirname, 'public', 'index.html'));
- });
- } else {
- // En développement, juste répondre à l'API
- app.get('/', (req, res) => {
- res.json({ message: 'API Framed Tracker fonctionne correctement. Utilisez le port 3001 pour accéder au frontend.' });
- });
- }
- // Démarrer le serveur HTTP avec WebSocket
- server.listen(port, () => {
- console.log(`Serveur HTTP en écoute sur le port ${port}`);
- console.log(`Serveur WebSocket en écoute sur le port ${port}`);
- });
- // Gestion des erreurs
- app.use((err, req, res, next) => {
- console.error('Erreur API:', err);
- res.status(500).json({ error: 'Erreur serveur' });
- });
- // Fermer proprement la connexion à la base de données lors de l'arrêt du serveur
- process.on('SIGINT', () => {
- db.close((err) => {
- if (err) {
- console.error(err.message);
- }
- console.log('Connexion à la base de données fermée');
- process.exit(0);
- });
- });
- // Gestion des exceptions non capturées
- process.on('uncaughtException', (error) => {
- console.error('ERREUR NON CAPTURÉE:', error);
- });
- process.on('unhandledRejection', (reason, promise) => {
- console.error('PROMESSE REJETÉE NON GÉRÉE:', reason);
- });
|