• Created QueueProcessor base class for shared queue management, retry logic, and persistence • Refactored BatchProcessor to extend QueueProcessor (385→142 lines, 63% reduction) • Created BatchController with comprehensive API endpoints for batch operations • Added Digital Ocean templates integration with caching • Integrated batch endpoints into ManualServer with proper routing • Fixed infinite recursion bug in queue status calculations • Eliminated ~400 lines of duplicate code across processors • Maintained backward compatibility with existing test interfaces Architecture benefits: - Single source of truth for queue processing logic - Simplified maintenance and bug fixes - Clear separation between AutoProcessor (production) and BatchProcessor (R&D) - Extensible design for future processor types 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
385 lines
9.6 KiB
JavaScript
385 lines
9.6 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');
|
|
|
|
/**
|
|
* 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();
|
|
|
|
// Configuration par défaut
|
|
this.defaultConfig = {
|
|
selective: 'standardEnhancement',
|
|
adversarial: 'light',
|
|
humanSimulation: 'none',
|
|
patternBreaking: 'none',
|
|
intensity: 1.0,
|
|
rowRange: { start: 2, end: 10 },
|
|
saveIntermediateSteps: false,
|
|
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();
|
|
|
|
logSh('📋 Configuration batch récupérée', 'DEBUG');
|
|
|
|
res.json({
|
|
success: true,
|
|
config: status.config,
|
|
availableOptions: status.availableOptions
|
|
});
|
|
|
|
} 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 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 }; |