import express from 'express';
import NextJSIntegrationTool from './NextJSIntegrationTool.js';
import fs from 'fs';
import path from 'path';
/**
* Interface pour l'outil d'intégration NextJS
* Expose les fonctionnalités de l'outil via une API REST
*/
export class NextJSIntegrationToolInterface {
private tool: NextJSIntegrationTool;
private outputBaseDir: string;
constructor() {
this.tool = new NextJSIntegrationTool();
this.outputBaseDir = process.env.NEXTJS_OUTPUT_DIR || path.join(process.cwd(), 'output', 'nextjs-integrations');
// Créer le répertoire de sortie s'il n'existe pas
if (!fs.existsSync(this.outputBaseDir)) {
fs.mkdirSync(this.outputBaseDir, { recursive: true });
}
}
/**
* Enregistre les routes de l'API pour l'outil
* @param app Application Express
*/
registerRoutes(app: express.Application): void {
// Route pour générer une intégration NextJS
app.post('/api/nextjs-integration/generate', async (req: express.Request, res: express.Response) => {
try {
const {
workflowId,
workflowName,
apiBasePath = '/api',
generateOpenAPI = true,
generateTypes = true,
includeWebhooks = true
} = req.body;
// Valider les paramètres requis
if (!workflowId) {
return res.status(400).json({
success: false,
message: 'Le paramètre workflowId est requis'
});
}
if (!workflowName) {
return res.status(400).json({
success: false,
message: 'Le paramètre workflowName est requis'
});
}
// Créer le répertoire de sortie spécifique au workflow
const outputDir = path.join(this.outputBaseDir, this.slugify(workflowName));
// Exécuter l'outil
const result = await this.tool.execute({
workflowId,
workflowName,
apiBasePath,
outputDir,
generateOpenAPI,
generateTypes,
includeWebhooks
});
// Retourner le résultat
return res.json(result);
} catch (error: unknown) {
console.error('Erreur lors de la génération de l\'intégration NextJS:', error);
const errorMessage = error instanceof Error ? error.message : String(error);
return res.status(500).json({
success: false,
message: `Erreur: ${errorMessage}`
});
}
});
// Route pour lister les intégrations NextJS générées
app.get('/api/nextjs-integration/list', (req: express.Request, res: express.Response) => {
try {
// Lire le contenu du répertoire de sortie
const integrations = fs.readdirSync(this.outputBaseDir)
.filter(item => fs.statSync(path.join(this.outputBaseDir, item)).isDirectory())
.map(dir => {
const integrationPath = path.join(this.outputBaseDir, dir);
// Vérifier si l'intégration contient une spécification OpenAPI
const hasOpenAPI = fs.existsSync(path.join(integrationPath, 'openapi.json'));
// Vérifier si l'intégration contient des types TypeScript
const hasTypes = fs.existsSync(path.join(integrationPath, 'types.ts'));
// Vérifier si l'intégration contient un client API
const hasApiClient = fs.existsSync(path.join(integrationPath, 'api-client.ts'));
// Vérifier si l'intégration contient des routes API
const hasApiRoutes = fs.existsSync(path.join(integrationPath, 'api'));
return {
name: dir,
path: integrationPath,
features: {
openapi: hasOpenAPI,
types: hasTypes,
apiClient: hasApiClient,
apiRoutes: hasApiRoutes
},
createdAt: fs.statSync(integrationPath).birthtime
};
})
.sort((a: any, b: any) => b.createdAt.getTime() - a.createdAt.getTime());
return res.json({
success: true,
integrations
});
} catch (error: unknown) {
console.error('Erreur lors de la liste des intégrations NextJS:', error);
const errorMessage = error instanceof Error ? error.message : String(error);
return res.status(500).json({
success: false,
message: `Erreur: ${errorMessage}`
});
}
});
// Route pour télécharger une intégration NextJS
app.get('/api/nextjs-integration/download/:name', (req: express.Request, res: express.Response) => {
try {
const { name } = req.params;
const integrationPath = path.join(this.outputBaseDir, name);
// Vérifier si l'intégration existe
if (!fs.existsSync(integrationPath)) {
return res.status(404).json({
success: false,
message: `L'intégration ${name} n'existe pas`
});
}
// Créer une archive ZIP de l'intégration
const archiver = require('archiver');
const archive = archiver('zip', {
zlib: { level: 9 }
});
// Configurer la réponse HTTP
res.attachment(`${name}.zip`);
archive.pipe(res);
// Ajouter les fichiers à l'archive
archive.directory(integrationPath, name);
// Finaliser l'archive
archive.finalize();
// Retourner explicitement undefined pour satisfaire TypeScript
return;
} catch (error: unknown) {
console.error('Erreur lors du téléchargement de l\'intégration NextJS:', error);
const errorMessage = error instanceof Error ? error.message : String(error);
return res.status(500).json({
success: false,
message: `Erreur: ${errorMessage}`
});
}
});
// Route pour supprimer une intégration NextJS
app.delete('/api/nextjs-integration/:name', (req: express.Request, res: express.Response) => {
try {
const { name } = req.params;
const integrationPath = path.join(this.outputBaseDir, name);
// Vérifier si l'intégration existe
if (!fs.existsSync(integrationPath)) {
return res.status(404).json({
success: false,
message: `L'intégration ${name} n'existe pas`
});
}
// Supprimer le répertoire de l'intégration
fs.rmSync(integrationPath, { recursive: true, force: true });
return res.json({
success: true,
message: `L'intégration ${name} a été supprimée avec succès`
});
} catch (error: unknown) {
console.error('Erreur lors de la suppression de l\'intégration NextJS:', error);
const errorMessage = error instanceof Error ? error.message : String(error);
return res.status(500).json({
success: false,
message: `Erreur: ${errorMessage}`
});
}
});
// Route pour accéder à la documentation OpenAPI d'une intégration
app.get('/api/nextjs-integration/:name/openapi', (req: express.Request, res: express.Response) => {
try {
const { name } = req.params;
const openApiPath = path.join(this.outputBaseDir, name, 'openapi.json');
// Vérifier si la spécification OpenAPI existe
if (!fs.existsSync(openApiPath)) {
return res.status(404).json({
success: false,
message: `La spécification OpenAPI pour l'intégration ${name} n'existe pas`
});
}
// Lire la spécification OpenAPI
const openApiSpec = JSON.parse(fs.readFileSync(openApiPath, 'utf8'));
return res.json(openApiSpec);
} catch (error: unknown) {
console.error('Erreur lors de l\'accès à la spécification OpenAPI:', error);
const errorMessage = error instanceof Error ? error.message : String(error);
return res.status(500).json({
success: false,
message: `Erreur: ${errorMessage}`
});
}
});
// Route pour la documentation Swagger UI
app.get('/api/nextjs-integration/:name/swagger', (req: express.Request, res: express.Response) => {
try {
const { name } = req.params;
const openApiPath = path.join(this.outputBaseDir, name, 'openapi.json');
// Vérifier si la spécification OpenAPI existe
if (!fs.existsSync(openApiPath)) {
return res.status(404).send(`<html>
<body>
<h1>Erreur 404</h1>
<p>La spécification OpenAPI pour l'intégration ${name} n'existe pas</p>
</body>
</html>`);
}
// Générer la page Swagger UI
const swaggerHtml = `
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<title>API ${name} - Documentation Swagger</title>
<link rel="stylesheet" type="text/css" href="https://unpkg.com/swagger-ui-dist@4.5.0/swagger-ui.css" />
<style>
html { box-sizing: border-box; overflow: -moz-scrollbars-vertical; overflow-y: scroll; }
*, *:before, *:after { box-sizing: inherit; }
body { margin: 0; background: #fafafa; }
</style>
</head>
<body>
<div id="swagger-ui"></div>
<script src="https://unpkg.com/swagger-ui-dist@4.5.0/swagger-ui-bundle.js"></script>
<script src="https://unpkg.com/swagger-ui-dist@4.5.0/swagger-ui-standalone-preset.js"></script>
<script>
window.onload = function() {
const ui = SwaggerUIBundle({
url: "/api/nextjs-integration/${name}/openapi",
dom_id: '#swagger-ui',
deepLinking: true,
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIStandalonePreset
],
plugins: [
SwaggerUIBundle.plugins.DownloadUrl
],
layout: "BaseLayout"
});
window.ui = ui;
};
</script>
</body>
</html>
`;
return res.send(swaggerHtml);
} catch (error: unknown) {
console.error('Erreur lors de l\'accès à Swagger UI:', error);
const errorMessage = error instanceof Error ? error.message : String(error);
return res.status(500).send(`<html>
<body>
<h1>Erreur 500</h1>
<p>Erreur lors de l'accès à Swagger UI: ${errorMessage}</p>
</body>
</html>`);
}
});
}
/**
* Convertit une chaîne en format slug
* @param text Chaîne à convertir
* @returns Chaîne en format slug
*/
private slugify(text: string): string {
return text
.toString()
.toLowerCase()
.replace(/\s+/g, '-')
.replace(/[^\w\-]+/g, '')
.replace(/\-\-+/g, '-')
.replace(/^-+/, '')
.replace(/-+$/, '');
}
}