- Add complete modular demo interface (public/modular-pipeline-demo.html) - Add standalone demo server (simple-server.js) on port 3333 - Integrate real SelectiveCore, AdversarialCore, HumanSimulation, PatternBreaking modules - Add configurable pipeline with step-by-step content transformation display - Add new trend management and workflow configuration modules - Add comprehensive test suite for full pipeline validation - Update core modules for better modular integration and demo compatibility 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
515 lines
13 KiB
JavaScript
515 lines
13 KiB
JavaScript
// ========================================
|
|
// BATCH CONTROLLER - API ENDPOINTS
|
|
// Responsabilité: Gestion API pour traitement batch avec configuration pipeline
|
|
// ========================================
|
|
|
|
const { logSh } = require('../ErrorReporting');
|
|
const fs = require('fs').promises;
|
|
const path = require('path');
|
|
const { BatchProcessor } = require('./BatchProcessor');
|
|
const { DigitalOceanTemplates } = require('./DigitalOceanTemplates');
|
|
const { TrendManager } = require('../trend-prompts/TrendManager');
|
|
|
|
/**
|
|
* BATCH CONTROLLER
|
|
* Gestion complète de l'interface de traitement batch
|
|
*/
|
|
class BatchController {
|
|
|
|
constructor() {
|
|
this.configPath = path.join(__dirname, '../../config/batch-config.json');
|
|
this.statusPath = path.join(__dirname, '../../config/batch-status.json');
|
|
|
|
// Initialiser les composants Phase 2
|
|
this.batchProcessor = new BatchProcessor();
|
|
this.digitalOceanTemplates = new DigitalOceanTemplates();
|
|
this.trendManager = new TrendManager();
|
|
|
|
// Configuration par défaut
|
|
this.defaultConfig = {
|
|
selective: 'standardEnhancement',
|
|
adversarial: 'light',
|
|
humanSimulation: 'none',
|
|
patternBreaking: 'none',
|
|
intensity: 1.0,
|
|
rowRange: { start: 2, end: 10 },
|
|
saveIntermediateSteps: false,
|
|
trendId: null, // Tendance à appliquer (optionnel)
|
|
lastUpdated: new Date().toISOString()
|
|
};
|
|
|
|
// État par défaut
|
|
this.defaultStatus = {
|
|
status: 'idle',
|
|
currentRow: null,
|
|
totalRows: 0,
|
|
progress: 0,
|
|
startTime: null,
|
|
estimatedEnd: null,
|
|
errors: [],
|
|
lastResult: null,
|
|
config: this.defaultConfig
|
|
};
|
|
|
|
this.initializeFiles();
|
|
}
|
|
|
|
/**
|
|
* Initialise les fichiers de configuration
|
|
*/
|
|
async initializeFiles() {
|
|
try {
|
|
// Créer le dossier config s'il n'existe pas
|
|
const configDir = path.dirname(this.configPath);
|
|
await fs.mkdir(configDir, { recursive: true });
|
|
|
|
// Créer config par défaut si inexistant
|
|
try {
|
|
await fs.access(this.configPath);
|
|
} catch {
|
|
await fs.writeFile(this.configPath, JSON.stringify(this.defaultConfig, null, 2));
|
|
logSh('📝 Configuration batch par défaut créée', 'DEBUG');
|
|
}
|
|
|
|
// Créer status par défaut si inexistant
|
|
try {
|
|
await fs.access(this.statusPath);
|
|
} catch {
|
|
await fs.writeFile(this.statusPath, JSON.stringify(this.defaultStatus, null, 2));
|
|
logSh('📊 Status batch par défaut créé', 'DEBUG');
|
|
}
|
|
|
|
} catch (error) {
|
|
logSh(`❌ Erreur initialisation fichiers batch: ${error.message}`, 'ERROR');
|
|
}
|
|
}
|
|
|
|
// ========================================
|
|
// ENDPOINTS CONFIGURATION
|
|
// ========================================
|
|
|
|
/**
|
|
* GET /api/batch/config
|
|
* Récupère la configuration actuelle
|
|
*/
|
|
async getConfig(req, res) {
|
|
try {
|
|
// Utiliser la nouvelle API du BatchProcessor refactorisé
|
|
const status = this.batchProcessor.getExtendedStatus();
|
|
|
|
// Ajouter les tendances disponibles
|
|
const availableTrends = this.trendManager.getAvailableTrends();
|
|
const currentTrend = this.trendManager.getCurrentTrend();
|
|
|
|
logSh('📋 Configuration batch récupérée', 'DEBUG');
|
|
|
|
res.json({
|
|
success: true,
|
|
config: status.config,
|
|
availableOptions: status.availableOptions,
|
|
trends: {
|
|
available: availableTrends,
|
|
current: currentTrend,
|
|
categories: this.groupTrendsByCategory(availableTrends)
|
|
}
|
|
});
|
|
|
|
} catch (error) {
|
|
logSh(`❌ Erreur récupération config: ${error.message}`, 'ERROR');
|
|
res.status(500).json({
|
|
success: false,
|
|
error: 'Erreur récupération configuration',
|
|
details: error.message
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* POST /api/batch/config
|
|
* Sauvegarde la configuration
|
|
*/
|
|
async saveConfig(req, res) {
|
|
try {
|
|
const newConfig = req.body;
|
|
|
|
// Utiliser la nouvelle API du BatchProcessor refactorisé
|
|
const result = await this.batchProcessor.updateConfiguration(newConfig);
|
|
|
|
res.json({
|
|
success: true,
|
|
message: 'Configuration sauvegardée avec succès',
|
|
config: result.config
|
|
});
|
|
|
|
} catch (error) {
|
|
logSh(`❌ Erreur sauvegarde config: ${error.message}`, 'ERROR');
|
|
res.status(500).json({
|
|
success: false,
|
|
error: 'Erreur sauvegarde configuration',
|
|
details: error.message
|
|
});
|
|
}
|
|
}
|
|
|
|
// ========================================
|
|
// ENDPOINTS CONTRÔLE TRAITEMENT
|
|
// ========================================
|
|
|
|
/**
|
|
* POST /api/batch/start
|
|
* Démarre le traitement batch
|
|
*/
|
|
async startBatch(req, res) {
|
|
try {
|
|
// Démarrer le traitement via BatchProcessor
|
|
const status = await this.batchProcessor.start();
|
|
|
|
logSh(`🚀 Traitement batch démarré - ${status.totalRows} lignes`, 'INFO');
|
|
|
|
res.json({
|
|
success: true,
|
|
message: 'Traitement batch démarré',
|
|
status: status
|
|
});
|
|
|
|
} catch (error) {
|
|
logSh(`❌ Erreur démarrage batch: ${error.message}`, 'ERROR');
|
|
res.status(500).json({
|
|
success: false,
|
|
error: 'Erreur démarrage traitement',
|
|
details: error.message
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* POST /api/batch/stop
|
|
* Arrête le traitement batch
|
|
*/
|
|
async stopBatch(req, res) {
|
|
try {
|
|
const status = await this.batchProcessor.stop();
|
|
|
|
logSh('🛑 Traitement batch arrêté', 'INFO');
|
|
|
|
res.json({
|
|
success: true,
|
|
message: 'Traitement batch arrêté',
|
|
status: status
|
|
});
|
|
|
|
} catch (error) {
|
|
logSh(`❌ Erreur arrêt batch: ${error.message}`, 'ERROR');
|
|
res.status(500).json({
|
|
success: false,
|
|
error: 'Erreur arrêt traitement',
|
|
details: error.message
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* POST /api/batch/pause
|
|
* Met en pause le traitement
|
|
*/
|
|
async pauseBatch(req, res) {
|
|
try {
|
|
const status = await this.batchProcessor.pause();
|
|
|
|
logSh('⏸️ Traitement batch mis en pause', 'INFO');
|
|
|
|
res.json({
|
|
success: true,
|
|
message: 'Traitement mis en pause',
|
|
status: status
|
|
});
|
|
|
|
} catch (error) {
|
|
logSh(`❌ Erreur pause batch: ${error.message}`, 'ERROR');
|
|
res.status(500).json({
|
|
success: false,
|
|
error: 'Erreur pause traitement',
|
|
details: error.message
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* POST /api/batch/resume
|
|
* Reprend le traitement
|
|
*/
|
|
async resumeBatch(req, res) {
|
|
try {
|
|
const status = await this.batchProcessor.resume();
|
|
|
|
logSh('▶️ Traitement batch repris', 'INFO');
|
|
|
|
res.json({
|
|
success: true,
|
|
message: 'Traitement repris',
|
|
status: status
|
|
});
|
|
|
|
} catch (error) {
|
|
logSh(`❌ Erreur reprise batch: ${error.message}`, 'ERROR');
|
|
res.status(500).json({
|
|
success: false,
|
|
error: 'Erreur reprise traitement',
|
|
details: error.message
|
|
});
|
|
}
|
|
}
|
|
|
|
// ========================================
|
|
// ENDPOINTS MONITORING
|
|
// ========================================
|
|
|
|
/**
|
|
* GET /api/batch/status
|
|
* Récupère l'état actuel du traitement
|
|
*/
|
|
async getStatus(req, res) {
|
|
try {
|
|
const status = this.batchProcessor.getStatus();
|
|
|
|
res.json({
|
|
success: true,
|
|
status: status,
|
|
timestamp: new Date().toISOString()
|
|
});
|
|
|
|
} catch (error) {
|
|
logSh(`❌ Erreur récupération status: ${error.message}`, 'ERROR');
|
|
res.status(500).json({
|
|
success: false,
|
|
error: 'Erreur récupération status',
|
|
details: error.message
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* GET /api/batch/progress
|
|
* Récupère la progression détaillée
|
|
*/
|
|
async getProgress(req, res) {
|
|
try {
|
|
const progress = this.batchProcessor.getProgress();
|
|
|
|
res.json({
|
|
success: true,
|
|
progress: progress,
|
|
timestamp: new Date().toISOString()
|
|
});
|
|
|
|
} catch (error) {
|
|
logSh(`❌ Erreur récupération progress: ${error.message}`, 'ERROR');
|
|
res.status(500).json({
|
|
success: false,
|
|
error: 'Erreur récupération progression',
|
|
details: error.message
|
|
});
|
|
}
|
|
}
|
|
|
|
// ========================================
|
|
// ENDPOINTS TENDANCES
|
|
// ========================================
|
|
|
|
/**
|
|
* GET /api/batch/trends
|
|
* Liste toutes les tendances disponibles
|
|
*/
|
|
async getTrends(req, res) {
|
|
try {
|
|
const trends = this.trendManager.getAvailableTrends();
|
|
const current = this.trendManager.getCurrentTrend();
|
|
const status = this.trendManager.getStatus();
|
|
|
|
res.json({
|
|
success: true,
|
|
trends: {
|
|
available: trends,
|
|
current: current,
|
|
categories: this.groupTrendsByCategory(trends),
|
|
status: status
|
|
},
|
|
timestamp: new Date().toISOString()
|
|
});
|
|
|
|
} catch (error) {
|
|
logSh(`❌ Erreur récupération tendances: ${error.message}`, 'ERROR');
|
|
res.status(500).json({
|
|
success: false,
|
|
error: 'Erreur récupération tendances',
|
|
details: error.message
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* POST /api/batch/trends/select
|
|
* Sélectionne une tendance
|
|
*/
|
|
async selectTrend(req, res) {
|
|
try {
|
|
const { trendId } = req.body;
|
|
|
|
if (!trendId) {
|
|
return res.status(400).json({
|
|
success: false,
|
|
error: 'ID de tendance requis'
|
|
});
|
|
}
|
|
|
|
const result = await this.trendManager.setTrend(trendId);
|
|
|
|
logSh(`🎯 Tendance sélectionnée: ${result.name}`, 'INFO');
|
|
|
|
res.json({
|
|
success: true,
|
|
trend: result,
|
|
message: `Tendance "${result.name}" appliquée`,
|
|
timestamp: new Date().toISOString()
|
|
});
|
|
|
|
} catch (error) {
|
|
logSh(`❌ Erreur sélection tendance: ${error.message}`, 'ERROR');
|
|
res.status(400).json({
|
|
success: false,
|
|
error: 'Erreur sélection tendance',
|
|
details: error.message
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* DELETE /api/batch/trends
|
|
* Désactive la tendance actuelle
|
|
*/
|
|
async clearTrend(req, res) {
|
|
try {
|
|
this.trendManager.clearTrend();
|
|
|
|
logSh('🔄 Tendance désactivée', 'INFO');
|
|
|
|
res.json({
|
|
success: true,
|
|
message: 'Aucune tendance appliquée',
|
|
timestamp: new Date().toISOString()
|
|
});
|
|
|
|
} catch (error) {
|
|
logSh(`❌ Erreur désactivation tendance: ${error.message}`, 'ERROR');
|
|
res.status(500).json({
|
|
success: false,
|
|
error: 'Erreur désactivation tendance',
|
|
details: error.message
|
|
});
|
|
}
|
|
}
|
|
|
|
// ========================================
|
|
// HELPER METHODS TENDANCES
|
|
// ========================================
|
|
|
|
/**
|
|
* Groupe les tendances par catégorie
|
|
*/
|
|
groupTrendsByCategory(trends) {
|
|
const categories = {};
|
|
|
|
trends.forEach(trend => {
|
|
const category = trend.category || 'autre';
|
|
if (!categories[category]) {
|
|
categories[category] = [];
|
|
}
|
|
categories[category].push(trend);
|
|
});
|
|
|
|
return categories;
|
|
}
|
|
|
|
// ========================================
|
|
// ENDPOINTS DIGITAL OCEAN
|
|
// ========================================
|
|
|
|
/**
|
|
* GET /api/batch/templates
|
|
* Liste les templates disponibles
|
|
*/
|
|
async getTemplates(req, res) {
|
|
try {
|
|
const templates = await this.digitalOceanTemplates.listAvailableTemplates();
|
|
const stats = this.digitalOceanTemplates.getCacheStats();
|
|
|
|
res.json({
|
|
success: true,
|
|
templates: templates,
|
|
cacheStats: stats,
|
|
timestamp: new Date().toISOString()
|
|
});
|
|
|
|
} catch (error) {
|
|
logSh(`❌ Erreur récupération templates: ${error.message}`, 'ERROR');
|
|
res.status(500).json({
|
|
success: false,
|
|
error: 'Erreur récupération templates',
|
|
details: error.message
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* GET /api/batch/templates/:filename
|
|
* Récupère un template spécifique
|
|
*/
|
|
async getTemplate(req, res) {
|
|
try {
|
|
const { filename } = req.params;
|
|
const template = await this.digitalOceanTemplates.getTemplate(filename);
|
|
|
|
res.json({
|
|
success: true,
|
|
filename: filename,
|
|
template: template,
|
|
timestamp: new Date().toISOString()
|
|
});
|
|
|
|
} catch (error) {
|
|
logSh(`❌ Erreur récupération template ${req.params.filename}: ${error.message}`, 'ERROR');
|
|
res.status(500).json({
|
|
success: false,
|
|
error: 'Erreur récupération template',
|
|
details: error.message
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* DELETE /api/batch/cache
|
|
* Vide le cache des templates
|
|
*/
|
|
async clearCache(req, res) {
|
|
try {
|
|
await this.digitalOceanTemplates.clearCache();
|
|
|
|
res.json({
|
|
success: true,
|
|
message: 'Cache vidé avec succès',
|
|
timestamp: new Date().toISOString()
|
|
});
|
|
|
|
} catch (error) {
|
|
logSh(`❌ Erreur vidage cache: ${error.message}`, 'ERROR');
|
|
res.status(500).json({
|
|
success: false,
|
|
error: 'Erreur vidage cache',
|
|
details: error.message
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
// ============= EXPORTS =============
|
|
module.exports = { BatchController }; |