seo-generator-server/lib/APIController.js
StillHammer f51c4095f6 Add modular pipeline demo system with real module integration
- 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>
2025-09-23 16:03:20 +08:00

711 lines
18 KiB
JavaScript

/**
* 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');
const { DynamicPromptEngine } = require('./prompt-engine/DynamicPromptEngine');
const { TrendManager } = require('./trend-prompts/TrendManager');
const { WorkflowEngine } = require('./workflow-configuration/WorkflowEngine');
class APIController {
constructor() {
this.articles = new Map(); // Cache articles en mémoire
this.projects = new Map(); // Cache projets
this.templates = new Map(); // Cache templates
// Initialize prompt engine components
this.promptEngine = new DynamicPromptEngine();
this.trendManager = new TrendManager();
this.workflowEngine = new WorkflowEngine();
}
// ========================================
// 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
});
}
}
// ========================================
// PROMPT ENGINE API
// ========================================
/**
* POST /api/generate-prompt - Génère un prompt adaptatif
*/
async generatePrompt(req, res) {
try {
const {
templateType = 'technical',
content = {},
csvData = null,
trend = null,
layerConfig = {},
customVariables = {}
} = req.body;
logSh(`🧠 Génération prompt: template=${templateType}, trend=${trend}`, 'INFO');
// Apply trend if specified
if (trend) {
await this.trendManager.setTrend(trend);
}
// Generate adaptive prompt
const result = await this.promptEngine.generateAdaptivePrompt({
templateType,
content,
csvData,
trend: this.trendManager.getCurrentTrend(),
layerConfig,
customVariables
});
res.json({
success: true,
prompt: result.prompt,
metadata: result.metadata,
timestamp: new Date().toISOString()
});
logSh(`✅ Prompt généré: ${result.prompt.length} caractères`, 'DEBUG');
} catch (error) {
logSh(`❌ Erreur génération prompt: ${error.message}`, 'ERROR');
res.status(500).json({
success: false,
error: 'Erreur lors de la génération du prompt',
message: error.message
});
}
}
/**
* GET /api/trends - Liste toutes les tendances disponibles
*/
async getTrends(req, res) {
try {
const trends = this.trendManager.getAvailableTrends();
const currentTrend = this.trendManager.getCurrentTrend();
res.json({
success: true,
data: {
trends,
currentTrend,
total: trends.length
},
timestamp: new Date().toISOString()
});
} catch (error) {
logSh(`❌ Erreur récupération tendances: ${error.message}`, 'ERROR');
res.status(500).json({
success: false,
error: 'Erreur lors de la récupération des tendances',
message: error.message
});
}
}
/**
* POST /api/trends/:trendId - Applique une tendance
*/
async setTrend(req, res) {
try {
const { trendId } = req.params;
const { customConfig = null } = req.body;
logSh(`🎯 Application tendance: ${trendId}`, 'INFO');
const trend = await this.trendManager.setTrend(trendId, customConfig);
res.json({
success: true,
data: {
trend,
applied: true
},
timestamp: new Date().toISOString()
});
} catch (error) {
logSh(`❌ Erreur application tendance ${req.params.trendId}: ${error.message}`, 'ERROR');
res.status(500).json({
success: false,
error: 'Erreur lors de l\'application de la tendance',
message: error.message
});
}
}
/**
* GET /api/prompt-engine/status - Status du moteur de prompts
*/
async getPromptEngineStatus(req, res) {
try {
const engineStatus = this.promptEngine.getEngineStatus();
const trendStatus = this.trendManager.getStatus();
const workflowStatus = this.workflowEngine.getEngineStatus();
res.json({
success: true,
data: {
engine: engineStatus,
trends: trendStatus,
workflow: workflowStatus,
health: 'operational'
},
timestamp: new Date().toISOString()
});
} catch (error) {
logSh(`❌ Erreur status prompt engine: ${error.message}`, 'ERROR');
res.status(500).json({
success: false,
error: 'Erreur lors de la récupération du status',
message: error.message
});
}
}
/**
* GET /api/workflow/sequences - Liste toutes les séquences de workflow
*/
async getWorkflowSequences(req, res) {
try {
const sequences = this.workflowEngine.getAvailableSequences();
res.json({
success: true,
data: {
sequences,
total: sequences.length
},
timestamp: new Date().toISOString()
});
} catch (error) {
logSh(`❌ Erreur récupération séquences workflow: ${error.message}`, 'ERROR');
res.status(500).json({
success: false,
error: 'Erreur lors de la récupération des séquences workflow',
message: error.message
});
}
}
/**
* POST /api/workflow/sequences - Crée une séquence de workflow personnalisée
*/
async createWorkflowSequence(req, res) {
try {
const { name, sequence } = req.body;
if (!name || !sequence) {
return res.status(400).json({
success: false,
error: 'Nom et séquence requis'
});
}
if (!this.workflowEngine.validateSequence(sequence)) {
return res.status(400).json({
success: false,
error: 'Séquence invalide'
});
}
const createdSequence = this.workflowEngine.createCustomSequence(name, sequence);
res.json({
success: true,
data: {
name,
sequence: createdSequence
},
timestamp: new Date().toISOString()
});
} catch (error) {
logSh(`❌ Erreur création séquence workflow: ${error.message}`, 'ERROR');
res.status(500).json({
success: false,
error: 'Erreur lors de la création de la séquence workflow',
message: error.message
});
}
}
/**
* POST /api/workflow/execute - Exécute un workflow configurable
*/
async executeConfigurableWorkflow(req, res) {
try {
const {
content,
sequenceName = 'default',
customSequence = null,
selectiveConfig = {},
adversarialConfig = {},
humanConfig = {},
patternConfig = {},
csvData = {},
personalities = {}
} = req.body;
if (!content || typeof content !== 'object') {
return res.status(400).json({
success: false,
error: 'Contenu requis (objet)'
});
}
logSh(`🔄 Exécution workflow configurable: ${customSequence ? 'custom' : sequenceName}`, 'INFO');
const result = await this.workflowEngine.executeConfigurableWorkflow(content, {
sequenceName,
customSequence,
selectiveConfig,
adversarialConfig,
humanConfig,
patternConfig,
csvData,
personalities
});
res.json({
success: result.success,
data: {
content: result.content,
stats: result.stats
},
error: result.error || null,
timestamp: new Date().toISOString()
});
} catch (error) {
logSh(`❌ Erreur exécution workflow configurable: ${error.message}`, 'ERROR');
res.status(500).json({
success: false,
error: 'Erreur lors de l\'exécution du workflow configurable',
message: error.message
});
}
}
}
module.exports = { APIController };