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:
+32
-18
@@ -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
|
||||
|
||||
@@ -20,3 +20,6 @@ project-docs/automation-artifacts/
|
||||
backend/project-docs/qa-artifacts/
|
||||
.claude/worktrees/
|
||||
.gstack/
|
||||
|
||||
# Temporary directories
|
||||
temp-*/
|
||||
|
||||
@@ -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 |
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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 模板
|
||||
@@ -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 |
|
||||
@@ -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"
|
||||
@@ -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"
|
||||
Reference in New Issue
Block a user