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 Compose v2
|
||||
- AWS CLI v2
|
||||
- Node.js 22+ (前端 SSR 服务)
|
||||
- PM2 (前端进程管理)
|
||||
|
||||
确认命令:
|
||||
|
||||
@@ -21,6 +23,31 @@
|
||||
docker --version
|
||||
docker compose 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 持久化数据。
|
||||
|
||||
## 前端部署
|
||||
|
||||
### 构建前端
|
||||
|
||||
在本地开发机器上:
|
||||
|
||||
```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',
|
||||
};
|
||||
|
||||
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 = '';
|
||||
try {
|
||||
raw = fs.readFileSync(filePath, 'utf-8');
|
||||
raw = raw.replace(/^#\s+.+\n*/m, '');
|
||||
} catch {
|
||||
for (const filePath of possiblePaths) {
|
||||
try {
|
||||
raw = fs.readFileSync(filePath, 'utf-8');
|
||||
raw = raw.replace(/^#\s+.+\n*/m, '');
|
||||
break;
|
||||
} catch {
|
||||
// Try next path
|
||||
}
|
||||
}
|
||||
if (!raw) {
|
||||
raw = `Content not available.`;
|
||||
}
|
||||
const content = await marked(raw);
|
||||
|
||||
@@ -46,12 +46,23 @@ const subtitle = subtitleMap[docType]?.[locale] ?? '';
|
||||
const warning = warningMap[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 = '';
|
||||
try {
|
||||
raw = fs.readFileSync(filePath, 'utf-8');
|
||||
raw = raw.replace(/^#\s+.+\n*/m, '');
|
||||
} catch {
|
||||
for (const filePath of possiblePaths) {
|
||||
try {
|
||||
raw = fs.readFileSync(filePath, 'utf-8');
|
||||
raw = raw.replace(/^#\s+.+\n*/m, '');
|
||||
break;
|
||||
} catch {
|
||||
// Try next path
|
||||
}
|
||||
}
|
||||
if (!raw) {
|
||||
raw = `Content not available.`;
|
||||
}
|
||||
const content = await marked(raw);
|
||||
|
||||
Reference in New Issue
Block a user