/** * Contrôleur API RESTful pour SEO Generator * Centralise toute la logique API métier */ const { logSh } = require('./ErrorReporting'); const { handleFullWorkflow } = require('./Main'); const { getPersonalities, readInstructionsData } = require('./BrainConfig'); const { getStoredArticle, getRecentArticles } = require('./ArticleStorage'); class APIController { constructor() { this.articles = new Map(); // Cache articles en mémoire this.projects = new Map(); // Cache projets this.templates = new Map(); // Cache templates } // ======================================== // GESTION ARTICLES // ======================================== /** * GET /api/articles - Liste tous les articles */ async getArticles(req, res) { try { const { limit = 50, offset = 0, project, status } = req.query; logSh(`📋 Récupération articles: limit=${limit}, offset=${offset}`, 'DEBUG'); // Récupération depuis Google Sheets const articles = await getRecentArticles(parseInt(limit)); // Filtrage optionnel let filteredArticles = articles; if (project) { filteredArticles = articles.filter(a => a.project === project); } if (status) { filteredArticles = filteredArticles.filter(a => a.status === status); } res.json({ success: true, data: { articles: filteredArticles.slice(offset, offset + limit), total: filteredArticles.length, limit: parseInt(limit), offset: parseInt(offset) }, timestamp: new Date().toISOString() }); } catch (error) { logSh(`❌ Erreur récupération articles: ${error.message}`, 'ERROR'); res.status(500).json({ success: false, error: 'Erreur lors de la récupération des articles', message: error.message }); } } /** * GET /api/articles/:id - Récupère un article spécifique */ async getArticle(req, res) { try { const { id } = req.params; const { format = 'json' } = req.query || {}; logSh(`📄 Récupération article ID: ${id}`, 'DEBUG'); const article = await getStoredArticle(id); if (!article) { return res.status(404).json({ success: false, error: 'Article non trouvé', id }); } // Format de réponse if (format === 'html') { res.setHeader('Content-Type', 'text/html; charset=utf-8'); res.send(article.htmlContent || article.content); } else if (format === 'text') { res.setHeader('Content-Type', 'text/plain; charset=utf-8'); res.send(article.textContent || article.content); } else { res.json({ success: true, data: article, timestamp: new Date().toISOString() }); } } catch (error) { logSh(`❌ Erreur récupération article ${req.params.id}: ${error.message}`, 'ERROR'); res.status(500).json({ success: false, error: 'Erreur lors de la récupération de l\'article', message: error.message }); } } /** * POST /api/articles - Créer un nouvel article */ async createArticle(req, res) { try { const { keyword, rowNumber, project = 'api', config = {}, template, personalityPreference } = req.body; // Validation if (!keyword && !rowNumber) { return res.status(400).json({ success: false, error: 'Mot-clé ou numéro de ligne requis' }); } logSh(`✨ Création article: ${keyword || `ligne ${rowNumber}`}`, 'INFO'); // Configuration par défaut const workflowConfig = { rowNumber: rowNumber || 2, source: 'api', project, selectiveStack: config.selectiveStack || 'standardEnhancement', adversarialMode: config.adversarialMode || 'light', humanSimulationMode: config.humanSimulationMode || 'none', patternBreakingMode: config.patternBreakingMode || 'none', personalityPreference, template, ...config }; // Si mot-clé fourni, créer données temporaires if (keyword && !rowNumber) { workflowConfig.csvData = { mc0: keyword, t0: `Guide complet ${keyword}`, personality: personalityPreference || { nom: 'Marc', style: 'professionnel' } }; } // Exécution du workflow const result = await handleFullWorkflow(workflowConfig); res.status(201).json({ success: true, data: { id: result.id || result.slug, article: result, config: workflowConfig }, message: 'Article créé avec succès', timestamp: new Date().toISOString() }); } catch (error) { logSh(`❌ Erreur création article: ${error.message}`, 'ERROR'); res.status(500).json({ success: false, error: 'Erreur lors de la création de l\'article', message: error.message }); } } // ======================================== // GESTION PROJETS // ======================================== /** * GET /api/projects - Liste tous les projets */ async getProjects(req, res) { try { const projects = Array.from(this.projects.values()); res.json({ success: true, data: { projects, total: projects.length }, timestamp: new Date().toISOString() }); } catch (error) { logSh(`❌ Erreur récupération projets: ${error.message}`, 'ERROR'); res.status(500).json({ success: false, error: 'Erreur lors de la récupération des projets', message: error.message }); } } /** * POST /api/projects - Créer un nouveau projet */ async createProject(req, res) { try { // Validation body null/undefined if (!req.body) { return res.status(400).json({ success: false, error: 'Corps de requête requis' }); } const { name, description, config = {} } = req.body; if (!name) { return res.status(400).json({ success: false, error: 'Nom du projet requis' }); } const project = { id: `project_${Date.now()}`, name, description, config, createdAt: new Date().toISOString(), articlesCount: 0 }; this.projects.set(project.id, project); logSh(`📁 Projet créé: ${name}`, 'INFO'); res.status(201).json({ success: true, data: project, message: 'Projet créé avec succès' }); } catch (error) { logSh(`❌ Erreur création projet: ${error.message}`, 'ERROR'); res.status(500).json({ success: false, error: 'Erreur lors de la création du projet', message: error.message }); } } // ======================================== // GESTION TEMPLATES // ======================================== /** * GET /api/templates - Liste tous les templates */ async getTemplates(req, res) { try { const templates = Array.from(this.templates.values()); res.json({ success: true, data: { templates, total: templates.length }, timestamp: new Date().toISOString() }); } catch (error) { logSh(`❌ Erreur récupération templates: ${error.message}`, 'ERROR'); res.status(500).json({ success: false, error: 'Erreur lors de la récupération des templates', message: error.message }); } } /** * POST /api/templates - Créer un nouveau template */ async createTemplate(req, res) { try { const { name, content, description, category = 'custom' } = req.body; if (!name || !content) { return res.status(400).json({ success: false, error: 'Nom et contenu du template requis' }); } const template = { id: `template_${Date.now()}`, name, content, description, category, createdAt: new Date().toISOString() }; this.templates.set(template.id, template); logSh(`📋 Template créé: ${name}`, 'INFO'); res.status(201).json({ success: true, data: template, message: 'Template créé avec succès' }); } catch (error) { logSh(`❌ Erreur création template: ${error.message}`, 'ERROR'); res.status(500).json({ success: false, error: 'Erreur lors de la création du template', message: error.message }); } } // ======================================== // CONFIGURATION & MONITORING // ======================================== /** * GET /api/config/personalities - Configuration personnalités */ async getPersonalitiesConfig(req, res) { try { const personalities = await getPersonalities(); res.json({ success: true, data: { personalities, total: personalities.length }, timestamp: new Date().toISOString() }); } catch (error) { logSh(`❌ Erreur config personnalités: ${error.message}`, 'ERROR'); res.status(500).json({ success: false, error: 'Erreur lors de la récupération des personnalités', message: error.message }); } } /** * GET /api/health - Health check */ async getHealth(req, res) { try { const health = { status: 'healthy', timestamp: new Date().toISOString(), version: '1.0.0', uptime: process.uptime(), memory: process.memoryUsage(), environment: process.env.NODE_ENV || 'development' }; res.json({ success: true, data: health }); } catch (error) { res.status(500).json({ success: false, error: 'Health check failed', message: error.message }); } } /** * GET /api/metrics - Métriques système */ async getMetrics(req, res) { try { const metrics = { articles: { total: this.articles.size, recent: Array.from(this.articles.values()).filter( a => new Date(a.createdAt) > new Date(Date.now() - 24 * 60 * 60 * 1000) ).length }, projects: { total: this.projects.size }, templates: { total: this.templates.size }, system: { uptime: process.uptime(), memory: process.memoryUsage(), platform: process.platform, nodeVersion: process.version } }; res.json({ success: true, data: metrics, timestamp: new Date().toISOString() }); } catch (error) { logSh(`❌ Erreur métriques: ${error.message}`, 'ERROR'); res.status(500).json({ success: false, error: 'Erreur lors de la récupération des métriques', message: error.message }); } } } module.exports = { APIController };