import { useEffect, useRef, useState } from 'react'; import SwaggerUI from 'swagger-ui-react'; import 'swagger-ui-react/swagger-ui.css'; import { env } from '@/config/env'; import { logger } from '@/utils/logger'; import { AlertCircle, RefreshCw } from 'lucide-react'; import { Button } from '@/components/ui/button'; interface SwaggerUIProps { specUrl?: string; spec?: object; useIframe?: boolean; // Option pour utiliser un iframe au lieu du composant React } /** * Composant Swagger UI pour afficher la documentation API * Charge le fichier OpenAPI depuis le backend ou utilise un spec fourni * Peut utiliser un iframe pour charger Swagger UI HTML directement */ export function SwaggerUIDoc({ specUrl, spec, useIframe = false }: SwaggerUIProps) { const containerRef = useRef(null); const iframeRef = useRef(null); const [error, setError] = useState(null); // Construire l'URL du fichier OpenAPI/Swagger ou Swagger UI HTML const getOpenApiUrl = () => { if (specUrl) return specUrl; // Si API_URL est relatif, construire l'URL complète const apiBase = env.API_URL.startsWith('http') ? env.API_URL : `${window.location.origin}${env.API_URL}`; const baseUrl = apiBase.replace(/\/api\/v1$/, ''); // Si useIframe est true, retourner l'URL de Swagger UI HTML if (useIframe) { return `${baseUrl}/swagger/index.html`; } // Sinon, essayer de charger le JSON Swagger // gin-swagger peut servir le JSON à différents endroits // Essayer /swagger/doc.json (endpoint standard gin-swagger) return `${baseUrl}/swagger/doc.json`; }; const getSwaggerUIUrl = () => { const apiBase = env.API_URL.startsWith('http') ? env.API_URL : `${window.location.origin}${env.API_URL}`; const baseUrl = apiBase.replace(/\/api\/v1$/, ''); return `${baseUrl}/swagger/index.html`; }; useEffect(() => { if (containerRef.current) { logger.debug('Swagger UI initialized', { specUrl: specUrl || getOpenApiUrl(), hasSpec: !!spec, useIframe, }); } }, [specUrl, spec, useIframe]); const swaggerConfig = { url: spec ? undefined : getOpenApiUrl(), spec, deepLinking: true, displayOperationId: false, defaultModelsExpandDepth: 1, defaultModelExpandDepth: 1, docExpansion: 'list' as const, filter: true, showExtensions: true, showCommonExtensions: true, tryItOutEnabled: true, supportedSubmitMethods: ['get', 'post', 'put', 'delete', 'patch'] as ('get' | 'post' | 'put' | 'delete' | 'patch')[], requestInterceptor: (request: any) => { // Ajouter le token d'authentification si disponible const token = localStorage.getItem('access_token'); if (token && request.headers) { request.headers['Authorization'] = `Bearer ${token}`; } // Ajouter le CSRF token si disponible const csrfToken = localStorage.getItem('csrf_token'); if (csrfToken && request.headers) { request.headers['X-CSRF-Token'] = csrfToken; } return request; }, onComplete: () => { setError(null); logger.debug('Swagger UI loaded successfully', { url: getOpenApiUrl(), }); }, onFailure: (err: Error) => { setError(err.message || 'Failed to load Swagger documentation'); logger.error('Failed to load Swagger UI', { error: err.message, stack: err.stack, url: getOpenApiUrl(), }); }, }; const handleRetry = () => { setError(null); // Force reload by updating the key window.location.reload(); }; // Si useIframe est true, utiliser un iframe pour charger Swagger UI HTML directement if (useIframe) { return (