1+ import { NextResponse } from 'next/server' ;
2+ // import { JsonSchemaStaticDocs } from 'json-schema-static-docs'; // Original import
3+ import * as JsonSchemaStaticDocsLib from 'json-schema-static-docs' ; // Use namespace import
4+ import fs from 'fs/promises' ;
5+ import path from 'path' ;
6+ import os from 'os' ;
7+ import { createRequire } from 'module' ; // Import createRequire
8+
9+ // Helper to resolve package paths
10+ const require = createRequire ( import . meta. url ) ;
11+
12+ // Recursive copy function (fs.cp might not be available everywhere or handle nested dirs reliably)
13+ async function copyDirRecursive ( src : string , dest : string ) {
14+ await fs . mkdir ( dest , { recursive : true } ) ;
15+ const entries = await fs . readdir ( src , { withFileTypes : true } ) ;
16+ for ( let entry of entries ) {
17+ const srcPath = path . join ( src , entry . name ) ;
18+ const destPath = path . join ( dest , entry . name ) ;
19+ if ( entry . isDirectory ( ) ) {
20+ await copyDirRecursive ( srcPath , destPath ) ;
21+ } else {
22+ await fs . copyFile ( srcPath , destPath ) ;
23+ }
24+ }
25+ }
26+
27+ // Helper to create a temporary file
28+ async function writeTempSchemaFile ( schema : any ) : Promise < string > {
29+ const tempDir = await fs . mkdtemp ( path . join ( os . tmpdir ( ) , 'schema-' ) ) ;
30+ const tempFilePath = path . join ( tempDir , 'schema.json' ) ;
31+ await fs . writeFile ( tempFilePath , JSON . stringify ( schema , null , 2 ) ) ;
32+ console . log ( `API: Wrote temp schema to ${ tempFilePath } ` ) ;
33+ return tempFilePath ;
34+ }
35+
36+ // Helper to read the generated markdown file
37+ async function readGeneratedMarkdown ( outputPath : string , schemaFileName : string ) : Promise < string > {
38+ const mdFileName = schemaFileName . replace ( / \. j s o n $ / , '.md' ) ; // Assuming input was .json
39+ const mdFilePath = path . join ( outputPath , mdFileName ) ;
40+ console . log ( `API: Reading generated markdown from ${ mdFilePath } ` ) ;
41+ try {
42+ const markdownContent = await fs . readFile ( mdFilePath , 'utf-8' ) ;
43+ return markdownContent ;
44+ } catch ( error ) {
45+ console . error ( `API: Error reading generated markdown file ${ mdFilePath } :` , error ) ;
46+ throw new Error ( `Could not read generated markdown file.` ) ;
47+ }
48+ }
49+
50+ // Helper to clean up temporary directories
51+ async function cleanupTempDirs ( pathsToClean : string [ ] ) {
52+ for ( const dirPath of pathsToClean ) {
53+ try {
54+ if ( dirPath ) { // Check if path is defined
55+ console . log ( `API: Cleaning up temp dir: ${ dirPath } ` ) ;
56+ await fs . rm ( dirPath , { recursive : true , force : true } ) ;
57+ }
58+ } catch ( error ) {
59+ console . error ( `API: Error during temp cleanup of ${ dirPath } :` , error ) ;
60+ // Don't throw error here, cleanup failure shouldn't fail the request
61+ }
62+ }
63+ }
64+
65+ export async function POST ( request : Request ) {
66+ let tempInputPath : string | undefined ;
67+ let tempOutputPath : string | undefined ;
68+ let tempTemplatePath : string | undefined ; // Path for copied templates
69+ const cleanupPaths : string [ ] = [ ] ; // Keep track of all paths to clean
70+
71+ try {
72+ const { schema } = await request . json ( ) ;
73+
74+ if ( ! schema ) {
75+ return NextResponse . json ( { error : 'Missing schema in request body' } , { status : 400 } ) ;
76+ }
77+
78+ // 1. Write the received schema to a temporary file
79+ tempInputPath = await writeTempSchemaFile ( schema ) ;
80+ const tempInputDirectory = path . dirname ( tempInputPath ) ;
81+ cleanupPaths . push ( tempInputDirectory ) ; // Add for cleanup
82+ console . log ( `API: Created temp input dir ${ tempInputDirectory } ` ) ;
83+
84+ // 2. Create a temporary output directory
85+ tempOutputPath = await fs . mkdtemp ( path . join ( os . tmpdir ( ) , 'schema-docs-' ) ) ;
86+ cleanupPaths . push ( tempOutputPath ) ; // Add for cleanup
87+ console . log ( `API: Created temp output dir ${ tempOutputPath } ` ) ;
88+
89+ // 3. Resolve source template path and create/copy to temp template path
90+ // Use the path to the copied templates in the public directory
91+ const projectRoot = process . cwd ( ) ;
92+ const sourceTemplatePath = path . join ( projectRoot , 'public' , '__schema_templates' ) ;
93+
94+ // Check if source template path exists before copying
95+ try {
96+ await fs . access ( sourceTemplatePath ) ;
97+ console . log ( `API: Source template path found: ${ sourceTemplatePath } ` ) ;
98+ } catch ( accessError ) {
99+ console . error ( `API: Source template path NOT FOUND: ${ sourceTemplatePath } ` , accessError ) ;
100+ throw new Error ( `Could not find source template directory at ${ sourceTemplatePath } . Check path resolution.` ) ;
101+ }
102+
103+ tempTemplatePath = await fs . mkdtemp ( path . join ( os . tmpdir ( ) , 'schema-templates-' ) ) ;
104+ cleanupPaths . push ( tempTemplatePath ) ; // Add for cleanup
105+ console . log ( `API: Copying templates from ${ sourceTemplatePath } to ${ tempTemplatePath } ` ) ;
106+ await copyDirRecursive ( sourceTemplatePath , tempTemplatePath ) ;
107+ console . log ( `API: Finished copying templates.` ) ;
108+
109+ // 4. Instantiate and run json-schema-static-docs
110+ const Constructor = JsonSchemaStaticDocsLib . default || JsonSchemaStaticDocsLib ;
111+
112+ const generator = new Constructor ( {
113+ inputPath : tempInputDirectory ,
114+ outputPath : tempOutputPath ,
115+ templatePath : tempTemplatePath , // <-- Use the path with copied templates
116+ createIndex : false , // We only want the single doc
117+ addFrontMatter : false , // No frontmatter needed
118+ // Add any other options needed for styling/generation
119+ // ajvOptions: { allowUnionTypes: true }, // Example ajv option
120+ // enableMetaEnum: true, // Example meta enum option
121+ } ) ;
122+
123+ console . log ( `API: Running generator. Input: ${ tempInputDirectory } , Output: ${ tempOutputPath } , Templates: ${ tempTemplatePath } ` ) ;
124+ await generator . generate ( ) ;
125+ console . log ( "API: Generator finished." ) ;
126+
127+ // 5. Read the generated Markdown file
128+ const markdown = await readGeneratedMarkdown ( tempOutputPath , path . basename ( tempInputPath ) ) ;
129+
130+ // 6. Clean up temporary files/directories (do this *before* returning)
131+ await cleanupTempDirs ( cleanupPaths ) ;
132+
133+ // 7. Return the Markdown content
134+ return NextResponse . json ( { markdown } ) ;
135+
136+ } catch ( error : any ) {
137+ console . error ( "API Error generating schema doc:" , error ) ;
138+
139+ // Ensure cleanup happens even on error
140+ await cleanupTempDirs ( cleanupPaths ) ;
141+
142+ return NextResponse . json (
143+ { error : 'Failed to generate schema documentation' , details : error . message } ,
144+ { status : 500 }
145+ ) ;
146+ }
147+ }
0 commit comments