fix: resolve legal/about pages in production, update deploy docs
- Fix markdown file path resolution for SSR production builds - Try multiple paths (public/legal, client/legal) for dev and prod - Update deploy/README.md with frontend deployment instructions - Document PM2, Nginx configuration, and port mapping
This commit is contained in:
@@ -14,6 +14,8 @@
|
|||||||
- Docker
|
- Docker
|
||||||
- Docker Compose v2
|
- Docker Compose v2
|
||||||
- AWS CLI v2
|
- AWS CLI v2
|
||||||
|
- Node.js 22+ (前端 SSR 服务)
|
||||||
|
- PM2 (前端进程管理)
|
||||||
|
|
||||||
确认命令:
|
确认命令:
|
||||||
|
|
||||||
@@ -21,6 +23,31 @@
|
|||||||
docker --version
|
docker --version
|
||||||
docker compose version
|
docker compose version
|
||||||
aws --version
|
aws --version
|
||||||
|
node --version # 需要 >= 22.12.0
|
||||||
|
pm2 --version
|
||||||
|
```
|
||||||
|
|
||||||
|
## 服务架构
|
||||||
|
|
||||||
|
生产环境运行以下服务:
|
||||||
|
|
||||||
|
| 服务 | 端口 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| Nginx | 80, 443 | 反向代理,SSL 终结 |
|
||||||
|
| 前端 SSR | 4322 | Node.js + Astro SSR |
|
||||||
|
| 后端 API | 5775 | Docker 容器 |
|
||||||
|
| Redis | 6379 | Docker 容器 |
|
||||||
|
| Worker Agent | - | Docker 容器 |
|
||||||
|
|
||||||
|
### 端口映射
|
||||||
|
|
||||||
|
```
|
||||||
|
Internet → Nginx (443)
|
||||||
|
├── /_astro/* → 静态文件 (client 目录)
|
||||||
|
├── /images/* → 静态文件 (client 目录)
|
||||||
|
└── /* → 反向代理到 localhost:4322 (前端 SSR)
|
||||||
|
|
||||||
|
前端 SSR (4322) → /api/* → 反向代理到 localhost:5775 (后端 API)
|
||||||
```
|
```
|
||||||
|
|
||||||
## 环境变量
|
## 环境变量
|
||||||
@@ -199,3 +226,98 @@ docker compose --env-file ./.env -f docker-compose.prod.yml --profile workers do
|
|||||||
```
|
```
|
||||||
|
|
||||||
谨慎使用 `down -v`,它会删除 Redis 持久化数据。
|
谨慎使用 `down -v`,它会删除 Redis 持久化数据。
|
||||||
|
|
||||||
|
## 前端部署
|
||||||
|
|
||||||
|
### 构建前端
|
||||||
|
|
||||||
|
在本地开发机器上:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd web
|
||||||
|
PUBLIC_API_URL=https://api.meeyao.com npm run build
|
||||||
|
```
|
||||||
|
|
||||||
|
构建产物在 `web/dist/` 目录:
|
||||||
|
- `dist/client/` - 静态资源 (CSS, JS, 图片)
|
||||||
|
- `dist/server/` - SSR 服务端代码
|
||||||
|
|
||||||
|
### 上传到服务器
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 上传构建产物
|
||||||
|
rsync -avz -e "ssh -i xunmee.pem" web/dist/ ubuntu@18.218.38.213:/home/ubuntu/deploy/web/
|
||||||
|
|
||||||
|
# 上传依赖配置
|
||||||
|
rsync -avz -e "ssh -i xunmee.pem" web/package.json web/package-lock.json ubuntu@18.218.38.213:/home/ubuntu/deploy/web/
|
||||||
|
|
||||||
|
# 安装生产依赖
|
||||||
|
ssh -i xunmee.pem ubuntu@18.218.38.213 "cd /home/ubuntu/deploy/web && npm install --omit=dev"
|
||||||
|
```
|
||||||
|
|
||||||
|
### PM2 管理
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 启动服务
|
||||||
|
pm2 start server/entry.mjs --name meeyao-web
|
||||||
|
|
||||||
|
# 设置端口 (默认 4321,需要改为 4322)
|
||||||
|
export HOST=0.0.0.0
|
||||||
|
export PORT=4322
|
||||||
|
pm2 restart meeyao-web
|
||||||
|
|
||||||
|
# 保存进程列表 (开机自启)
|
||||||
|
pm2 save
|
||||||
|
|
||||||
|
# 查看状态
|
||||||
|
pm2 status
|
||||||
|
|
||||||
|
# 查看日志
|
||||||
|
pm2 logs meeyao-web
|
||||||
|
```
|
||||||
|
|
||||||
|
### Nginx 配置
|
||||||
|
|
||||||
|
前端 SSR 服务监听 `localhost:4322`,Nginx 配置要点:
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
server {
|
||||||
|
listen 443 ssl http2;
|
||||||
|
server_name meeyao.com;
|
||||||
|
|
||||||
|
# 静态资源直接从 client 目录提供
|
||||||
|
location /_astro {
|
||||||
|
root /home/ubuntu/deploy/web/client;
|
||||||
|
expires 30d;
|
||||||
|
add_header Cache-Control "public, immutable";
|
||||||
|
}
|
||||||
|
|
||||||
|
location /images {
|
||||||
|
root /home/ubuntu/deploy/web/client;
|
||||||
|
expires 30d;
|
||||||
|
}
|
||||||
|
|
||||||
|
# 其他请求代理到 Node.js SSR
|
||||||
|
location / {
|
||||||
|
proxy_pass http://127.0.0.1:4322;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 前端更新流程
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. 本地构建
|
||||||
|
cd web && PUBLIC_API_URL=https://api.meeyao.com npm run build
|
||||||
|
|
||||||
|
# 2. 上传
|
||||||
|
rsync -avz -e "ssh -i xunmee.pem" dist/ ubuntu@18.218.38.213:/home/ubuntu/deploy/web/
|
||||||
|
|
||||||
|
# 3. 重启服务
|
||||||
|
ssh -i xunmee.pem ubuntu@18.218.38.213 "pm2 restart meeyao-web"
|
||||||
|
```
|
||||||
|
|||||||
@@ -17,12 +17,23 @@ const titleMap: Record<Locale, string> = {
|
|||||||
en: 'About Us',
|
en: 'About Us',
|
||||||
};
|
};
|
||||||
|
|
||||||
const filePath = path.resolve('public/legal', locale, 'about_us.md');
|
// Try multiple paths for dev and production environments
|
||||||
|
const possiblePaths = [
|
||||||
|
path.resolve('public/legal', locale, 'about_us.md'),
|
||||||
|
path.resolve('client/legal', locale, 'about_us.md'),
|
||||||
|
path.resolve('../client/legal', locale, 'about_us.md'),
|
||||||
|
];
|
||||||
let raw = '';
|
let raw = '';
|
||||||
try {
|
for (const filePath of possiblePaths) {
|
||||||
|
try {
|
||||||
raw = fs.readFileSync(filePath, 'utf-8');
|
raw = fs.readFileSync(filePath, 'utf-8');
|
||||||
raw = raw.replace(/^#\s+.+\n*/m, '');
|
raw = raw.replace(/^#\s+.+\n*/m, '');
|
||||||
} catch {
|
break;
|
||||||
|
} catch {
|
||||||
|
// Try next path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!raw) {
|
||||||
raw = `Content not available.`;
|
raw = `Content not available.`;
|
||||||
}
|
}
|
||||||
const content = await marked(raw);
|
const content = await marked(raw);
|
||||||
|
|||||||
@@ -46,12 +46,23 @@ const subtitle = subtitleMap[docType]?.[locale] ?? '';
|
|||||||
const warning = warningMap[locale];
|
const warning = warningMap[locale];
|
||||||
const legalTitle = legalTitleMap[locale];
|
const legalTitle = legalTitleMap[locale];
|
||||||
|
|
||||||
const filePath = path.resolve('public/legal', locale, `${docType}.md`);
|
// Try multiple paths for dev and production environments
|
||||||
|
const possiblePaths = [
|
||||||
|
path.resolve('public/legal', locale, `${docType}.md`),
|
||||||
|
path.resolve('client/legal', locale, `${docType}.md`),
|
||||||
|
path.resolve('../client/legal', locale, `${docType}.md`),
|
||||||
|
];
|
||||||
let raw = '';
|
let raw = '';
|
||||||
try {
|
for (const filePath of possiblePaths) {
|
||||||
|
try {
|
||||||
raw = fs.readFileSync(filePath, 'utf-8');
|
raw = fs.readFileSync(filePath, 'utf-8');
|
||||||
raw = raw.replace(/^#\s+.+\n*/m, '');
|
raw = raw.replace(/^#\s+.+\n*/m, '');
|
||||||
} catch {
|
break;
|
||||||
|
} catch {
|
||||||
|
// Try next path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!raw) {
|
||||||
raw = `Content not available.`;
|
raw = `Content not available.`;
|
||||||
}
|
}
|
||||||
const content = await marked(raw);
|
const content = await marked(raw);
|
||||||
|
|||||||
Reference in New Issue
Block a user