|
@@ -21,16 +21,16 @@ const clients = new Set();
|
|
|
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);
|
|
@@ -47,17 +47,50 @@ function broadcastMessage(type, data) {
|
|
|
});
|
|
|
}
|
|
|
|
|
|
+const getAllowedOrigins = () => {
|
|
|
+ // Récupérer les origines depuis les variables d'environnement ou utiliser des valeurs par défaut
|
|
|
+ const corsOrigins = process.env.CORS_ORIGINS || 'http://localhost:3000,http://localhost:3001,http://localhost';
|
|
|
+
|
|
|
+ // Convertir en tableau et ajouter undefined pour les requêtes sans origine
|
|
|
+ const origins = corsOrigins.split(',').map(origin => origin.trim());
|
|
|
+ origins.push(undefined); // Pour les requêtes sans origine (comme curl ou Postman)
|
|
|
+
|
|
|
+ console.log('Origines CORS autorisées:', origins.filter(o => o !== undefined));
|
|
|
+ return origins;
|
|
|
+};
|
|
|
+
|
|
|
+const corsOptions = {
|
|
|
+ origin: function (origin, callback) {
|
|
|
+ const allowedOrigins = getAllowedOrigins();
|
|
|
+
|
|
|
+ if (!origin || allowedOrigins.indexOf(origin) !== -1) {
|
|
|
+ callback(null, true);
|
|
|
+ } else {
|
|
|
+ console.log(`Origine bloquée par CORS: ${origin}`);
|
|
|
+ callback(null, false);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ credentials: true,
|
|
|
+ methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
|
|
|
+ allowedHeaders: ['Content-Type', 'Authorization', 'X-Requested-With']
|
|
|
+};
|
|
|
+
|
|
|
+
|
|
|
+// Log CORS options
|
|
|
+app.use((req, res, next) => {
|
|
|
+ console.log(`${new Date().toISOString()} - ${req.method} ${req.url} - Origin: ${req.headers.origin || 'Aucune'}`);
|
|
|
+ next();
|
|
|
+});
|
|
|
+
|
|
|
// Middleware
|
|
|
app.use(express.json());
|
|
|
-app.use(cors({
|
|
|
- origin: ['http://localhost:3001', 'http://localhost:3000', 'http://localhost'],
|
|
|
- credentials: true
|
|
|
-}));
|
|
|
+app.use(cors(corsOptions));
|
|
|
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}`);
|
|
|
+console.log(`Origines CORS autorisées: ${getAllowedOrigins().filter(o => o !== undefined).join(', ')}`);
|
|
|
|
|
|
const db = new sqlite3.Database(DB_PATH, (err) => {
|
|
|
if (err) {
|
|
@@ -122,26 +155,26 @@ app.post('/api/register', async (req, res) => {
|
|
|
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) {
|
|
|
+ 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({
|
|
|
+
|
|
|
+ res.status(201).json({
|
|
|
message: 'Utilisateur créé avec succès',
|
|
|
userId: this.lastID,
|
|
|
- username
|
|
|
+ username
|
|
|
});
|
|
|
}
|
|
|
);
|
|
@@ -163,18 +196,18 @@ app.post('/api/login', (req, res) => {
|
|
|
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
|
|
@@ -194,11 +227,11 @@ app.post('/api/scores', (req, res) => {
|
|
|
db.run(
|
|
|
'INSERT INTO scores (user_id, date, game_type, game_number, score, comment) VALUES (?, ?, ?, ?, ?, ?)',
|
|
|
[userId, date, gameType, gameNumber, score, comment || null],
|
|
|
- function(err) {
|
|
|
+ function (err) {
|
|
|
if (err) {
|
|
|
return res.status(500).json({ error: err.message });
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
const newScore = {
|
|
|
id: this.lastID,
|
|
|
userId,
|
|
@@ -208,17 +241,17 @@ app.post('/api/scores', (req, res) => {
|
|
|
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);
|
|
|
}
|
|
|
);
|
|
@@ -232,7 +265,7 @@ app.get('/api/scores/user/:userId', (req, res) => {
|
|
|
if (err) {
|
|
|
return res.status(500).json({ error: err.message });
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
res.json(scores);
|
|
|
});
|
|
|
});
|
|
@@ -240,22 +273,22 @@ app.get('/api/scores/user/:userId', (req, res) => {
|
|
|
// 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);
|
|
|
});
|
|
|
});
|
|
@@ -269,18 +302,18 @@ app.delete('/api/scores/:id', (req, res) => {
|
|
|
return res.status(400).json({ error: 'Utilisateur non spécifié' });
|
|
|
}
|
|
|
|
|
|
- db.run('DELETE FROM scores WHERE id = ? AND user_id = ?', [scoreId, userId], function(err) {
|
|
|
+ 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' });
|
|
|
});
|
|
|
});
|
|
@@ -288,7 +321,7 @@ app.delete('/api/scores/:id', (req, res) => {
|
|
|
// 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,
|
|
@@ -299,12 +332,12 @@ app.get('/api/stats/:userId', (req, res) => {
|
|
|
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);
|
|
|
});
|
|
|
});
|
|
@@ -312,7 +345,7 @@ app.get('/api/stats/:userId', (req, res) => {
|
|
|
// Récupérer le classement
|
|
|
app.get('/api/leaderboard', (req, res) => {
|
|
|
const gameType = req.query.gameType || 'all';
|
|
|
-
|
|
|
+
|
|
|
let query = `
|
|
|
SELECT
|
|
|
u.id,
|
|
@@ -323,21 +356,21 @@ app.get('/api/leaderboard', (req, res) => {
|
|
|
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);
|
|
|
});
|
|
|
});
|
|
@@ -346,7 +379,7 @@ app.get('/api/leaderboard', (req, res) => {
|
|
|
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'));
|