- 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>
711 lines
18 KiB
JavaScript
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 }; |