]> git.digitality.be Git - pdw25-26/commitdiff
Ajout serveur principal avec Socket.IO + page de test
authorSteph Ponzo <ponzo.stephane2@gmail.com>
Mon, 23 Feb 2026 18:00:01 +0000 (19:00 +0100)
committerSteph Ponzo <ponzo.stephane2@gmail.com>
Mon, 23 Feb 2026 18:00:01 +0000 (19:00 +0100)
server/app.js [new file with mode: 0644]
server/package-lock.json
server/package.json
server/test-server-socket.js [new file with mode: 0644]

diff --git a/server/app.js b/server/app.js
new file mode 100644 (file)
index 0000000..91b3589
--- /dev/null
@@ -0,0 +1,399 @@
+// =========================================================
+// WALL-E-TTE - SERVEUR PRINCIPAL
+// =========================================================
+// Point d'entrée de l'application
+// Intègre tous les modules : Alerts, (Stratégie, Prix, etc.)
+// =========================================================
+// USAGE : node app.js
+// =========================================================
+
+import cors from 'cors';
+import dotenv from 'dotenv';
+import express from 'express';
+import { createServer } from 'http';
+import path from 'path';
+import { fileURLToPath } from 'url';
+
+// Charger les variables d'environnement
+const __filename = fileURLToPath(import.meta.url);
+const __dirname = path.dirname(__filename);
+dotenv.config({ path: path.resolve(__dirname, '.env') });
+
+// =========================================================
+// IMPORTS DES MODULES
+// =========================================================
+
+// Module Alerts (Stéphane)
+import db from './config/db.js';
+import { createMySQLAdapter } from './modules/alerts/adapters/mysql.adapter.js';
+import { createAlertsController } from './modules/alerts/alerts.controller.js';
+import { createAlertsRepo } from './modules/alerts/alerts.repo.js';
+import { createAlertsRouter } from './modules/alerts/alerts.router.js';
+import { createAlertsService } from './modules/alerts/alerts.service.js';
+import { broadcastAlert, getConnectedUsersCount, initSocketIO } from './modules/alerts/socketManager.js';
+
+// ─────────────────────────────────────────────────────────
+// TODO: Ajouter les autres modules ici
+// ─────────────────────────────────────────────────────────
+// Module Stratégie (Sacha)
+// import { initializeStrategyModule } from './modules/strategy/index.js';
+
+// Module Prix (Valentin)
+// import { initializePriceModule } from './modules/price/index.js';
+
+// Module Auth (si existant)
+// import { initializeAuthModule } from './modules/auth/index.js';
+
+// =========================================================
+// CRÉER L'APPLICATION EXPRESS
+// =========================================================
+const app = express();
+
+// Middlewares
+app.use(cors());                    // Autoriser les requêtes cross-origin
+app.use(express.json());            // Parser le JSON
+app.use(express.urlencoded({ extended: true }));
+
+// =========================================================
+// CRÉER LE SERVEUR HTTP (nécessaire pour Socket.IO)
+// =========================================================
+const httpServer = createServer(app);
+
+// =========================================================
+// INITIALISER SOCKET.IO
+// =========================================================
+const io = initSocketIO(httpServer, {
+    cors: {
+        origin: "*",  // En prod, restreindre aux domaines autorisés
+        methods: ["GET", "POST"]
+    }
+});
+
+console.log('✅ Socket.IO initialisé');
+
+// =========================================================
+// INITIALISER LE MODULE ALERTS (Stéphane)
+// =========================================================
+const alertsAdapter = createMySQLAdapter(db);
+const alertsRepo = createAlertsRepo(alertsAdapter);
+const alertsService = createAlertsService(alertsRepo);
+const alertsController = createAlertsController(alertsService, alertsRepo);
+const alertsRouter = createAlertsRouter(alertsController);
+
+console.log('✅ Module Alerts initialisé');
+
+// =========================================================
+// MONTER LES ROUTES API
+// =========================================================
+
+// Routes Alerts
+app.use('/api/alerts', alertsRouter);
+
+// ─────────────────────────────────────────────────────────
+// TODO: Monter les autres routes ici
+// ─────────────────────────────────────────────────────────
+// app.use('/api/strategy', strategyRouter);
+// app.use('/api/price', priceRouter);
+// app.use('/api/auth', authRouter);
+
+// =========================================================
+// ROUTE HEALTH CHECK
+// =========================================================
+app.get('/', (req, res) => {
+    res.json({
+        name: 'Wall-e-tte API',
+        status: 'running',
+        version: '1.0.0',
+        socketConnections: getConnectedUsersCount(),
+        timestamp: new Date().toISOString(),
+        endpoints: {
+            alerts: '/api/alerts',
+            // strategy: '/api/strategy',
+            // price: '/api/price',
+        }
+    });
+});
+
+// =========================================================
+// PAGE DE TEST SOCKET.IO
+// =========================================================
+app.get('/test', (req, res) => {
+    res.send(`<!DOCTYPE html>
+<html lang="fr">
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>Wall-e-tte - Test Socket.IO</title>
+    <script src="/socket.io/socket.io.js"></script>
+    <style>
+        * { box-sizing: border-box; }
+        body {
+            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
+            background: #1a1a2e;
+            color: #eee;
+            padding: 20px;
+            max-width: 800px;
+            margin: 0 auto;
+        }
+        h1 { color: #00d4ff; }
+        .status {
+            padding: 10px 20px;
+            border-radius: 8px;
+            margin: 10px 0;
+            font-weight: bold;
+        }
+        .connected { background: #00c853; color: #000; }
+        .disconnected { background: #ff5252; }
+        .authenticated { background: #00bcd4; color: #000; }
+
+        #alerts {
+            background: #16213e;
+            border-radius: 8px;
+            padding: 15px;
+            max-height: 400px;
+            overflow-y: auto;
+        }
+        .alert-item {
+            background: #0f3460;
+            border-left: 4px solid #00d4ff;
+            padding: 10px 15px;
+            margin: 10px 0;
+            border-radius: 0 8px 8px 0;
+        }
+        .alert-item.WARNING { border-left-color: #ffc107; }
+        .alert-item.CRITICAL { border-left-color: #ff5252; }
+        .alert-item.INFO { border-left-color: #00d4ff; }
+
+        .alert-action {
+            font-size: 1.5em;
+            font-weight: bold;
+        }
+        .alert-action.BUY { color: #00c853; }
+        .alert-action.SELL { color: #ff5252; }
+        .alert-action.HOLD { color: #ffc107; }
+
+        button {
+            background: #00d4ff;
+            color: #000;
+            border: none;
+            padding: 10px 20px;
+            border-radius: 8px;
+            cursor: pointer;
+            font-size: 1em;
+            margin: 5px;
+        }
+        button:hover { background: #00a0c0; }
+
+        input {
+            background: #16213e;
+            border: 1px solid #00d4ff;
+            color: #fff;
+            padding: 10px;
+            border-radius: 8px;
+            margin: 5px;
+        }
+
+        #log {
+            background: #000;
+            color: #0f0;
+            font-family: monospace;
+            padding: 15px;
+            border-radius: 8px;
+            max-height: 200px;
+            overflow-y: auto;
+            font-size: 12px;
+        }
+    </style>
+</head>
+<body>
+    <h1> Wall-e-tte - Test Socket.IO</h1>
+
+    <div id="connection-status" class="status disconnected">
+         Déconnecté
+    </div>
+
+    <div id="auth-status" class="status" style="display: none;">
+    </div>
+
+    <div style="margin: 20px 0;">
+        <input type="text" id="userId" placeholder="User ID" value="test-user">
+        <button onclick="authenticate()">S'authentifier</button>
+        <button onclick="sendTestAlert()">Envoyer alerte test</button>
+    </div>
+
+    <h2> Alertes reçues</h2>
+    <div id="alerts">
+        <em>En attente d'alertes...</em>
+    </div>
+
+    <h2> Log</h2>
+    <div id="log"></div>
+
+    <script>
+        const statusEl = document.getElementById('connection-status');
+        const authStatusEl = document.getElementById('auth-status');
+        const alertsEl = document.getElementById('alerts');
+        const logEl = document.getElementById('log');
+
+        let alertCount = 0;
+
+        function log(message) {
+            const time = new Date().toLocaleTimeString();
+            logEl.innerHTML = '[' + time + '] ' + message + '<br>' + logEl.innerHTML;
+            console.log(message);
+        }
+
+        // Connexion Socket.IO
+        const socket = io();
+
+        socket.on('connect', () => {
+            log(' Connecté au serveur Socket.IO');
+            statusEl.className = 'status connected';
+            statusEl.textContent = ' Connecté (ID: ' + socket.id + ')';
+        });
+
+        socket.on('disconnect', (reason) => {
+            log(' Déconnecté: ' + reason);
+            statusEl.className = 'status disconnected';
+            statusEl.textContent = '❌ Déconnecté';
+            authStatusEl.style.display = 'none';
+        });
+
+        socket.on('auth_success', (data) => {
+            log(' Authentifié: ' + data.message);
+            authStatusEl.style.display = 'block';
+            authStatusEl.className = 'status authenticated';
+            authStatusEl.textContent = ' Authentifié: ' + data.userId;
+        });
+
+        socket.on('alert', (alert) => {
+            alertCount++;
+            log(' Alerte reçue: ' + alert.action + ' ' + alert.pair);
+
+            if (alertCount === 1) {
+                alertsEl.innerHTML = '';
+            }
+
+            const alertHtml =
+                '<div class="alert-item ' + alert.alertLevel + '">' +
+                    '<div class="alert-action ' + alert.action + '">' + alert.action + '</div>' +
+                    '<div><strong>' + alert.pair + '</strong></div>' +
+                    '<div>Confiance: ' + Math.round(alert.confidence * 100) + '%</div>' +
+                    '<div>Niveau: ' + alert.alertLevel + '</div>' +
+                    '<div>' + alert.reason + '</div>' +
+                    '<div><small>' + new Date(alert.timestamp).toLocaleString() + '</small></div>' +
+                '</div>';
+
+            alertsEl.innerHTML = alertHtml + alertsEl.innerHTML;
+        });
+
+        function authenticate() {
+            const userId = document.getElementById('userId').value;
+            if (userId) {
+                socket.emit('auth', userId);
+                log(' Envoi auth pour: ' + userId);
+            }
+        }
+
+        async function sendTestAlert() {
+            try {
+                const response = await fetch('/test/broadcast-alert', {
+                    method: 'POST',
+                    headers: { 'Content-Type': 'application/json' },
+                    body: JSON.stringify({})
+                });
+                const result = await response.json();
+                log(' Alerte broadcast: ' + result.message);
+            } catch (error) {
+                log(' Erreur: ' + error.message);
+            }
+        }
+
+        // Auto-auth au chargement
+        socket.on('connect', () => {
+            setTimeout(authenticate, 500);
+        });
+    </script>
+</body>
+</html>`);
+});
+
+// =========================================================
+// ROUTE DE TEST : SIMULER UNE ALERTE
+// =========================================================
+app.post('/test/broadcast-alert', (req, res) => {
+    const testAlert = req.body.alert || {
+        action: 'BUY',
+        pair: 'BTC/EUR',
+        confidence: 0.87,
+        reason: 'Signal de test',
+        alertLevel: 'WARNING',
+        price: 42150.23
+    };
+
+    const count = broadcastAlert(testAlert);
+
+    res.json({
+        success: true,
+        message: `Alerte envoyée à ${count} utilisateur(s) connecté(s)`,
+        alert: testAlert
+    });
+});
+
+// =========================================================
+// GESTION DES ERREURS 404
+// =========================================================
+app.use((req, res) => {
+    res.status(404).json({
+        error: 'Route non trouvée',
+        path: req.path
+    });
+});
+
+// =========================================================
+// GESTION DES ERREURS GLOBALES
+// =========================================================
+app.use((err, req, res, next) => {
+    console.error(' Erreur serveur:', err);
+    res.status(500).json({
+        error: 'Erreur serveur',
+        message: process.env.NODE_ENV === 'development' ? err.message : undefined
+    });
+});
+
+// =========================================================
+// DÉMARRER LE SERVEUR
+// =========================================================
+const PORT = process.env.PORT || 3000;
+
+httpServer.listen(PORT, () => {
+    console.log(`
+╔═══════════════════════════════════════════════════════════╗
+║               WALL-E-TTE SERVER                         ║
+╠═══════════════════════════════════════════════════════════╣
+║                                                           ║
+║  HTTP    : http://localhost:${PORT}                         ║
+║  Socket  : ws://localhost:${PORT}                           ║
+║                                                           ║
+╠═══════════════════════════════════════════════════════════╣
+║  MODULES ACTIFS :                                         ║
+║   Alerts (Stéphane)    → /api/alerts                    ║
+║   Strategy (Sacha)     → À intégrer                     ║
+║   Price (Valentin)     → À intégrer                     ║
+║                                                           ║
+╠═══════════════════════════════════════════════════════════╣
+║  SOCKET.IO EVENTS :                                       ║
+║  → Client envoie 'auth' avec userId                       ║
+║  → Client reçoit 'alert' quand signal détecté             ║
+║                                                           ║
+╚═══════════════════════════════════════════════════════════╝
+`);
+});
+
+// =========================================================
+// EXPORT POUR LES AUTRES MODULES
+// =========================================================
+// Les autres modules peuvent importer le service alerts
+// pour envoyer des notifications
+export { alertsRepo, alertsService, io };
+
index ba0a0dfcc12be750c61e607d13604195f48b836b..4ca2e4c5d4e0475042f07c37cf905217e0cecd89 100644 (file)
@@ -1,22 +1,22 @@
 {
-  "name": "wall-e-tte-alerts",
+  "name": "wall-e-tte-server",
   "version": "1.0.0",
   "lockfileVersion": 3,
   "requires": true,
   "packages": {
     "": {
-      "name": "wall-e-tte-alerts",
+      "name": "wall-e-tte-server",
       "version": "1.0.0",
       "license": "ISC",
       "dependencies": {
+        "cors": "^2.8.5",
         "dotenv": "^16.4.5",
         "express": "^4.18.2",
         "mysql2": "^3.6.5",
-        "nodemailer": "^6.9.7",
+        "nodemailer": "^8.0.1",
         "socket.io": "^4.8.3",
         "uuid": "^9.0.1"
-      },
-      "devDependencies": {}
+      }
     },
     "node_modules/@socket.io/component-emitter": {
       "version": "3.1.2",
       }
     },
     "node_modules/nodemailer": {
-      "version": "6.10.1",
-      "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.10.1.tgz",
-      "integrity": "sha512-Z+iLaBGVaSjbIzQ4pX6XV41HrooLsQ10ZWPUehGmuantvzWoDVBnmsdUcOIDM1t+yPor5pDhVlDESgOMEGxhHA==",
+      "version": "8.0.1",
+      "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-8.0.1.tgz",
+      "integrity": "sha512-5kcldIXmaEjZcHR6F28IKGSgpmZHaF1IXLWFTG+Xh3S+Cce4MiakLtWY+PlBU69fLbRa8HlaGIrC/QolUpHkhg==",
       "license": "MIT-0",
       "engines": {
         "node": ">=6.0.0"
index 59655e5513b64f407596c460d0444bea28276101..1c28eb2e37bb449251161fc6b8218749a157e28e 100644 (file)
@@ -1,25 +1,29 @@
 {
-  "name": "wall-e-tte-alerts",
+  "name": "wall-e-tte-server",
   "version": "1.0.0",
-  "description": "Module d'alertes et notifications pour Wall-e-tte",
+  "description": "Serveur Wall-e-tte - Trading Crypto Advisor",
   "type": "module",
-  "main": "modules/alerts/alerts.service.js",
+  "main": "app.js",
   "scripts": {
-    "test": "node test-alerts.js"
+    "start": "node app.js",
+    "dev": "node --watch app.js",
+    "test": "node test-module-complet.js",
+    "test:server": "node test-server-socket.js"
   },
   "keywords": [
     "crypto",
+    "trading",
     "alerts",
-    "notifications",
     "wall-e-tte"
   ],
-  "author": "Stéphane",
+  "author": "Équipe Wall-e-tte",
   "license": "ISC",
   "dependencies": {
+    "cors": "^2.8.5",
     "dotenv": "^16.4.5",
     "express": "^4.18.2",
     "mysql2": "^3.6.5",
-    "nodemailer": "^6.9.7",
+    "nodemailer": "^8.0.1",
     "socket.io": "^4.8.3",
     "uuid": "^9.0.1"
   }
diff --git a/server/test-server-socket.js b/server/test-server-socket.js
new file mode 100644 (file)
index 0000000..dbac7f0
--- /dev/null
@@ -0,0 +1,169 @@
+// =========================================================
+// SERVEUR DE TEST AVEC SOCKET.IO
+// =========================================================
+// Lance ce serveur pour tester les alertes temps réel
+// avec Thibault (mobile) ou Océane (web)
+// =========================================================
+// USAGE : node test-server-socket.js
+// =========================================================
+
+import express from 'express';
+import { createServer } from 'http';
+import dotenv from 'dotenv';
+import path from 'path';
+import { fileURLToPath } from 'url';
+
+// Charger les variables d'environnement
+const __filename = fileURLToPath(import.meta.url);
+const __dirname = path.dirname(__filename);
+dotenv.config({ path: path.resolve(__dirname, '.env') });
+
+// =========================================================
+// IMPORTS DU MODULE ALERTS
+// =========================================================
+import db from './config/db.js';
+import { createMySQLAdapter } from './modules/alerts/adapters/mysql.adapter.js';
+import { createAlertsRepo } from './modules/alerts/alerts.repo.js';
+import { createAlertsService } from './modules/alerts/alerts.service.js';
+import { createAlertsController } from './modules/alerts/alerts.controller.js';
+import { createAlertsRouter } from './modules/alerts/alerts.router.js';
+import { initSocketIO, sendAlertToUser, broadcastAlert, getConnectedUsersCount } from './modules/alerts/socketManager.js';
+
+// =========================================================
+// CRÉER L'APPLICATION EXPRESS
+// =========================================================
+const app = express();
+app.use(express.json());
+
+// =========================================================
+// CRÉER LE SERVEUR HTTP (nécessaire pour Socket.IO)
+// =========================================================
+const httpServer = createServer(app);
+
+// =========================================================
+// INITIALISER SOCKET.IO
+// =========================================================
+const io = initSocketIO(httpServer, {
+    cors: {
+        origin: "*",  // Accepter toutes les origines pour le test
+        methods: ["GET", "POST"]
+    }
+});
+
+// =========================================================
+// INITIALISER LE MODULE ALERTS
+// =========================================================
+const adapter = createMySQLAdapter(db);
+const repo = createAlertsRepo(adapter);
+const service = createAlertsService(repo);
+const controller = createAlertsController(service, repo);
+const router = createAlertsRouter(controller);
+
+// =========================================================
+// MONTER LES ROUTES
+// =========================================================
+app.use('/api/alerts', router);
+
+// =========================================================
+// ROUTE DE TEST / HEALTH CHECK
+// =========================================================
+app.get('/', (req, res) => {
+    res.json({
+        status: 'ok',
+        message: 'Serveur Wall-e-tte avec Socket.IO',
+        socketConnections: getConnectedUsersCount(),
+        timestamp: new Date().toISOString()
+    });
+});
+
+// =========================================================
+// ROUTE POUR SIMULER UNE ALERTE (test)
+// =========================================================
+app.post('/test/send-alert', (req, res) => {
+    const { userId, broadcast } = req.body;
+
+    const testAlert = {
+        action: 'BUY',
+        pair: 'BTC/EUR',
+        confidence: 0.87,
+        reason: 'Test manuel depuis le serveur',
+        alertLevel: 'WARNING',
+        price: 42150.23
+    };
+
+    if (broadcast) {
+        // Envoyer à tous
+        const count = broadcastAlert(testAlert);
+        res.json({
+            success: true,
+            message: `Alerte envoyée à ${count} utilisateur(s)`
+        });
+    } else if (userId) {
+        // Envoyer à un utilisateur spécifique
+        const sent = sendAlertToUser(userId, testAlert);
+        res.json({
+            success: sent,
+            message: sent ? 'Alerte envoyée' : 'Utilisateur non connecté'
+        });
+    } else {
+        res.status(400).json({
+            error: 'userId ou broadcast requis'
+        });
+    }
+});
+
+// =========================================================
+// ROUTE POUR VOIR LES CONNEXIONS ACTIVES
+// =========================================================
+app.get('/test/connections', (req, res) => {
+    const { getConnectedUserIds } = require('./modules/alerts/socketManager.js');
+    res.json({
+        count: getConnectedUsersCount(),
+        users: getConnectedUserIds ? getConnectedUserIds() : 'N/A'
+    });
+});
+
+// =========================================================
+// DÉMARRER LE SERVEUR
+// =========================================================
+const PORT = process.env.PORT || 3000;
+
+httpServer.listen(PORT, () => {
+    console.log(`
+                ╔═══════════════════════════════════════════════════════════╗
+                ║        SERVEUR WALL-E-TTE AVEC SOCKET.IO                  ║
+                ╠═══════════════════════════════════════════════════════════╣
+                ║                                                           ║
+                ║  HTTP Server  : http://localhost:${PORT}                  ║
+                ║  Socket.IO    : ws://localhost:${PORT}                    ║
+                ║                                                           ║
+                ╠═══════════════════════════════════════════════════════════╣
+                ║  ROUTES API :                                             ║
+                ║  ───────────────────────────────────────────────────────  ║
+                ║  GET    /                     → Health check              ║
+                ║  GET    /test/connections     → Voir qui est connecté     ║
+                ║  POST   /test/send-alert      → Simuler une alerte        ║
+                ║  POST   /api/alerts/rules     → Créer une règle           ║
+                ║  GET    /api/alerts/rules/:id → Lister les règles         ║
+                ║                                                           ║
+                ╠═══════════════════════════════════════════════════════════╣
+                ║  SOCKET.IO EVENTS :                                       ║
+                ║  ───────────────────────────────────────────────────────  ║
+                ║  Client → Serveur :                                       ║
+                ║    • 'auth' (userId)         → S'authentifier             ║
+                ║    • 'ping_alerts'           → Tester la connexion        ║
+                ║                                                           ║
+                ║  Serveur → Client :                                       ║
+                ║    • 'auth_success'          → Authentification OK        ║
+                ║    • 'alert'                 → Réception d'une alerte     ║
+                ║    • 'pong_alerts'           → Réponse au ping            ║
+                ║                                                           ║
+                ╚═══════════════════════════════════════════════════════════╝
+
+
+                Pour tester manuellement :
+                curl -X POST http://localhost:${PORT}/test/send-alert -H "Content-Type: application/json" -d '{"broadcast": true}'
+
+                Ctrl+C pour arrêter.
+`);
+});