Files
Parking/server/server.js
2026-06-03 14:47:35 +02:00

172 lines
5.7 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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();