sourcefinder/server.js
Alexis Trouvé a7bd6115b7
Some checks failed
SourceFinder CI/CD Pipeline / Code Quality & Linting (push) Has been cancelled
SourceFinder CI/CD Pipeline / Unit Tests (push) Has been cancelled
SourceFinder CI/CD Pipeline / Security Tests (push) Has been cancelled
SourceFinder CI/CD Pipeline / Integration Tests (push) Has been cancelled
SourceFinder CI/CD Pipeline / Performance Tests (push) Has been cancelled
SourceFinder CI/CD Pipeline / Code Coverage Report (push) Has been cancelled
SourceFinder CI/CD Pipeline / Build & Deployment Validation (16.x) (push) Has been cancelled
SourceFinder CI/CD Pipeline / Build & Deployment Validation (18.x) (push) Has been cancelled
SourceFinder CI/CD Pipeline / Build & Deployment Validation (20.x) (push) Has been cancelled
SourceFinder CI/CD Pipeline / Regression Tests (push) Has been cancelled
SourceFinder CI/CD Pipeline / Security Audit (push) Has been cancelled
SourceFinder CI/CD Pipeline / Notify Results (push) Has been cancelled
feat: Implémentation complète du système SourceFinder avec tests
- Architecture modulaire avec injection de dépendances
- Système de scoring intelligent multi-facteurs (spécificité, fraîcheur, qualité, réutilisation)
- Moteur anti-injection 4 couches (preprocessing, patterns, sémantique, pénalités)
- API REST complète avec validation et rate limiting
- Repository JSON avec index mémoire et backup automatique
- Provider LLM modulaire pour génération de contenu
- Suite de tests complète (Jest) :
  * Tests unitaires pour sécurité et scoring
  * Tests d'intégration API end-to-end
  * Tests de sécurité avec simulation d'attaques
  * Tests de performance et charge
- Pipeline CI/CD avec GitHub Actions
- Logging structuré et monitoring
- Configuration ESLint et environnement de test

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-15 23:06:10 +08:00

177 lines
5.0 KiB
JavaScript

/**
* Point d'entrée du serveur SourceFinder
* Démarrage et arrêt gracieux de l'application
*/
require('dotenv').config();
const SourceFinderApp = require('./src/app');
const logger = require('./src/utils/logger');
class Server {
constructor() {
this.app = null;
this.server = null;
this.sourceFinderApp = new SourceFinderApp();
}
/**
* Démarrer le serveur
*/
async start() {
try {
// Initialiser l'application avec toutes ses dépendances
this.app = await this.sourceFinderApp.initialize();
// Configuration du port
const port = parseInt(process.env.PORT) || 3000;
const host = process.env.HOST || '0.0.0.0';
// Démarrer le serveur HTTP
this.server = this.app.listen(port, host, () => {
logger.info('🚀 SourceFinder server started', {
server: {
port,
host,
environment: process.env.NODE_ENV || 'development',
apiVersion: process.env.API_VERSION || 'v1',
pid: process.pid
},
endpoints: {
health: `http://${host}:${port}/health`,
api: `http://${host}:${port}/api/v1`,
docs: `http://${host}:${port}/api/v1/docs` // TODO: implémenter docs
}
});
// Log configuration active
const container = this.sourceFinderApp.getContainer();
const config = container.get('config');
logger.info('📦 Active configuration', {
components: {
newsProvider: config.newsProvider.type,
stockRepository: config.stockRepository.type,
scoringEngine: config.scoringEngine.type
},
features: {
rateLimiting: true,
cors: true,
requestLogging: true,
errorHandling: true
}
});
});
// Configuration serveur
this.server.keepAliveTimeout = 65000; // Plus que le load balancer
this.server.headersTimeout = 66000; // Plus que keepAliveTimeout
// Gestion des signaux de fermeture
this.setupGracefulShutdown();
} catch (error) {
logger.error('❌ Failed to start SourceFinder server', error);
process.exit(1);
}
}
/**
* Configurer l'arrêt gracieux
*/
setupGracefulShutdown() {
// Gestion des signaux système
const signals = ['SIGTERM', 'SIGINT', 'SIGUSR2'];
signals.forEach((signal) => {
process.on(signal, () => {
logger.info(`📡 Received ${signal}, starting graceful shutdown...`);
this.shutdown();
});
});
// Gestion des exceptions non catchées
process.on('uncaughtException', (error) => {
logger.error('💥 Uncaught Exception', error);
this.shutdown(1);
});
process.on('unhandledRejection', (reason, promise) => {
logger.error('💥 Unhandled Rejection', new Error(reason), { promise });
this.shutdown(1);
});
// Gestion mémoire (warning si > 80% utilisée)
if (process.env.NODE_ENV === 'production') {
setInterval(() => {
const memUsage = process.memoryUsage();
const memUsedMB = Math.round(memUsage.heapUsed / 1024 / 1024);
const memTotalMB = Math.round(memUsage.heapTotal / 1024 / 1024);
const memPercent = (memUsage.heapUsed / memUsage.heapTotal) * 100;
if (memPercent > 80) {
logger.warn(`⚠️ High memory usage: ${memUsedMB}MB / ${memTotalMB}MB (${memPercent.toFixed(1)}%)`, {
memory: {
used: memUsedMB,
total: memTotalMB,
percent: memPercent,
rss: Math.round(memUsage.rss / 1024 / 1024)
}
});
}
}, 30000); // Check toutes les 30s
}
}
/**
* Arrêter le serveur gracieusement
*/
async shutdown(exitCode = 0) {
logger.info('🔄 Starting graceful shutdown...');
try {
// Arrêter d'accepter nouvelles connexions
if (this.server) {
await new Promise((resolve, reject) => {
this.server.close((err) => {
if (err) {
logger.error('❌ Error closing HTTP server', err);
reject(err);
} else {
logger.info('✅ HTTP server closed');
resolve();
}
});
});
}
// Fermer l'application et ses dépendances
if (this.sourceFinderApp) {
await this.sourceFinderApp.shutdown();
}
// Forcer fermeture après timeout
setTimeout(() => {
logger.warn('⚠️ Forceful shutdown after timeout');
process.exit(1);
}, 30000); // 30 secondes max
logger.info('✅ Graceful shutdown completed');
process.exit(exitCode);
} catch (error) {
logger.error('❌ Error during shutdown', error);
process.exit(1);
}
}
}
// Démarrer le serveur si ce fichier est exécuté directement
if (require.main === module) {
const server = new Server();
server.start().catch((error) => {
console.error('Failed to start server:', error);
process.exit(1);
});
}
module.exports = Server;