Files
docker-vitepress/docker/api/server.js
2026-05-19 22:27:43 +08:00

221 lines
6.6 KiB
JavaScript

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(`========================================`);
});