Initial commit
This commit is contained in:
16
docker/api/package.json
Normal file
16
docker/api/package.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"name": "vitepress-pdf-api",
|
||||
"version": "1.0.0",
|
||||
"description": "API service for VitePress PDF export",
|
||||
"main": "server.js",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"start": "node server.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"express": "^4.18.2",
|
||||
"cors": "^2.8.5",
|
||||
"child_process": "^1.0.2",
|
||||
"fs-extra": "^11.1.1"
|
||||
}
|
||||
}
|
||||
220
docker/api/server.js
Normal file
220
docker/api/server.js
Normal file
@@ -0,0 +1,220 @@
|
||||
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(`========================================`);
|
||||
});
|
||||
Reference in New Issue
Block a user