--- /dev/null
+// =========================================================
+// 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 };
+
--- /dev/null
+// =========================================================
+// 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.
+`);
+});