/** * Request handlers for OpenAPI specification and Swagger UI */ import type { IRequestContext } from '../core/smartserve.interfaces.js'; import { OpenApiGenerator } from './openapi.generator.js'; import type { IOpenApiGeneratorOptions } from './openapi.types.js'; /** * Create a handler that serves the OpenAPI JSON specification */ export function createOpenApiHandler(options: IOpenApiGeneratorOptions) { let cachedSpec: string | null = null; return async (ctx: IRequestContext): Promise => { // Generate spec on first request (lazy loading) if (!cachedSpec) { const generator = new OpenApiGenerator(options); cachedSpec = generator.toJSON(); } return new Response(cachedSpec, { status: 200, headers: { 'Content-Type': 'application/json', 'Cache-Control': 'public, max-age=3600', }, }); }; } /** * Create a handler that serves Swagger UI * * Loads Swagger UI from unpkg CDN - no bundled assets needed */ export function createSwaggerUiHandler(specUrl: string = '/openapi.json', title: string = 'API Documentation') { const html = ` ${escapeHtml(title)}
`; return async (ctx: IRequestContext): Promise => { return new Response(html, { status: 200, headers: { 'Content-Type': 'text/html; charset=utf-8', 'Cache-Control': 'public, max-age=86400', }, }); }; } /** * Create a handler that serves ReDoc UI (alternative to Swagger UI) * * ReDoc provides a clean, responsive documentation layout */ export function createReDocHandler(specUrl: string = '/openapi.json', title: string = 'API Documentation') { const html = ` ${escapeHtml(title)} `; return async (ctx: IRequestContext): Promise => { return new Response(html, { status: 200, headers: { 'Content-Type': 'text/html; charset=utf-8', 'Cache-Control': 'public, max-age=86400', }, }); }; } /** * Escape HTML special characters to prevent XSS */ function escapeHtml(str: string): string { return str .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"') .replace(/'/g, '''); }