import express from 'express'; import cors from 'cors'; import { exec } from 'child_process'; import { promisify } from 'util'; import fs from 'fs-extra'; import path from 'path'; import { fileURLToPath } from 'url'; const execAsync = promisify(exec); const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const app = express(); const PORT = process.env.API_PORT || 3001; // Enable CORS for all origins app.use(cors({ origin: '*', methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'], allowedHeaders: ['Content-Type', 'Authorization'] })); app.use(express.json()); // Logging middleware app.use((req, res, next) => { console.log(`[${new Date().toISOString()}] ${req.method} ${req.url}`); next(); }); // Health check endpoint app.get('/health', (req, res) => { res.json({ status: 'ok', timestamp: new Date().toISOString(), services: { api: 'running', vitepress: 'running' } }); }); // Export PDF endpoint app.get('/export-pdf', async (req, res) => { const docsPath = process.env.DOCS_PATH || '/app/docs'; const pdfFileName = req.query.fileName || `export-${Date.now()}.pdf`; const vitepressPort = process.env.VITEPRESS_PORT || 3000; console.log(`[${new Date().toISOString()}] Starting PDF export...`); console.log(`Docs path: ${docsPath}`); console.log(`PDF output: ${path.join(docsPath, pdfFileName)}`); console.log(`VitePress port: ${vitepressPort}`); try { // Ensure PDF directory exists await fs.ensureDir(docsPath); // Clean old dist directory const distDir = path.join(docsPath, '.vitepress', 'dist'); console.log('Cleaning old dist directory...'); await fs.remove(distDir).catch(() => {}); // Build the VitePress site /* console.log('Building VitePress site...'); try { await execAsync('npm run docs:build', { cwd: '/app', stdio: 'inherit', timeout: 120000 // 2 minutes timeout }); } catch (buildError) { console.error('Build warning/error:', buildError.message); // Continue anyway, sometimes there are non-fatal warnings } */ // Run PDF export (VitePress preview is already running) console.log('Starting PDF export using running VitePress server...'); const exportEnv = { ...process.env, NODE_ENV: 'development' }; try { /* console.log('Installing vitepress-export-pdf...'); await execAsync('npm install vitepress-export-pdf -D', { cwd: '/app', stdio: 'inherit', env: exportEnv, timeout: 120000 }); */ console.log('Running export-pdf...'); await execAsync('npm run export-pdf', { cwd: '/app', stdio: 'inherit', env: exportEnv, timeout: 180000, maxBuffer: 1024 * 1024 * 100 }); } catch (exportError) { console.error('Export command failed:', exportError.message); throw new Error(`PDF export failed: ${exportError.message}`); } // Verify PDF was created const pdfPath = path.join(docsPath, pdfFileName); const pdfExists = await fs.pathExists(pdfPath); if (!pdfExists) { // Try to find any PDF file in the directory const files = await fs.readdir(docsPath); const pdfFiles = files.filter(f => f.endsWith('.pdf')); if (pdfFiles.length > 0) { console.log(`PDF found with different name: ${pdfFiles[0]}`); const stats = await fs.stat(path.join(docsPath, pdfFiles[0])); console.log(`[${new Date().toISOString()}] PDF export completed successfully!`); console.log(`PDF size: ${(stats.size / 1024 / 1024).toFixed(2)} MB`); return res.json({ success: true, message: 'PDF exported successfully', pdfPath: path.join(docsPath, pdfFiles[0]), pdfUrl: `${pdfFiles[0]}`, fileName: pdfFiles[0], fileSize: stats.size, fileSizeMB: (stats.size / 1024 / 1024).toFixed(2) }); } else { throw new Error('PDF file was not created'); } } const stats = await fs.stat(pdfPath); console.log(`[${new Date().toISOString()}] PDF export completed successfully!`); console.log(`PDF path: ${pdfPath}`); console.log(`PDF size: ${(stats.size / 1024 / 1024).toFixed(2)} MB`); res.json({ success: true, message: 'PDF exported successfully', pdfPath: pdfPath, pdfUrl: `${pdfFileName}`, fileName: pdfFileName, fileSize: stats.size, fileSizeMB: (stats.size / 1024 / 1024).toFixed(2) }); } catch (error) { console.error(`[${new Date().toISOString()}] PDF export failed:`, error); res.status(500).json({ success: false, error: error.message, stack: process.env.NODE_ENV === 'development' ? error.stack : undefined }); } }); // List all PDF files app.get('/pdf-files', async (req, res) => { const docsPath = process.env.DOCS_PATH || '/app/docs'; try { await fs.ensureDir(docsPath); const files = await fs.readdir(docsPath); const pdfFiles = files.filter(f => f.endsWith('.pdf')); const fileDetails = await Promise.all( pdfFiles.map(async (file) => { const stats = await fs.stat(path.join(docsPath, file)); return { name: file, size: stats.size, sizeMB: (stats.size / 1024 / 1024).toFixed(2), created: stats.birthtime, modified: stats.mtime }; }) ); res.json({ count: pdfFiles.length, files: fileDetails }); } catch (error) { res.status(500).json({ error: error.message }); } }); // Error handling middleware app.use((err, req, res, next) => { console.error(`[${new Date().toISOString()}] Error:`, err); res.status(500).json({ error: err.message || 'Internal server error', stack: process.env.NODE_ENV === 'development' ? err.stack : undefined }); }); app.listen(PORT, '0.0.0.0', () => { console.log(`========================================`); console.log(`VitePress API Server`); console.log(`========================================`); console.log(`API Server running on port: ${PORT}`); console.log(`VitePress Server: http://localhost:${process.env.VITEPRESS_PORT || 3000}`); console.log(`PDF output directory: ${process.env.DOCS_PATH || '/app/docs'}`); console.log(`========================================`); console.log(`Available API endpoints:`); console.log(` GET /health - Health check`); console.log(` GET /export-pdf - Export PDF`); console.log(` GET /pdf-files - List all PDF files`); console.log(`========================================`); });