262 lines
8.0 KiB
JavaScript
262 lines
8.0 KiB
JavaScript
// ========================================
|
|
// FICHIER: server.js - POINT D'ENTRÉE MODES EXCLUSIFS
|
|
// RESPONSABILITÉ: Démarrage serveur avec sélection mode MANUAL/AUTO
|
|
// MODES: MANUAL (interface client) | AUTO (batch GSheets)
|
|
// ========================================
|
|
|
|
require('dotenv').config();
|
|
|
|
const { logSh } = require('./lib/ErrorReporting');
|
|
const { ModeManager } = require('./lib/modes/ModeManager');
|
|
|
|
/**
|
|
* SERVEUR SEO GENERATOR - MODES EXCLUSIFS
|
|
* Point d'entrée unique pour démarrer en mode MANUAL ou AUTO
|
|
*/
|
|
class SEOGeneratorServer {
|
|
|
|
constructor() {
|
|
this.startTime = Date.now();
|
|
this.isShuttingDown = false;
|
|
}
|
|
|
|
/**
|
|
* Démarrage principal du serveur
|
|
*/
|
|
async start() {
|
|
try {
|
|
// Banner de démarrage
|
|
this.displayStartupBanner();
|
|
|
|
// Gestion signaux système
|
|
this.setupSignalHandlers();
|
|
|
|
// Initialisation du gestionnaire de modes
|
|
const mode = await ModeManager.initialize();
|
|
|
|
logSh(`🎯 Serveur démarré en mode ${mode.toUpperCase()}`, 'INFO');
|
|
logSh(`⏱️ Temps de démarrage: ${Date.now() - this.startTime}ms`, 'DEBUG');
|
|
|
|
// Monitoring périodique
|
|
this.startHealthMonitoring();
|
|
|
|
return mode;
|
|
|
|
} catch (error) {
|
|
logSh(`❌ ÉCHEC DÉMARRAGE SERVEUR: ${error.message}`, 'ERROR');
|
|
logSh(`Stack trace: ${error.stack}`, 'ERROR');
|
|
|
|
await this.gracefulShutdown(1);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Affiche la bannière de démarrage
|
|
*/
|
|
displayStartupBanner() {
|
|
const version = require('./package.json').version || '1.0.0';
|
|
const nodeVersion = process.version;
|
|
const platform = process.platform;
|
|
|
|
console.log(`
|
|
╔════════════════════════════════════════════════════════════╗
|
|
║ SEO GENERATOR SERVER ║
|
|
║ MODES EXCLUSIFS ║
|
|
╠════════════════════════════════════════════════════════════╣
|
|
║ Version: ${version.padEnd(10)} │ Node: ${nodeVersion.padEnd(10)} │ OS: ${platform.padEnd(8)} ║
|
|
║ ║
|
|
║ 🎯 MANUAL MODE: Interface Client + API + WebSocket ║
|
|
║ 🤖 AUTO MODE: Traitement Batch Google Sheets ║
|
|
║ ║
|
|
║ 💡 Utilisation: ║
|
|
║ npm start (défaut: MANUAL) ║
|
|
║ npm start -- --mode=manual ║
|
|
║ npm start -- --mode=auto ║
|
|
║ SERVER_MODE=auto npm start ║
|
|
╚════════════════════════════════════════════════════════════╝
|
|
`);
|
|
|
|
logSh('🚀 === SEO GENERATOR SERVER - DÉMARRAGE ===', 'INFO');
|
|
logSh(`📦 Version: ${version} | Node: ${nodeVersion}`, 'INFO');
|
|
}
|
|
|
|
/**
|
|
* Configure la gestion des signaux système
|
|
*/
|
|
setupSignalHandlers() {
|
|
// Arrêt propre sur SIGTERM/SIGINT
|
|
process.on('SIGTERM', () => this.handleShutdownSignal('SIGTERM'));
|
|
process.on('SIGINT', () => this.handleShutdownSignal('SIGINT'));
|
|
|
|
// Gestion erreurs non capturées
|
|
process.on('uncaughtException', (error) => {
|
|
logSh(`❌ ERREUR NON CAPTURÉE: ${error.message}`, 'ERROR');
|
|
logSh(`Stack: ${error.stack}`, 'ERROR');
|
|
this.gracefulShutdown(1);
|
|
});
|
|
|
|
process.on('unhandledRejection', (reason, promise) => {
|
|
logSh(`❌ PROMESSE REJETÉE: ${reason}`, 'ERROR');
|
|
logSh(`Promise: ${promise}`, 'DEBUG');
|
|
// Ne pas arrêter pour les rejections non gérées, juste logger
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Gère les signaux d'arrêt
|
|
*/
|
|
async handleShutdownSignal(signal) {
|
|
logSh(`🛑 Signal reçu: ${signal}`, 'INFO');
|
|
await this.gracefulShutdown(0);
|
|
}
|
|
|
|
/**
|
|
* Arrêt propre du serveur
|
|
*/
|
|
async gracefulShutdown(exitCode = 0) {
|
|
if (this.isShuttingDown) {
|
|
logSh('⚠️ Arrêt déjà en cours...', 'WARNING');
|
|
return;
|
|
}
|
|
|
|
this.isShuttingDown = true;
|
|
const shutdownStart = Date.now();
|
|
|
|
logSh('🛑 ARRÊT SERVEUR EN COURS...', 'INFO');
|
|
|
|
try {
|
|
// Arrêter le monitoring
|
|
if (this.healthInterval) {
|
|
clearInterval(this.healthInterval);
|
|
}
|
|
|
|
// Arrêter le mode actuel via ModeManager
|
|
await ModeManager.stopCurrentMode();
|
|
|
|
// Nettoyage final
|
|
await this.finalCleanup();
|
|
|
|
const shutdownDuration = Date.now() - shutdownStart;
|
|
const totalUptime = Date.now() - this.startTime;
|
|
|
|
logSh(`✅ Arrêt terminé (${shutdownDuration}ms)`, 'INFO');
|
|
logSh(`⏱️ Uptime total: ${Math.floor(totalUptime / 1000)}s`, 'INFO');
|
|
logSh('👋 Au revoir !', 'INFO');
|
|
|
|
} catch (error) {
|
|
logSh(`❌ Erreur durant arrêt: ${error.message}`, 'ERROR');
|
|
exitCode = 1;
|
|
}
|
|
|
|
// Attendre un peu pour que les logs se finalisent
|
|
setTimeout(() => {
|
|
process.exit(exitCode);
|
|
}, 100);
|
|
}
|
|
|
|
/**
|
|
* Nettoyage final avant arrêt
|
|
*/
|
|
async finalCleanup() {
|
|
try {
|
|
// Sauvegarder l'état final
|
|
ModeManager.saveModeState();
|
|
|
|
// Autres nettoyages si nécessaire
|
|
|
|
} catch (error) {
|
|
logSh(`⚠️ Erreur nettoyage final: ${error.message}`, 'WARNING');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Démarre le monitoring de santé
|
|
*/
|
|
startHealthMonitoring() {
|
|
const HEALTH_CHECK_INTERVAL = 30000; // 30 secondes
|
|
|
|
this.healthInterval = setInterval(() => {
|
|
try {
|
|
this.performHealthCheck();
|
|
} catch (error) {
|
|
logSh(`⚠️ Erreur health check: ${error.message}`, 'WARNING');
|
|
}
|
|
}, HEALTH_CHECK_INTERVAL);
|
|
|
|
logSh('💓 Health monitoring démarré', 'DEBUG');
|
|
}
|
|
|
|
/**
|
|
* Vérifie la santé du système
|
|
*/
|
|
performHealthCheck() {
|
|
const status = ModeManager.getStatus();
|
|
const memUsage = process.memoryUsage();
|
|
const uptime = process.uptime();
|
|
|
|
// Log périodique de l'état
|
|
logSh(`💓 Health Check - Mode: ${status.currentMode} | Uptime: ${Math.floor(uptime)}s | RAM: ${Math.round(memUsage.rss / 1024 / 1024)}MB`, 'TRACE');
|
|
|
|
// Vérifications critiques
|
|
if (memUsage.rss > 1024 * 1024 * 1024) { // > 1GB
|
|
logSh('⚠️ Utilisation mémoire élevée', 'WARNING');
|
|
}
|
|
|
|
if (!status.currentMode) {
|
|
logSh('⚠️ Aucun mode actif détecté', 'WARNING');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* API pour changer de mode (si nécessaire)
|
|
*/
|
|
async switchMode(newMode, force = false) {
|
|
logSh(`🔄 Demande changement mode vers: ${newMode}`, 'INFO');
|
|
|
|
try {
|
|
await ModeManager.switchToMode(newMode, force);
|
|
logSh(`✅ Changement mode réussi vers: ${newMode}`, 'INFO');
|
|
return true;
|
|
|
|
} catch (error) {
|
|
logSh(`❌ Échec changement mode: ${error.message}`, 'ERROR');
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Retourne le statut actuel
|
|
*/
|
|
getStatus() {
|
|
return {
|
|
server: {
|
|
startTime: this.startTime,
|
|
uptime: Date.now() - this.startTime,
|
|
isShuttingDown: this.isShuttingDown,
|
|
nodeVersion: process.version,
|
|
platform: process.platform,
|
|
pid: process.pid
|
|
},
|
|
mode: ModeManager.getStatus(),
|
|
memory: process.memoryUsage(),
|
|
timestamp: new Date().toISOString()
|
|
};
|
|
}
|
|
}
|
|
|
|
// ========================================
|
|
// DÉMARRAGE AUTOMATIQUE
|
|
// ========================================
|
|
|
|
// Démarrer le serveur si ce fichier est exécuté directement
|
|
if (require.main === module) {
|
|
const server = new SEOGeneratorServer();
|
|
|
|
server.start().catch((error) => {
|
|
console.error('💥 ERREUR CRITIQUE DÉMARRAGE:', error.message);
|
|
process.exit(1);
|
|
});
|
|
}
|
|
|
|
// ============= EXPORTS =============
|
|
module.exports = { SEOGeneratorServer }; |