const express = require('express'); const cors = require('cors'); const bodyParser = require('body-parser'); const path = require('path'); const mqtt = require('mqtt'); require('dotenv').config(); const db = require('./db/database'); const apiRoutes = require('./routes/api'); const PORT = process.env.PORT || 3000; const app = express(); app.use(cors()); app.use(bodyParser.json()); app.use(bodyParser.urlencoded({ extended: true })); app.use(express.static(path.join(__dirname, '..'))); app.use('/api', apiRoutes); app.get('/', (_req, res) => res.sendFile(path.join(__dirname, '..', 'index.html'))); app.get('/dashboard', (_req, res) => res.sendFile(path.join(__dirname, '..', 'pages', 'dashboard.html'))); const MQTT_HOST = process.env.MQTT_HOST || 'localhost'; const MQTT_PORT = process.env.MQTT_PORT || 1883; const MQTT_TOPIC = 'smartparking/sensor/#'; let mqttClient = null; function connectMQTT() { const brokerUrl = `mqtt://${MQTT_HOST}:${MQTT_PORT}`; console.log(`πŸ”Œ Connexion au broker MQTT : ${brokerUrl}`); mqttClient = mqtt.connect(brokerUrl, { clientId: 'smartparking-server-' + Math.random().toString(16).slice(3), keepalive: 60, reconnectPeriod: 5000, connectTimeout: 10000 }); mqttClient.on('connect', () => { console.log('βœ… MQTT connectΓ© au broker Mosquitto'); mqttClient.subscribe(MQTT_TOPIC, (err) => { if (err) { console.error('❌ Erreur abonnement MQTT :', err.message); } else { console.log(`πŸ“‘ AbonnΓ© au topic : ${MQTT_TOPIC}`); } }); }); mqttClient.on('message', async (topic, messageBuffer) => { const message = messageBuffer.toString().trim(); const topicParts = topic.split('/'); const spotNumber = parseInt(topicParts[2]); if (isNaN(spotNumber)) { console.warn(`⚠️ Topic MQTT invalide : ${topic}`); return; } console.log(`πŸ“© MQTT reΓ§u β†’ topic: ${topic} | valeur: ${message}`); const newStatus = message === '1' ? 'occupied' : 'free'; try { const spots = await db.getAllSpots(); const spot = spots.find(s => s.number === spotNumber); if (!spot) { console.warn(`⚠️ Place numΓ©ro ${spotNumber} introuvable en base`); return; } if (spot.status === 'reserved' && newStatus === 'occupied') { console.log(`ℹ️ Place ${spotNumber} dΓ©jΓ  rΓ©servΓ©e β€” capteur ignorΓ©`); await db.recordMqttEvent(topic, message); return; } await db.updateSpotStatus(spot.id, newStatus); await db.recordMqttEvent(topic, message); console.log(`βœ… Place ${spotNumber} mise Γ  jour β†’ ${newStatus}`); } catch (err) { console.error('❌ Erreur traitement message MQTT :', err.message); } }); mqttClient.on('error', (err) => { console.error('❌ Erreur MQTT :', err.message); }); mqttClient.on('reconnect', () => { console.log('πŸ”„ Reconnexion MQTT en cours...'); }); mqttClient.on('offline', () => { console.warn('⚠️ Client MQTT hors-ligne'); }); } async function startServer() { try { // 1. Initialiser la base de donnΓ©es await db.initDatabase(); // 2. DΓ©marrer le serveur HTTP app.listen(PORT, () => { console.log(` ╔══════════════════════════════════════════════════════╗ β•‘ πŸ…ΏοΈ SMART PARKING SERVER v2.0 - PRÊT β•‘ ╠══════════════════════════════════════════════════════╣ β•‘ 🌐 Port HTTP : ${PORT} β•‘ πŸ—„οΈ Base : MariaDB (${process.env.DB_HOST || 'localhost'}) β•‘ πŸ“‘ MQTT : ${MQTT_HOST}:${MQTT_PORT} β•‘ πŸ” JWT sΓ©curisΓ© β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β• `); }); connectMQTT(); setInterval(async () => { try { const spots = await db.getAllSpots(); const total = spots.length; const free = spots.filter(s => s.status === 'free').length; const occupied = spots.filter(s => s.status === 'occupied').length; const reserved = spots.filter(s => s.status === 'reserved').length; await db.recordStats(total, free, occupied, reserved); } catch (err) { console.error('❌ Erreur stats :', err.message); } }, 5 * 60 * 1000); setInterval(async () => { try { const count = await db.expireReservations(); if (count > 0) { console.log(`⏰ ${count} rΓ©servation(s) expirΓ©e(s) β€” places libΓ©rΓ©es`); } } catch (err) { console.error('❌ Erreur expiration rΓ©servations :', err.message); } }, 60 * 1000); } catch (err) { console.error('❌ Erreur au dΓ©marrage :', err); process.exit(1); } } process.on('SIGINT', async () => { console.log('\nπŸ›‘ ArrΓͺt du serveur...'); if (mqttClient) mqttClient.end(); await db.closeDatabase(); process.exit(0); }); startServer();