seo-generator-server/lib/batch/BatchController.js
StillHammer a2ffe7fec5 Refactor batch processing system with shared QueueProcessor base class
• 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>
2025-09-19 02:04:48 +08:00

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 };