/** * API: OpenAPI Spec * GET — returns the parsed openapi.yaml as structured JSON catalog */ import { NextResponse } from "next/server"; import fs from "fs"; import path from "path"; import yaml from "js-yaml"; let cachedSpec: { data: any; mtime: number } | null = null; export async function GET() { try { // Try multiple locations for the spec file const candidates = [ path.join(process.cwd(), "docs", "openapi.yaml"), path.join(process.cwd(), "app", "docs", "openapi.yaml"), ]; let specPath = ""; for (const p of candidates) { if (fs.existsSync(p)) { specPath = p; break; } } if (!specPath) { return NextResponse.json({ error: "openapi.yaml not found" }, { status: 404 }); } const stat = fs.statSync(specPath); const mtime = stat.mtimeMs; // Use cache if file hasn't changed if (cachedSpec && cachedSpec.mtime === mtime) { return NextResponse.json(cachedSpec.data); } const content = fs.readFileSync(specPath, "utf-8"); const raw: any = yaml.load(content); // Build a structured catalog const catalog: any = { info: raw.info || {}, servers: raw.servers || [], tags: Array.isArray(raw.tags) ? raw.tags : [], endpoints: [] as any[], schemas: Object.keys(raw.components?.schemas || {}), }; // Parse paths into flat endpoint list const paths = raw.paths || {}; for (const [pathStr, methods] of Object.entries(paths as Record)) { if (!methods || typeof methods !== "object") continue; for (const [method, spec] of Object.entries(methods as Record)) { if (["get", "post", "put", "patch", "delete"].includes(method) && spec) { catalog.endpoints.push({ method: method.toUpperCase(), path: pathStr, tags: Array.isArray(spec.tags) ? spec.tags : [], summary: spec.summary || "", description: spec.description || "", security: spec.security ? true : false, parameters: spec.parameters || [], requestBody: spec.requestBody ? true : false, responses: Object.keys(spec.responses || {}), }); } } } cachedSpec = { data: catalog, mtime }; return NextResponse.json(catalog); } catch (error: any) { return NextResponse.json( { error: error.message || "Failed to parse OpenAPI spec" }, { status: 500 } ); } }