- fix(dto): ajouter @IsEmail() sur contactFunctionalEmail/TechnicalEmail dans UpdateApiEntryDto - fix(docs): corriger stringify des erreurs (String(error) → error.message) - refactor(mapper): extraire toApiEntryListItem() dans api-entry.mapper.ts partagé - refactor(categories): supprimer CONVENTION_LABELS et toApiEntryListItem locaux dupliqués - refactor(browse): extraire CategoryFormModalComponent et CategoryDeleteModalComponent - refactor(api-detail): convertir editPreviewTitle() en computed() Generated with [Claude Code](https://claude.ai/code) via [Happy](https://happy.engineering) Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Happy <yesreply@happy.engineering>
69 lines
2.4 KiB
TypeScript
69 lines
2.4 KiB
TypeScript
import { Injectable, Inject, forwardRef } from '@nestjs/common';
|
|
import { execFile } from 'child_process';
|
|
import { promisify } from 'util';
|
|
import * as fs from 'fs/promises';
|
|
import * as path from 'path';
|
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
const AsyncApiGenerator = require('@asyncapi/generator');
|
|
import { ApisService } from '../apis/apis.service';
|
|
|
|
const execFileAsync = promisify(execFile);
|
|
|
|
/* Ajoute node_modules/.bin au PATH pour trouver redocly en local */
|
|
const localBin = path.join(process.cwd(), 'node_modules', '.bin');
|
|
const execEnv = {
|
|
...process.env,
|
|
PATH: `${localBin}${path.delimiter}${process.env.PATH ?? ''}`,
|
|
};
|
|
|
|
@Injectable()
|
|
export class DocsGenerationService {
|
|
private readonly outputDir = process.env.DOCS_OUTPUT_DIR ?? './docs-output';
|
|
|
|
constructor(
|
|
@Inject(forwardRef(() => ApisService))
|
|
private readonly apisService: ApisService,
|
|
) {}
|
|
|
|
async generate(entryId: string): Promise<void> {
|
|
const entry = await this.apisService.findOneOrThrow(entryId);
|
|
const entryDir = path.join(this.outputDir, entryId);
|
|
|
|
await fs.mkdir(entryDir, { recursive: true });
|
|
|
|
const yamlPath = path.join(entryDir, 'input.yaml');
|
|
await fs.writeFile(yamlPath, entry.yamlContent, 'utf-8');
|
|
|
|
try {
|
|
if (entry.type === 'ASYNCAPI') {
|
|
await this.generateAsyncApi(yamlPath, entryDir);
|
|
} else {
|
|
await this.generateOpenApi(yamlPath, entryDir);
|
|
}
|
|
|
|
const htmlPath = path.join(entryDir, 'index.html');
|
|
await this.apisService.updateInternal(entryId, { htmlPath, status: 'GENERATED' });
|
|
} catch (error) {
|
|
await this.apisService.updateInternal(entryId, {
|
|
status: 'ERROR',
|
|
errorMessage: error instanceof Error ? error.message : String(error),
|
|
});
|
|
}
|
|
}
|
|
|
|
private async generateAsyncApi(yamlPath: string, outputDir: string): Promise<void> {
|
|
/* API programmatique — aucun appel réseau, template résolu localement */
|
|
const templatePath = path.join(process.cwd(), 'node_modules', '@asyncapi', 'html-template');
|
|
const generator = new AsyncApiGenerator(templatePath, outputDir, { forceWrite: true });
|
|
await generator.generateFromFile(yamlPath);
|
|
}
|
|
|
|
private async generateOpenApi(yamlPath: string, outputDir: string): Promise<void> {
|
|
await execFileAsync('redocly', [
|
|
'build-docs',
|
|
yamlPath,
|
|
'--output', path.join(outputDir, 'index.html'),
|
|
], { shell: true, env: execEnv });
|
|
}
|
|
}
|