feat: enhance production deployment configuration and documentation

- Update .env.example with production-ready environment variables
- Add frontend Aliyun deployment configuration with nginx gateway
- Refactor docker-compose.server.yml for external services
- Add comprehensive deployment guide (docs/DEPLOYMENT.md)
- Enhance troubleshooting documentation with Git Bash path issues
- Add deployment scripts (build-frontend.sh, deploy-frontend.sh)
- Add deploy directory with docker-compose and nginx config
- Update .gitignore to exclude temporary directories

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-06-18 22:27:00 +08:00
parent 175c0a69af
commit b7073d9c5b
11 changed files with 768 additions and 64 deletions
+32 -18
View File
@@ -1,21 +1,35 @@
# frontend
NEXT_PUBLIC_API_BASE_URL=http://localhost:8000/api/v1
# backend
# ===== 应用 =====
APP_NAME=AI Contract Platform
APP_ENV=development
SHARED_AI_PLATFORM_BASE_URL=
# Default shared-ai contract version is v0.2. Override to /api/v0.1 only for temporary compatibility with legacy deployments.
APP_ENV=production
# ===== 安全 =====
SECRET_KEY=change_me_to_a_random_64_plus_character_secret_for_production
# ===== 数据库(外部共享 PostgreSQL =====
DATABASE_URL=postgresql+psycopg://yuqei:change_me@192.168.3.90:5432/ai_contract_platform
# ===== Redis =====
REDIS_URL=redis://redis:6379/0
# ===== MinIO(外部共享) =====
MINIO_ENDPOINT=192.168.3.90:9000
MINIO_ACCESS_KEY=change_me_minio_access_key
MINIO_SECRET_KEY=change_me_minio_secret_key
MINIO_BUCKET=contracts
# ===== Shared AI Platform =====
SHARED_AI_PLATFORM_BASE_URL=http://192.168.3.90:18011
SHARED_AI_PLATFORM_API_PREFIX=/api/v0.2
SHARED_AI_PLATFORM_APP_ID=ai-contract-platform
SHARED_AI_PLATFORM_API_KEY=
SHARED_AI_PLATFORM_TIMEOUT_SECONDS=30
SHARED_AI_PLATFORM_LEGAL_REVIEW_ENABLED=false
SHARED_AI_PLATFORM_LEGAL_REVIEW_FALLBACK_TO_LOCAL=true
SECRET_KEY=change-me
DATABASE_URL=postgresql+psycopg://postgres:postgres@localhost:5432/ai_contract_platform
REDIS_URL=redis://localhost:6379/0
MINIO_ENDPOINT=localhost:9000
MINIO_ACCESS_KEY=minioadmin
MINIO_SECRET_KEY=minioadmin
MINIO_BUCKET=contracts
SHARED_AI_PLATFORM_API_KEY=change_me_shared_ai_api_key
SHARED_AI_PLATFORM_TIMEOUT_SECONDS=180
SHARED_AI_PLATFORM_LEGAL_REVIEW_ENABLED=true
SHARED_AI_PLATFORM_LEGAL_REVIEW_FALLBACK_TO_LOCAL=false
# ===== Legal Change Impact Worker =====
LEGAL_CHANGE_IMPACT_WORKER_BATCH_SIZE=10
LEGAL_CHANGE_IMPACT_WORKER_INTERVAL_SECONDS=300
LEGAL_CHANGE_IMPACT_SEMANTIC_ENABLED=true
# ===== 前端(阿里云部署) =====
NEXT_PUBLIC_API_BASE_URL=/api/v1
+3
View File
@@ -20,3 +20,6 @@ project-docs/automation-artifacts/
backend/project-docs/qa-artifacts/
.claude/worktrees/
.gstack/
# Temporary directories
temp-*/
+117
View File
@@ -0,0 +1,117 @@
# AI Contract Platform 部署说明
## 部署架构
与 hyburk.com 部署方式一致,采用前后端分离部署:
| 组件 | 部署位置 | 目录 | 说明 |
|------|---------|------|------|
| 前端 Next.js | 阿里云 ECS | `/opt/apps/ai-contract-web` | nginx gateway + Next.js 容器 |
| 后端 FastAPI | 192.168.3.90 | `/opt/apps/ai-contract-platform` | backend + worker + redis |
| 后端 Gateway | 192.168.3.90 | `/opt/apps/ai-contract-platform-gateway` | nginx 代理 API |
| 共享 PostgreSQL | 192.168.3.90 | - | 端口 5432 |
| 共享 Redis | 192.168.3.90 | - | 端口 6379 |
| 共享 MinIO | 192.168.3.90 | - | 端口 9000 |
**域名**: `contact.yuqei.com`
**流量路径**:
```
contact.yuqei.com → 阿里云 nginx (127.0.0.1:18083)
├── /api/ → 后端服务器 10.20.0.11:18083 → backend:8000
├── /health → 后端服务器 health check
└── 其他 → 阿里云 Next.js 容器:3000
```
## 配置文件说明
| 文件 | 用途 |
|------|------|
| `docker-compose.server.yml` | 后端服务器部署(192.168.3.90 |
| `docker-compose.frontend.aliyun.yml` | 前端阿里云部署模板 |
| `deploy/web-gateway.conf` | 阿里云 nginx 配置(前端 gateway |
| `.env.example` | 后端环境变量模板 |
## 阿里云前端部署目录结构
```
/opt/apps/ai-contract-web/
├── releases/
│ └── YYYYMMDDHHMMSS/
│ ├── docker-compose.yml # 使用 docker-compose.frontend.aliyun.yml
│ ├── nginx/
│ │ └── web-gateway.conf # nginx 配置
│ └── app/ # 前端代码(从 frontend/ 复制)
│ ├── Dockerfile
│ └── ... (Next.js 项目文件)
├── current → releases/YYYYMMDDHHMMSS # 软链接指向当前版本
└── logs/
└── nginx/
```
## 后端服务器部署目录结构
```
/opt/apps/ai-contract-platform/
├── docker-compose.server.yml
├── .env
├── backend/
│ ├── Dockerfile
│ └── ... (FastAPI 项目文件)
└── frontend/ # 可选,本地开发用
```
## 部署步骤
### 1. 后端部署(192.168.3.90
```bash
cd /opt/apps/ai-contract-platform
git pull
docker compose -f docker-compose.server.yml up -d --build
```
### 2. 前端部署(阿里云)
```bash
# 创建发布目录
TIMESTAMP=$(date +%Y%m%d%H%M%S)
mkdir -p /opt/apps/ai-contract-web/releases/$TIMESTAMP
cd /opt/apps/ai-contract-web/releases/$TIMESTAMP
# 复制配置和代码
cp docker-compose.yml . # 来自项目 docker-compose.frontend.aliyun.yml
mkdir -p nginx app
cp web-gateway.conf nginx/ # 来自项目 deploy/web-gateway.conf
cp -r frontend/* app/ # 复制前端代码
# 构建和启动
docker compose up -d --build
# 更新软链接
ln -sfn /opt/apps/ai-contract-web/releases/$TIMESTAMP /opt/apps/ai-contract-web/current
```
### 3. 验证
```bash
# 后端健康检查
curl http://10.20.0.11:18083/health
# 前端健康检查
curl http://127.0.0.1:18083/healthz
# 完整检查
curl http://contact.yuqei.com/health
```
## 端口说明
| 端口 | 服务 | 位置 |
|------|------|------|
| 18083 | 前端 nginx gateway | 阿里云 127.0.0.1 |
| 18083 | 后端 nginx gateway | 192.168.3.90 10.20.0.11 |
| 8001 | 后端 FastAPI | 192.168.3.90 |
| 5432 | PostgreSQL | 192.168.3.90 |
| 6379 | Redis | 192.168.3.90 |
| 9000 | MinIO | 192.168.3.90 |
+36
View File
@@ -0,0 +1,36 @@
services:
ai-contract-web:
build:
context: ./app
dockerfile: Dockerfile
image: local/ai-contract-web:0.0.1
container_name: ai-contract-web
restart: unless-stopped
environment:
NODE_ENV: production
HOSTNAME: 0.0.0.0
PORT: 3000
BACKEND_BASE_URL: http://10.20.0.11:18083
NEXT_PUBLIC_API_BASE_URL: /api/v1
extra_hosts:
- "10.20.0.11:host-gateway"
networks:
- ai-contract-web-proxy
ai-contract-web-gateway:
image: nginx:1.27-alpine
container_name: ai-contract-web-gateway
restart: unless-stopped
ports:
- "127.0.0.1:18083:80"
volumes:
- ./nginx/web-gateway.conf:/etc/nginx/conf.d/default.conf:ro
- /opt/apps/ai-contract-web/logs/nginx:/var/log/nginx
depends_on:
- ai-contract-web
networks:
- ai-contract-web-proxy
networks:
ai-contract-web-proxy:
name: ai-contract-web-proxy
+71
View File
@@ -0,0 +1,71 @@
upstream ai_contract_web {
server ai-contract-web:3000;
}
upstream ai_contract_api {
server 10.20.0.11:18083;
}
server {
listen 80;
server_name contact.yuqei.com;
charset utf-8;
client_max_body_size 100m;
location = /healthz {
access_log off;
return 200 'ok';
add_header Content-Type text/plain;
}
location /api/ {
proxy_pass http://ai_contract_api/api/;
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;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_buffering off;
proxy_connect_timeout 10s;
proxy_send_timeout 300s;
proxy_read_timeout 300s;
}
location = /health {
proxy_pass http://ai_contract_api/health;
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;
}
location /_next/static/ {
proxy_pass http://ai_contract_web;
proxy_http_version 1.1;
proxy_set_header Host $host;
add_header Cache-Control "public, max-age=31536000, immutable" always;
}
location / {
proxy_pass http://ai_contract_web;
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;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_hide_header Cache-Control;
proxy_hide_header Expires;
add_header Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate" always;
add_header Pragma "no-cache" always;
add_header Expires "0" always;
proxy_connect_timeout 10s;
proxy_send_timeout 120s;
proxy_read_timeout 120s;
}
}
+38 -7
View File
@@ -1,16 +1,47 @@
# AI Contract Platform 前端阿里云部署配置
# 部署位置: 阿里云 /opt/apps/ai-contract-web
#
# ⚠️ 重要提示:NEXT_PUBLIC_API_BASE_URL 和 BACKEND_BASE_URL 必须在构建时设置!
# 运行时的 environment 设置无效,因为这些值会被硬编码到 JS 文件中。
# 构建命令:MSYS_NO_PATHCONV=1 NEXT_PUBLIC_API_BASE_URL="/api/v1" BACKEND_BASE_URL="http://10.20.0.11:18083" npm run build
version: "3.9"
services:
frontend:
ai-contract-web:
build:
context: ./frontend
context: ./app
dockerfile: Dockerfile
args:
BACKEND_BASE_URL: ${BACKEND_BASE_URL:-http://10.20.0.11:8001}
image: local/ai-contract-web:0.0.1
container_name: ai-contract-web
restart: unless-stopped
environment:
NEXT_PUBLIC_API_BASE_URL: ${NEXT_PUBLIC_API_BASE_URL:-/api/v1}
BACKEND_BASE_URL: ${BACKEND_BASE_URL:-http://10.20.0.11:8001}
NODE_ENV: production
HOSTNAME: 0.0.0.0
PORT: 3000
# 注意:以下环境变量在运行时设置无效!
# 它们必须在构建时通过 npm run build 设置:
# MSYS_NO_PATHCONV=1 NEXT_PUBLIC_API_BASE_URL="/api/v1" BACKEND_BASE_URL="http://10.20.0.11:18083" npm run build
extra_hosts:
# 让容器能访问内网 IP(映射到 Docker 宿主机)
- "10.20.0.11:host-gateway"
networks:
- ai-contract-web-proxy
ai-contract-web-gateway:
image: nginx:1.27-alpine
container_name: ai-contract-web-gateway
restart: unless-stopped
ports:
- "${FRONTEND_HOST_PORT:-3003}:3000"
- "127.0.0.1:18083:80"
volumes:
- ./nginx/web-gateway.conf:/etc/nginx/conf.d/default.conf:ro
- /opt/apps/ai-contract-web/logs/nginx:/var/log/nginx
depends_on:
- ai-contract-web
networks:
- ai-contract-web-proxy
networks:
ai-contract-web-proxy:
name: ai-contract-web-proxy
+20 -20
View File
@@ -1,9 +1,16 @@
version: "3.9"
# AI Contract Platform 后端生产部署配置
# 部署位置: 192.168.3.90:/opt/apps/ai-contract-platform
# 使用外部共享服务(PostgreSQL、Redis、MinIO 在 192.168.3.90
# 前端部署在阿里云,见 docker-compose.frontend.aliyun.yml
name: ai-contract-platform
services:
redis:
image: redis:7
restart: unless-stopped
networks:
- ai-contract-backend
backend:
build:
@@ -37,6 +44,10 @@ services:
- backend_storage:/app/storage
ports:
- "8001:8000"
extra_hosts:
- "host.docker.internal:192.168.3.90"
networks:
- ai-contract-backend
legal-change-impact-worker:
build:
@@ -72,25 +83,14 @@ services:
--interval-seconds 300
volumes:
- backend_storage:/app/storage
frontend:
profiles:
- single-host
build:
context: ./frontend
dockerfile: Dockerfile
args:
BACKEND_BASE_URL: ${FRONTEND_BACKEND_BASE_URL:-http://backend:8000}
restart: unless-stopped
depends_on:
backend:
condition: service_started
environment:
NEXT_PUBLIC_API_BASE_URL: ${NEXT_PUBLIC_API_BASE_URL:-/api/v1}
BACKEND_BASE_URL: ${FRONTEND_BACKEND_BASE_URL:-http://backend:8000}
NODE_ENV: production
ports:
- "${FRONTEND_HOST_PORT:-3003}:3000"
extra_hosts:
- "host.docker.internal:192.168.3.90"
networks:
- ai-contract-backend
volumes:
backend_storage:
networks:
ai-contract-backend:
name: ai-contract-platform_default
+289
View File
@@ -0,0 +1,289 @@
# AI Contract Platform 生产部署指南
> 本文档记录了实际部署过程中遇到的问题和解决方案,避免以后部署走弯路。
## 部署架构
| 组件 | 部署位置 | 目录 | 端口 |
|------|---------|------|------|
| 前端 Next.js | 阿里云 ECS (47.94.178.212) | `/opt/apps/ai-contract-web/current` | 容器内 3000 |
| 前端 Gateway | 阿里云 ECS | 同上 | 127.0.0.1:18083 |
| 后端 FastAPI | 192.168.3.90 | `/opt/apps/ai-contract-platform` | 8001:8000 |
| 后端 Gateway | 192.168.3.90 | `/opt/apps/ai-contract-platform-gateway` | 10.20.0.11:18083 |
| PostgreSQL | 192.168.3.90 | - | 5432 |
| Redis | 192.168.3.90 | - | 6379 |
| MinIO | 192.168.3.90 | - | 9000 |
**域名**: `contact.yuqei.com`
**流量路径**:
```
contact.yuqei.com → 阿里云 nginx (127.0.0.1:18083)
├── /api/ → 后端服务器 10.20.0.11:18083 → backend:8000
├── /health → 后端服务器 health check
└── 其他 → 阿里云 Next.js 容器:3000
```
## 构建注意事项(重要!)
### ⚠️ Git Bash 路径转换问题
**问题**: 在 Windows Git Bash 环境下构建 Next.js 时,环境变量 `/api/v1` 会被错误地转换成 Windows 路径 `C:/Program Files/Git/api/v1`
**解决方案**: 使用 `MSYS_NO_PATHCONV=1` 环境变量防止路径转换:
```bash
# 正确的构建命令(在 Git Bash 中执行)
MSYS_NO_PATHCONV=1 NEXT_PUBLIC_API_BASE_URL="/api/v1" BACKEND_BASE_URL="http://10.20.0.11:18083" npm run build
```
**错误示例**:
```bash
# 这会导致 API_BASE_URL = "C:/Program Files/Git/api/v1"
NEXT_PUBLIC_API_BASE_URL=/api/v1 npm run build
# 这会导致 API_BASE_URL = "//api/v1",浏览器解析为 "http://api/v1"
NEXT_PUBLIC_API_BASE_URL="//api/v1" npm run build
```
### Next.js 环境变量特性
**关键点**: `NEXT_PUBLIC_*``BACKEND_BASE_URL` 是**构建时**嵌入的环境变量,运行时设置无效!
- `NEXT_PUBLIC_API_BASE_URL` - 嵌入到浏览器 JS 中,用于客户端 API 请求
- `BACKEND_BASE_URL` - 嵌入到 `server.js` 中,用于 Next.js rewrites(服务器端代理)
构建后这些值被硬编码到 JS 文件中,docker-compose 的 environment 设置无法覆盖。
## 部署步骤
### 1. 本地构建前端
```bash
cd frontend
# 使用 MSYS_NO_PATHCONV=1 防止 Git Bash 路径转换
MSYS_NO_PATHCONV=1 NEXT_PUBLIC_API_BASE_URL="/api/v1" BACKEND_BASE_URL="http://10.20.0.11:18083" npm run build
# 打包 standalone 输出
rm -rf .next/standalone/.next/static
cp -r .next/static .next/standalone/.next/
tar -czf frontend-standalone.tar.gz -C .next/standalone .
```
### 2. 上传到阿里云
```bash
scp frontend-standalone.tar.gz root@47.94.178.212:/tmp/
```
### 3. 在阿里云部署
```bash
ssh root@47.94.178.212
# 解压到 app 目录
cd /opt/apps/ai-contract-web/current/app
rm -rf .next server.js node_modules public
tar -xzf /tmp/frontend-standalone.tar.gz
# 重启容器
cd /opt/apps/ai-contract-web/current
docker compose down
docker compose up -d --build
```
### 4. 后端部署(192.168.3.90
```bash
ssh root@192.168.3.90
cd /opt/apps/ai-contract-platform
git pull
docker compose -f docker-compose.server.yml up -d --build
```
## 配置文件
### docker-compose.yml(阿里云前端)
```yaml
services:
ai-contract-web:
build:
context: ./app
dockerfile: Dockerfile
image: local/ai-contract-web:0.0.1
container_name: ai-contract-web
restart: unless-stopped
environment:
NODE_ENV: production
HOSTNAME: 0.0.0.0
PORT: 3000
# 注意:以下环境变量在运行时设置无效,必须在构建时设置
# BACKEND_BASE_URL: http://10.20.0.11:18083
# NEXT_PUBLIC_API_BASE_URL: /api/v1
extra_hosts:
- "10.20.0.11:host-gateway" # 让容器能访问内网 IP
networks:
- ai-contract-web-proxy
ai-contract-web-gateway:
image: nginx:1.27-alpine
container_name: ai-contract-web-gateway
restart: unless-stopped
ports:
- "127.0.0.1:18083:80"
volumes:
- ./nginx/web-gateway.conf:/etc/nginx/conf.d/default.conf:ro
- /opt/apps/ai-contract-web/logs/nginx:/var/log/nginx
depends_on:
- ai-contract-web
networks:
- ai-contract-web-proxy
networks:
ai-contract-web-proxy:
name: ai-contract-web-proxy
```
### nginx/web-gateway.conf(阿里云)
```nginx
upstream ai_contract_web {
server ai-contract-web:3000;
}
upstream ai_contract_api {
server 10.20.0.11:18083; # 后端服务器 gateway
}
server {
listen 80;
server_name contact.yuqei.com;
charset utf-8;
client_max_body_size 100m;
# API 代理到后端服务器
location /api/ {
proxy_pass http://ai_contract_api/api/;
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;
proxy_buffering off;
proxy_connect_timeout 10s;
proxy_send_timeout 300s;
proxy_read_timeout 300s;
}
# 前端 Next.js
location / {
proxy_pass http://ai_contract_web;
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;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
```
## 默认账号
| 账号 | 密码 | 角色 |
|------|------|------|
| admin@example.com | 123456 | admin |
## 常见问题排查
### 1. 登录报"网络异常"
**原因**: `NEXT_PUBLIC_API_BASE_URL` 构建时值错误(如 `C:/Program Files/Git/api/v1``//api/v1`
**排查**:
```bash
# 检查部署的 JS 文件中的 API_BASE_URL
curl -s 'http://contact.yuqei.com/_next/static/chunks/*.js' | grep -oP 'tl="[^"]*"'
# 正确值应为: tl="/api/v1"
# 错误值: tl="C:/Program Files/Git/api/v1" 或 tl="//api/v1"
```
**解决**: 使用 `MSYS_NO_PATHCONV=1` 重新构建前端
### 2. 容器无法访问后端
**原因**: 容器内无法访问内网 IP `10.20.0.11`
**解决**: 在 docker-compose.yml 中添加 `extra_hosts`
### 3. API 请求 404
**检查**: nginx 是否正确代理 `/api/` 到后端 gateway
```bash
# 测试 nginx gateway 代理
ssh root@47.94.178.212 "curl -s http://127.0.0.1:18083/api/v1/auth/login -X POST -H 'Content-Type: application/json' -d '{\"account\":\"admin@example.com\",\"password\":\"123456\"}'"
```
### 4. 检查后端日志
```bash
ssh root@192.168.3.90 "docker logs ai-contract-platform-backend-1 --tail 30"
```
## 验证部署
```bash
# 1. 测试前端页面
curl -s -o /dev/null -w "%{http_code}" http://contact.yuqei.com/
# 2. 测试 API 登录
curl -s -X POST 'http://contact.yuqei.com/api/v1/auth/login' \
-H 'Content-Type: application/json' \
-d '{"account":"admin@example.com","password":"123456"}'
# 3. 测试后端 health check
curl -s http://10.20.0.11:18083/health
```
## 目录结构
### 阿里云 `/opt/apps/ai-contract-web/current/`
```
├── docker-compose.yml
├── nginx/
│ └── web-gateway.conf
└── app/
├── Dockerfile
├── server.js # Next.js standalone 服务器
├── .next/
│ ├── static/ # 静态 JS/CSS
│ └── server/ # 服务端渲染代码
├── public/
├── node_modules/
└── package.json
```
### 后端服务器 `/opt/apps/ai-contract-platform/`
```
├── docker-compose.server.yml
├── .env
├── backend/
│ ├── Dockerfile
│ ├── app/
│ ├── migrations/
│ └── requirements.txt
└── frontend/ # 本地开发用,不部署
```
## 相关文档
- [[deploy/README.md]] - 部署说明
- [[deploy/web-gateway.conf]] - nginx 配置模板
- [[deploy/docker-compose.yml]] - docker-compose 模板
+93 -19
View File
@@ -27,19 +27,6 @@ environment:
**Rule of thumb**: Any `%` in a URL must be written as `%25`. This applies to `DATABASE_URL`, `REDIS_URL`, and any other URL-based configuration.
**Quick fix command** (on server):
```bash
cd /opt/ai-contract-platform
# Stop backend
docker compose -f docker-compose.server.yml stop backend
# Fix DATABASE_URL: replace % with %25 in docker-compose.server.yml
sed -i 's|zmfCSR%816709301|zmfCSR%25816709301|g' docker-compose.server.yml
# Verify
grep DATABASE_URL docker-compose.server.yml
# Restart
docker compose -f docker-compose.server.yml up -d backend
```
### 2. Missing Template Files at Startup
**Problem**: Backend crashes on startup with:
@@ -73,21 +60,108 @@ def _context(self, current_user: Dict[str, Any]) -> SharedAIRequestContext:
)
```
### 4. 前端登录报"网络异常" - Git Bash 路径转换问题 ⚠️
**问题**: 前端部署后登录失败,浏览器控制台显示请求发送到错误的 URL(如 `http://api/v1/auth/login``http://C:/Program Files/Git/api/v1/auth/login`
**根本原因**: 在 Windows Git Bash 环境下构建 Next.js 时:
- `NEXT_PUBLIC_API_BASE_URL=/api/v1` 被错误地转换成 Windows 路径 `C:/Program Files/Git/api/v1`
- `NEXT_PUBLIC_API_BASE_URL="//api/v1"` 被浏览器解析为 `http://api/v1`(协议相对 URL
**错误示例**:
```bash
# 这会导致 API_BASE_URL = "C:/Program Files/Git/api/v1"
NEXT_PUBLIC_API_BASE_URL=/api/v1 npm run build
# 这会导致 API_BASE_URL = "//api/v1",浏览器解析为 "http://api/v1"
NEXT_PUBLIC_API_BASE_URL="//api/v1" npm run build
```
**正确做法**:
```bash
# 使用 MSYS_NO_PATHCONV=1 防止路径转换
MSYS_NO_PATHCONV=1 NEXT_PUBLIC_API_BASE_URL="/api/v1" BACKEND_BASE_URL="http://10.20.0.11:18083" npm run build
```
**排查方法**:
```bash
# 检查部署的 JS 文件中 API_BASE_URL 的实际值
curl -s 'http://contact.yuqei.com/_next/static/chunks/*.js' | grep -oP 'tl="[^"]*"'
# 正确值: tl="/api/v1"
# 错误值: tl="C:/Program Files/Git/api/v1" 或 tl="//api/v1"
```
### 5. Next.js 环境变量运行时无效
**问题**: 在 docker-compose 的 environment 中设置了 `NEXT_PUBLIC_API_BASE_URL`,但前端仍然使用旧值
**原因**: `NEXT_PUBLIC_*``BACKEND_BASE_URL` 是**构建时**嵌入的环境变量,运行时设置无效!这些值会被硬编码到 JS 文件中。
**解决方案**: 必须在 `npm run build` 时设置环境变量:
```bash
MSYS_NO_PATHCONV=1 NEXT_PUBLIC_API_BASE_URL="/api/v1" BACKEND_BASE_URL="http://10.20.0.11:18083" npm run build
```
### 6. Docker 容器无法访问内网 IP
**问题**: 前端容器内无法访问后端服务器的内网 IP(如 `10.20.0.11`
**解决方案**: 在 docker-compose.yml 中添加 `extra_hosts`:
```yaml
services:
ai-contract-web:
extra_hosts:
- "10.20.0.11:host-gateway" # 映射到 Docker 宿主机
```
## 部署架构
| 组件 | 部署位置 | 目录 | 端口 |
|------|---------|------|------|
| 前端 Next.js | 阿里云 ECS (47.94.178.212) | `/opt/apps/ai-contract-web/current` | 容器内 3000 |
| 前端 Gateway | 阿里云 ECS | 同上 | 127.0.0.1:18083 |
| 后端 FastAPI | 192.168.3.90 | `/opt/apps/ai-contract-platform` | 8001:8000 |
| 后端 Gateway | 192.168.3.90 | `/opt/apps/ai-contract-platform-gateway` | 10.20.0.11:18083 |
| PostgreSQL | 192.168.3.90 | - | 5432 |
| Redis | 192.168.3.90 | - | 6379 |
| MinIO | 192.168.3.90 | - | 9000 |
**域名**: `contact.yuqei.com`
## 部署脚本
项目提供了便捷的部署脚本:
```bash
# 构建前端
./scripts/build-frontend.sh
# 部署前端到阿里云
./scripts/deploy-frontend.sh
```
## Deployment Checklist
1. **Verify password encoding**: Check `DATABASE_URL` in `docker-compose.server.yml` has `%` encoded as `%25`
2. **Pull latest code**: `git pull origin main`
3. **Clean rebuild**: `docker compose -f docker-compose.server.yml down && docker compose -f docker-compose.server.yml up -d --build`
4. **Wait for postgres**: The `backend` container depends on `postgres` health check (up to 20 retries)
5. **Verify**: `curl -s http://localhost:8001/health` should return `{"status":"ok"}`
1. **验证密码编码**: 检查 `DATABASE_URL` `%` 是否编码为 `%25`
2. **前端构建环境变量**: 使用 `MSYS_NO_PATHCONV=1` 防止路径转换
3. **拉取最新代码**: `git pull origin main`
4. **清理重建**: `docker compose down && docker compose up -d --build`
5. **等待 postgres**: backend 容器依赖 postgres 健康检查(最多 20 次重试)
6. **验证**: `curl -s http://localhost:8001/health` 应返回 `{"status":"ok"}`
## Server Information
- **Server**: 192.168.3.90
- **Project directory**: `/opt/ai-contract-platform`
- **Project directory**: `/opt/apps/ai-contract-platform`
- **SSH key**: `~/.ssh/water_audit_ed25519`
- **SSH user**: `root`
- **Gitea**: http://192.168.3.90:3000
- **Frontend**: http://192.168.3.90:3003
- **Backend API**: http://192.168.3.90:8001
- **Database password**: `zmfCSR%816709301` (use `%25` in URLs)
## 默认账号
| 账号 | 密码 | 角色 |
|------|------|------|
| admin@example.com | 123456 | admin |
+36
View File
@@ -0,0 +1,36 @@
#!/bin/bash
# 前端构建脚本 - 用于生产部署
# 解决 Git Bash 路径转换问题
set -e
cd "$(dirname "$0")/frontend"
echo "Building Next.js frontend..."
# 使用 MSYS_NO_PATHCONV=1 防止 Git Bash 将 /api/v1 转换为 Windows 路径
# NEXT_PUBLIC_API_BASE_URL: 浏览器端 API 请求路径(相对路径)
# BACKEND_BASE_URL: 服务器端 rewrites 目标地址
export MSYS_NO_PATHCONV=1
export NEXT_PUBLIC_API_BASE_URL="/api/v1"
export BACKEND_BASE_URL="http://10.20.0.11:18083"
npm run build
echo "Copying static assets to standalone..."
rm -rf .next/standalone/.next/static
cp -r .next/static .next/standalone/.next/
echo "Creating deployment package..."
tar -czf ../deploy/frontend-standalone.tar.gz -C .next/standalone .
echo "Build complete! Package: deploy/frontend-standalone.tar.gz"
echo ""
echo "Next steps:"
echo " 1. scp deploy/frontend-standalone.tar.gz root@47.94.178.212:/tmp/"
echo " 2. ssh root@47.94.178.212"
echo " 3. cd /opt/apps/ai-contract-web/current/app"
echo " 4. rm -rf .next server.js node_modules public"
echo " 5. tar -xzf /tmp/frontend-standalone.tar.gz"
echo " 6. cd /opt/apps/ai-contract-web/current && docker compose down && docker compose up -d --build"
+33
View File
@@ -0,0 +1,33 @@
#!/bin/bash
# 部署前端到阿里云服务器
set -e
ALIYUN_HOST="47.94.178.212"
REMOTE_APP_DIR="/opt/apps/ai-contract-web/current/app"
REMOTE_DIR="/opt/apps/ai-contract-web/current"
PACKAGE="deploy/frontend-standalone.tar.gz"
if [ ! -f "$PACKAGE" ]; then
echo "Error: $PACKAGE not found. Run scripts/build-frontend.sh first."
exit 1
fi
echo "Uploading to Aliyun..."
scp "$PACKAGE" "root@${ALIYUN_HOST}:/tmp/"
echo "Deploying..."
ssh "root@${ALIYUN_HOST}" << 'REMOTE'
cd /opt/apps/ai-contract-web/current/app
rm -rf .next server.js node_modules public
tar -xzf /tmp/frontend-standalone.tar.gz
cd /opt/apps/ai-contract-web/current
docker compose down
docker compose up -d --build
echo "Waiting for container to start..."
sleep 3
docker compose ps
REMOTE
echo "Deployment complete!"
echo "Test: curl -s http://contact.yuqei.com/healthz"