Initial commit
This commit is contained in:
420
docker/API.md
Normal file
420
docker/API.md
Normal file
@@ -0,0 +1,420 @@
|
||||
# VitePress API Documentation
|
||||
|
||||
## 概述
|
||||
|
||||
VitePress Docker 服务提供了一套完整的 RESTful API,用于管理文档系统和 PDF 导出功能。
|
||||
|
||||
## 服务地址
|
||||
|
||||
- **API 服务**: `http://localhost:3001`
|
||||
- **VitePress 文档**: `http://localhost:3000`
|
||||
|
||||
## API 端点概览
|
||||
|
||||
| 方法 | 端点 | 描述 |
|
||||
|------|------|------|
|
||||
| GET | `/health` | 健康检查 |
|
||||
| GET | `/export-pdf` | 导出 PDF |
|
||||
| GET | `/pdf-files` | 列出所有 PDF 文件 |
|
||||
|
||||
---
|
||||
|
||||
## API 端点详情
|
||||
|
||||
### 1. 健康检查
|
||||
|
||||
**端点**: `GET /health`
|
||||
|
||||
检查 API 服务和 VitePress 服务是否正常运行。
|
||||
|
||||
**响应示例**:
|
||||
```json
|
||||
{
|
||||
"status": "ok",
|
||||
"timestamp": "2026-04-11T12:00:00.000Z",
|
||||
"services": {
|
||||
"api": "running",
|
||||
"vitepress": "running"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**状态码**:
|
||||
- `200 OK`: 服务正常运行
|
||||
|
||||
**curl 示例**:
|
||||
```bash
|
||||
curl http://localhost:3001/health
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2. 导出 PDF
|
||||
|
||||
**端点**: `GET /export-pdf`
|
||||
|
||||
启动 PDF 导出流程,将当前 VitePress 文档导出为 PDF 文件。
|
||||
|
||||
**请求参数**:
|
||||
|
||||
| 参数 | 类型 | 必需 | 默认值 | 说明 |
|
||||
|------|------|------|--------|------|
|
||||
| `fileName` | string | 否 | `export-{timestamp}.pdf` | 导出文件名 |
|
||||
|
||||
**示例请求**:
|
||||
|
||||
```bash
|
||||
# 使用默认文件名
|
||||
curl "http://localhost:3001/export-pdf"
|
||||
|
||||
# 指定文件名
|
||||
curl "http://localhost:3001/export-pdf?fileName=HF-MES-Manual.pdf"
|
||||
```
|
||||
|
||||
**成功响应**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "PDF exported successfully",
|
||||
"pdfPath": "/app/docs/HF-MES-Manual.pdf",
|
||||
"pdfUrl": "HF-MES-Manual.pdf",
|
||||
"fileName": "HF-MES-Manual.pdf",
|
||||
"fileSize": 1234567,
|
||||
"fileSizeMB": "1.18"
|
||||
}
|
||||
```
|
||||
|
||||
**错误响应**:
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"error": "PDF file was not created",
|
||||
"stack": "Error: PDF file was not created..."
|
||||
}
|
||||
```
|
||||
|
||||
**状态码**:
|
||||
- `200 OK`: PDF 导出成功
|
||||
- `500 Internal Server Error`: 导出失败
|
||||
|
||||
**工作流程**:
|
||||
|
||||
1. 清理旧的 `.vitepress/dist` 目录
|
||||
2. 检查 VitePress 开发服务器是否运行
|
||||
3. 执行 `npm run export-pdf` 命令
|
||||
4. 查找生成的 PDF 文件
|
||||
5. 返回 PDF 文件信息
|
||||
|
||||
**注意事项**:
|
||||
|
||||
- PDF 文件会保存到配置的 `DOCS_DIR` 目录下
|
||||
- 如果 VitePress 服务未运行,导出会失败
|
||||
- 首次导出可能需要较长时间
|
||||
|
||||
---
|
||||
|
||||
### 3. 列出 PDF 文件
|
||||
|
||||
**端点**: `GET /pdf-files`
|
||||
|
||||
列出 `DOCS_DIR` 目录下所有已导出的 PDF 文件。
|
||||
|
||||
**响应示例**:
|
||||
```json
|
||||
{
|
||||
"count": 2,
|
||||
"files": [
|
||||
{
|
||||
"name": "HF-MES-Manual.pdf",
|
||||
"size": 1234567,
|
||||
"sizeMB": "1.18",
|
||||
"created": "2026-04-11T12:00:00.000Z",
|
||||
"modified": "2026-04-11T12:00:00.000Z"
|
||||
},
|
||||
{
|
||||
"name": "export-123456.pdf",
|
||||
"size": 2345678,
|
||||
"sizeMB": "2.24",
|
||||
"created": "2026-04-11T11:00:00.000Z",
|
||||
"modified": "2026-04-11T11:00:00.000Z"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**文件信息字段**:
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `name` | string | 文件名 |
|
||||
| `size` | number | 文件大小(字节) |
|
||||
| `sizeMB` | string | 文件大小(MB,保留两位小数) |
|
||||
| `created` | string | 文件创建时间(ISO 8601) |
|
||||
| `modified` | string | 文件最后修改时间(ISO 8601) |
|
||||
|
||||
**状态码**:
|
||||
- `200 OK`: 成功获取文件列表
|
||||
- `500 Internal Server Error`: 读取目录失败
|
||||
|
||||
**curl 示例**:
|
||||
```bash
|
||||
curl http://localhost:3001/pdf-files
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 使用示例
|
||||
|
||||
### 完整的 PDF 导出流程
|
||||
|
||||
```bash
|
||||
# 1. 检查服务状态
|
||||
curl http://localhost:3001/health
|
||||
|
||||
# 2. 导出 PDF(指定文件名)
|
||||
curl "http://localhost:3001/export-pdf?fileName=My-Document.pdf"
|
||||
|
||||
# 3. 查看导出的文件
|
||||
curl http://localhost:3001/pdf-files
|
||||
|
||||
# 4. 在浏览器中访问
|
||||
# http://localhost:3000/My-Document.pdf
|
||||
```
|
||||
|
||||
### 使用 JavaScript 调用
|
||||
|
||||
```javascript
|
||||
// 检查服务状态
|
||||
async function checkHealth() {
|
||||
const response = await fetch('http://localhost:3001/health');
|
||||
const data = await response.json();
|
||||
console.log('Service Status:', data);
|
||||
}
|
||||
|
||||
// 导出 PDF
|
||||
async function exportPDF(fileName = null) {
|
||||
const url = fileName
|
||||
? `http://localhost:3001/export-pdf?fileName=${fileName}`
|
||||
: 'http://localhost:3001/export-pdf';
|
||||
|
||||
const response = await fetch(url);
|
||||
const data = await response.json();
|
||||
|
||||
if (data.success) {
|
||||
console.log('PDF exported:', data.fileName);
|
||||
console.log('File size:', data.fileSizeMB, 'MB');
|
||||
} else {
|
||||
console.error('Export failed:', data.error);
|
||||
}
|
||||
}
|
||||
|
||||
// 获取 PDF 列表
|
||||
async function listPDFs() {
|
||||
const response = await fetch('http://localhost:3001/pdf-files');
|
||||
const data = await response.json();
|
||||
|
||||
console.log(`Found ${data.count} PDF files:`);
|
||||
data.files.forEach(file => {
|
||||
console.log(` - ${file.name} (${file.sizeMB} MB)`);
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
### 使用 Python 调用
|
||||
|
||||
```python
|
||||
import requests
|
||||
|
||||
# 检查服务状态
|
||||
def check_health():
|
||||
response = requests.get('http://localhost:3001/health')
|
||||
print(response.json())
|
||||
|
||||
# 导出 PDF
|
||||
def export_pdf(file_name=None):
|
||||
url = 'http://localhost:3001/export-pdf'
|
||||
if file_name:
|
||||
url += f'?fileName={file_name}'
|
||||
|
||||
response = requests.get(url)
|
||||
data = response.json()
|
||||
|
||||
if data['success']:
|
||||
print(f"PDF exported: {data['fileName']}")
|
||||
print(f"File size: {data['fileSizeMB']} MB")
|
||||
else:
|
||||
print(f"Export failed: {data['error']}")
|
||||
|
||||
# 列出 PDF 文件
|
||||
def list_pdfs():
|
||||
response = requests.get('http://localhost:3001/pdf-files')
|
||||
data = response.json()
|
||||
|
||||
print(f"Found {data['count']} PDF files:")
|
||||
for file in data['files']:
|
||||
print(f" - {file['name']} ({file['sizeMB']} MB)")
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 错误处理
|
||||
|
||||
### 常见错误
|
||||
|
||||
| 错误信息 | 可能原因 | 解决方案 |
|
||||
|----------|----------|----------|
|
||||
| `PDF file was not created` | VitePress 服务未运行 | 确认 http://localhost:3000 可访问 |
|
||||
| `Export command failed` | PDF 导出命令执行失败 | 检查容器日志 |
|
||||
| `ENOENT: no such file or directory` | 文档目录不存在 | 确认 DOCS_DIR 配置正确 |
|
||||
|
||||
### 错误响应格式
|
||||
|
||||
所有错误响应都遵循以下格式:
|
||||
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"error": "错误描述信息",
|
||||
"stack": "错误堆栈信息(仅在开发环境显示)"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 配置说明
|
||||
|
||||
### 环境变量
|
||||
|
||||
| 变量名 | 默认值 | 说明 |
|
||||
|--------|--------|------|
|
||||
| `API_PORT` | 3001 | API 服务端口 |
|
||||
| `DOCS_PATH` | /app/docs | 容器内文档目录路径 |
|
||||
| `VITEPRESS_PORT` | 3000 | VitePress 服务端口 |
|
||||
| `NODE_ENV` | production | Node.js 运行环境 |
|
||||
|
||||
### Docker Compose 配置
|
||||
|
||||
```yaml
|
||||
services:
|
||||
vitepress:
|
||||
environment:
|
||||
- API_PORT=3001
|
||||
- DOCS_PATH=/app/docs
|
||||
- VITEPRESS_PORT=3000
|
||||
- NODE_ENV=production
|
||||
ports:
|
||||
- "3001:3001"
|
||||
volumes:
|
||||
- ${DOCS_DIR:-./docs}:/app/docs
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **PDF 文件位置**: 导出的 PDF 文件保存在配置的 `DOCS_DIR` 目录下,可直接在浏览器中通过 VitePress 服务访问
|
||||
|
||||
2. **VitePress 服务依赖**: PDF 导出功能依赖 VitePress 开发服务器运行,请确保 http://localhost:3000 可访问
|
||||
|
||||
3. **导出超时**: PDF 导出命令超时时间设置为 3 分钟,大型文档可能需要更长的时间
|
||||
|
||||
4. **并发限制**: 不建议同时发起多个 PDF 导出请求
|
||||
|
||||
5. **CORS 支持**: API 服务已启用 CORS,允许来自任何源的请求
|
||||
|
||||
6. **文件覆盖**: 如果指定文件名已存在,导出时会被覆盖
|
||||
|
||||
---
|
||||
|
||||
## 技术实现
|
||||
|
||||
### API 服务架构
|
||||
|
||||
```
|
||||
Express.js Server (端口 3001)
|
||||
├── /health → 直接返回状态
|
||||
├── /export-pdf → 执行 export-pdf 脚本
|
||||
└── /pdf-files → 读取 DOCS_DIR 目录
|
||||
```
|
||||
|
||||
### PDF 导出流程
|
||||
|
||||
```
|
||||
1. Client Request
|
||||
↓
|
||||
2. Clean .vitepress/dist
|
||||
↓
|
||||
3. Check VitePress Server
|
||||
↓
|
||||
4. Execute npm run export-pdf
|
||||
↓
|
||||
5. Find Generated PDF
|
||||
↓
|
||||
6. Return Response
|
||||
```
|
||||
|
||||
### 依赖项
|
||||
|
||||
- **express**: Web 框架
|
||||
- **cors**: 跨域资源共享
|
||||
- **fs-extra**: 文件系统操作
|
||||
- **vitepress-export-pdf**: PDF 导出插件
|
||||
|
||||
---
|
||||
|
||||
## 故障排除
|
||||
|
||||
### 服务无法访问
|
||||
|
||||
1. 检查容器状态:
|
||||
```bash
|
||||
docker ps | grep vitepress
|
||||
```
|
||||
|
||||
2. 检查端口占用:
|
||||
```bash
|
||||
netstat -ano | findstr 3001
|
||||
```
|
||||
|
||||
3. 查看日志:
|
||||
```bash
|
||||
docker-compose logs -f vitepress
|
||||
```
|
||||
|
||||
### PDF 导出失败
|
||||
|
||||
1. 确认 VitePress 服务运行:
|
||||
```bash
|
||||
curl http://localhost:3000
|
||||
```
|
||||
|
||||
2. 检查容器日志:
|
||||
```bash
|
||||
docker-compose logs -f
|
||||
```
|
||||
|
||||
3. 验证文档目录挂载:
|
||||
```bash
|
||||
docker exec vitepress-docker ls -la /app/docs
|
||||
```
|
||||
|
||||
4. 检查权限:
|
||||
```bash
|
||||
docker exec vitepress-docker whoami
|
||||
```
|
||||
|
||||
### 文件列表为空
|
||||
|
||||
1. 确认 PDF 文件存在:
|
||||
```bash
|
||||
ls -la ./docs/*.pdf
|
||||
```
|
||||
|
||||
2. 检查 DOCS_DIR 配置:
|
||||
```bash
|
||||
cat .env | grep DOCS_DIR
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
如有问题,请查看容器日志或联系技术支持。
|
||||
97
docker/Dockerfile
Normal file
97
docker/Dockerfile
Normal file
@@ -0,0 +1,97 @@
|
||||
# VitePress Docker Image
|
||||
FROM node:25
|
||||
|
||||
LABEL maintainer="xeden3"
|
||||
LABEL description="VitePress Documentation with PDF Export Support"
|
||||
|
||||
# Install Chrome dependencies for PDF export (Debian)
|
||||
RUN apt-get update && apt-get install -y \
|
||||
chromium \
|
||||
chromium-driver \
|
||||
tzdata \
|
||||
fonts-liberation \
|
||||
libnss3 \
|
||||
libatk-bridge2.0-0 \
|
||||
libxcomposite1 \
|
||||
libxdamage1 \
|
||||
libxrandr2 \
|
||||
libgbm1 \
|
||||
libasound2 \
|
||||
libpangocairo-1.0-0 \
|
||||
libgtk-3-0 \
|
||||
libxss1 \
|
||||
libglib2.0-0 \
|
||||
libxkbcommon0 \
|
||||
libdrm2 \
|
||||
libcups2 \
|
||||
libdbus-1-3 \
|
||||
# 中文字体支持(开源字体,Docker环境默认使用)
|
||||
fonts-wqy-microhei \
|
||||
fonts-wqy-zenhei \
|
||||
xfonts-intl-chinese \
|
||||
fonts-noto-cjk \
|
||||
# 俄语字体支持(DejaVu 字体包含 Cyrillic 字符)
|
||||
fonts-dejavu-core \
|
||||
# 字体缓存工具
|
||||
fontconfig \
|
||||
&& fc-cache -fv \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# 提示:如果需要使用微软雅黑字体,可以从 Windows 系统复制字体文件到容器
|
||||
# Windows 字体目录:C:\Windows\Fonts\msyh.ttc (微软雅黑)
|
||||
# 将字体复制到容器:docker cp msyh.ttc <container_name>:/usr/share/fonts/truetype/
|
||||
# 然后运行:fc-cache -fv
|
||||
|
||||
# Set environment variables
|
||||
ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true \
|
||||
PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium \
|
||||
NODE_ENV=development \
|
||||
CHROME_BIN=/usr/bin/chromium
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Copy package files for main app
|
||||
COPY package*.json ./
|
||||
|
||||
# Install main dependencies (vitepress, etc.)
|
||||
RUN export NODE_ENV=development && \
|
||||
rm -rf node_modules package-lock.json && \
|
||||
npm install && \
|
||||
npm install vitepress-export-pdf -D && \
|
||||
npm install vitepress-plugin-mermaid mermaid && \
|
||||
npm install medium-zoom markdown-it-mathjax3
|
||||
|
||||
# Copy API service
|
||||
COPY docker/api/package*.json ./api/
|
||||
WORKDIR /app/api
|
||||
|
||||
# Install API dependencies
|
||||
RUN npm install
|
||||
|
||||
# Go back to app directory
|
||||
WORKDIR /app
|
||||
|
||||
# Copy API service files
|
||||
COPY docker/api/ ./api/
|
||||
|
||||
# Copy entry point script
|
||||
COPY docker/docker-entrypoint.sh /docker-entrypoint.sh
|
||||
RUN chmod +x /docker-entrypoint.sh
|
||||
|
||||
# Create log directory
|
||||
RUN mkdir -p /var/log
|
||||
|
||||
# Create working directory for docs
|
||||
WORKDIR /app
|
||||
|
||||
# Expose ports
|
||||
EXPOSE ${VITEPRESS_PORT:-3000} ${API_PORT:-3001}
|
||||
|
||||
# Volume for docs and PDF output
|
||||
VOLUME ["/app/docs"]
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Entry point
|
||||
ENTRYPOINT ["/docker-entrypoint.sh"]
|
||||
CMD []
|
||||
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(`========================================`);
|
||||
});
|
||||
84
docker/docker-entrypoint.sh
Normal file
84
docker/docker-entrypoint.sh
Normal file
@@ -0,0 +1,84 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
echo "========================================"
|
||||
echo "VitePress Container"
|
||||
echo "========================================"
|
||||
|
||||
# Set default values
|
||||
export DOCS_PATH=${DOCS_PATH:-/app/docs}
|
||||
export VITEPRESS_PORT=${VITEPRESS_PORT:-3000}
|
||||
export API_PORT=${API_PORT:-3001}
|
||||
|
||||
echo "Configuration:"
|
||||
echo " DOCS_PATH: $DOCS_PATH"
|
||||
echo " VITEPRESS_PORT: $VITEPRESS_PORT"
|
||||
echo " API_PORT: $API_PORT"
|
||||
echo "========================================"
|
||||
|
||||
# Check if docs directory exists
|
||||
if [ ! -d "$DOCS_PATH" ]; then
|
||||
echo "Creating docs directory: $DOCS_PATH"
|
||||
mkdir -p "$DOCS_PATH"
|
||||
fi
|
||||
|
||||
# Check if package.json exists
|
||||
if [ ! -f "/app/package.json" ]; then
|
||||
echo "Error: /app/package.json not found!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Install API dependencies if needed
|
||||
if [ ! -f "/app/api/node_modules/.package-lock.json" ]; then
|
||||
echo "Installing API dependencies..."
|
||||
cd /app/api && npm install
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "========================================"
|
||||
echo "Starting VitePress Documentation Server..."
|
||||
echo "========================================"
|
||||
|
||||
# Start VitePress dev server in background (运行在 docs 目录下)
|
||||
echo "[1/2] Starting VitePress dev server on port $VITEPRESS_PORT..."
|
||||
cd /app
|
||||
export NODE_ENV=development
|
||||
nohup npm run docs:dev -- --port $VITEPRESS_PORT --host > /var/log/vitepress.log 2>&1 &
|
||||
VITEPRESS_PID=$!
|
||||
echo "VitePress started with PID: $VITEPRESS_PID"
|
||||
# export NODE_ENV=development
|
||||
# npm run docs:dev -- --port $VITEPRESS_PORT --host
|
||||
|
||||
|
||||
# Wait for VitePress to start
|
||||
echo "Waiting for VitePress to start..."
|
||||
sleep 10
|
||||
|
||||
# Check if VitePress is running
|
||||
if ! kill -0 $VITEPRESS_PID 2>/dev/null; then
|
||||
echo "Warning: VitePress server may not have started properly"
|
||||
echo "Check logs: cat /var/log/vitepress.log"
|
||||
fi
|
||||
|
||||
echo "[2/2] Starting API server on port $API_PORT..."
|
||||
echo ""
|
||||
|
||||
echo "========================================"
|
||||
echo "All services started successfully!"
|
||||
echo "========================================"
|
||||
echo ""
|
||||
echo "Services:"
|
||||
echo " - VitePress Documentation: http://localhost:$VITEPRESS_PORT"
|
||||
echo " - API Server: http://localhost:$API_PORT"
|
||||
echo ""
|
||||
echo "API Endpoints:"
|
||||
echo " GET /health - Health check"
|
||||
echo " GET /export-pdf - Export PDF"
|
||||
echo "Logs:"
|
||||
echo " VitePress: cat /var/log/vitepress.log"
|
||||
echo " API Server: docker logs vitepress-docker"
|
||||
echo ""
|
||||
echo "========================================"
|
||||
|
||||
# Execute the API server with absolute path
|
||||
exec node /app/api/server.js
|
||||
Reference in New Issue
Block a user