feat: ship OA drafts/badges, role dual-track, and test-env deploy tooling.
Add approval instance drafts with menu badges, dual-track user/role read model and role bound names, form-designer outline docs, and bare-metal test deploy scripts. Ignore local test-server credential file. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
7af53fc344
commit
de22678c87
56 files changed
+11861
-6082
No files matched your search
+215
-340
@@ -1,409 +1,284 @@
|
||||
# 后端测试环境部署说明
|
||||
# 测试环境裸机部署说明
|
||||
|
||||
> 本文档汇总测试环境相关结论与操作步骤,便于后续追查。
|
||||
> 代码仓库路径:`backend/`;服务器部署目录示例:`/root/zkxw-backend/backend`。
|
||||
> 本文档描述测试机 `101.35.245.122` 上的 **web-admin + backend** 裸机部署拓扑与日常操作。
|
||||
> 代码仓库:`web-admin/`、`backend/`;服务器部署根目录:`/root/zkxw/`。
|
||||
|
||||
**说明**:旧 **8112 / 双机拓扑**(前端 `47.121.120.79` + 后端 `47.99.164.66:8112`)已废弃,**以本文件为准**。
|
||||
|
||||
---
|
||||
|
||||
## 1. 环境与脚本对照
|
||||
## 1. 环境拓扑
|
||||
|
||||
| 项 | 测试环境 | 生产环境 |
|
||||
|----|----------|----------|
|
||||
| 角色 | 地址 |
|
||||
|------|------|
|
||||
| 入口 | `http://101.35.245.122:10001` |
|
||||
| Nginx 静态 | `/root/zkxw/web-admin/dist` |
|
||||
| 后端 | `127.0.0.1:10002`(仅本机) |
|
||||
| 中间件 | 与本机 `backend/.env` 相同(`47.99.164.66`) |
|
||||
| MinIO 浏览器 | `http://47.99.164.66:20004/zkxw`(直连,Nginx 不反代) |
|
||||
|
||||
流量路径:
|
||||
|
||||
```text
|
||||
浏览器 → http://101.35.245.122:10001
|
||||
├─ / → Nginx 静态(web-admin/dist)
|
||||
├─ /api/* → 127.0.0.1:10002(本机后端)
|
||||
└─ MinIO 附件 → 浏览器直连 47.99.164.66:20004/zkxw
|
||||
```
|
||||
|
||||
云安全组 / 防火墙仅放行 **TCP 10001**;后端绑定 `127.0.0.1`,不对公网暴露 10002。
|
||||
|
||||
---
|
||||
|
||||
## 2. 机装依赖
|
||||
|
||||
| 组件 | 状态 | 说明 |
|
||||
|------|------|------|
|
||||
| Python | **已有 3.12.3** | 用于创建 `.venv` |
|
||||
| Nginx | **需安装** | 对外 10001,反代 `/api` |
|
||||
| 中间件 | 远程复用 | MySQL / Redis / MinIO 在 `47.99.164.66` |
|
||||
|
||||
探测包管理器后安装 Nginx(示例):
|
||||
|
||||
```bash
|
||||
# Debian/Ubuntu
|
||||
sudo apt-get update && sudo apt-get install -y nginx
|
||||
|
||||
# RHEL/CentOS/Alma(示例)
|
||||
sudo yum install -y nginx # 或 dnf install -y nginx
|
||||
sudo systemctl enable --now nginx
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 服务器目录
|
||||
|
||||
```text
|
||||
/root/zkxw/
|
||||
├── web-admin/
|
||||
│ └── dist/ # pnpm build:test 产物
|
||||
├── backend/
|
||||
│ ├── app/
|
||||
│ ├── requirements.txt
|
||||
│ ├── start-test.sh
|
||||
│ ├── .env.test # 敏感,勿提交 git
|
||||
│ ├── .venv/ # Python 3.12.3 虚拟环境
|
||||
│ ├── backend-test.pid
|
||||
│ └── logs/
|
||||
└── (Nginx site 配置见下文)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 首次部署
|
||||
|
||||
### 4.1 后端环境变量
|
||||
|
||||
`.env.test` 来源:从**本机开发机** `backend/.env` 复制为服务器 `backend/.env.test`,按测试需要调整(库名、Redis DB、密钥等)。**勿将含密码的文件提交 git**。
|
||||
|
||||
```bash
|
||||
# 在本机执行(示例)
|
||||
scp backend/.env root@101.35.245.122:/root/zkxw/backend/.env.test
|
||||
```
|
||||
|
||||
或使用模板 `backend/.env.test.example` 手工填写。
|
||||
|
||||
### 4.2 虚拟环境与启动
|
||||
|
||||
```bash
|
||||
cd /root/zkxw/backend
|
||||
python3.12 -m venv .venv
|
||||
./.venv/bin/pip install -U pip
|
||||
./.venv/bin/pip install -r requirements.txt
|
||||
chmod +x start-test.sh
|
||||
HOST=127.0.0.1 PORT=10002 ./start-test.sh start
|
||||
./start-test.sh status
|
||||
curl -s http://127.0.0.1:10002/health
|
||||
```
|
||||
|
||||
`start-test.sh` 子命令:`start` / `stop` / `restart` / `status` / `logs` / `activate` / `help`。
|
||||
|
||||
### 4.3 前端静态资源
|
||||
|
||||
在本机构建后上传(见 §6 日常发版)。
|
||||
|
||||
### 4.4 启用 Nginx
|
||||
|
||||
配置文件已存在于仓库:`测试环境部署/zkxw-test-10001.conf`。
|
||||
|
||||
**Ubuntu / Debian:**
|
||||
|
||||
```bash
|
||||
sudo cp 测试环境部署/zkxw-test-10001.conf /etc/nginx/sites-available/zkxw-test-10001.conf
|
||||
sudo ln -sf /etc/nginx/sites-available/zkxw-test-10001.conf /etc/nginx/sites-enabled/
|
||||
sudo nginx -t && sudo systemctl reload nginx
|
||||
```
|
||||
|
||||
**RHEL / CentOS / Alma:**
|
||||
|
||||
```bash
|
||||
sudo cp 测试环境部署/zkxw-test-10001.conf /etc/nginx/conf.d/zkxw-test-10001.conf
|
||||
sudo nginx -t && sudo systemctl reload nginx
|
||||
```
|
||||
|
||||
Nginx 关键配置摘要:
|
||||
|
||||
- `listen 10001`
|
||||
- `root /root/zkxw/web-admin/dist`
|
||||
- `location ^~ /api/` → `http://127.0.0.1:10002`
|
||||
- **不**反代 MinIO(前端直连 `http://47.99.164.66:20004/zkxw`)
|
||||
|
||||
静态目录在 `/root/...` 下时,Nginx 工作用户(如 `www-data`)须能穿越 `/root`,否则首页 500 / Permission denied:
|
||||
|
||||
```bash
|
||||
chmod 755 /root
|
||||
chmod -R a+rX /root/zkxw/web-admin
|
||||
```
|
||||
|
||||
(或将静态目录放到 `/var/www/zkxw` 等非 `/root` 路径。)
|
||||
|
||||
---
|
||||
|
||||
## 5. 脚本与环境对照
|
||||
|
||||
| 项 | 测试环境(本拓扑) | 生产环境 |
|
||||
|----|-------------------|----------|
|
||||
| 启动脚本 | `backend/start-test.sh` | `backend/start-prod.sh` |
|
||||
| 环境变量文件 | `.env.test` | `.env.production` |
|
||||
| 环境变量模板 | `backend/.env.test.example` | `backend/.env.example`(复制为 `.env.production`) |
|
||||
| 默认端口 | **8112** | **8111** |
|
||||
| 默认监听 | `0.0.0.0` | `0.0.0.0` |
|
||||
| 工作进程数 | 1 | 2 |
|
||||
| 日志级别 | `debug` | `info` |
|
||||
| 虚拟环境目录 | **`.venv`** | **`venv`** |
|
||||
| 监听地址 | `127.0.0.1:10002` | `0.0.0.0:8111` |
|
||||
| 虚拟环境 | `.venv` | `venv` |
|
||||
| PID 文件 | `backend-test.pid` | `backend-prod.pid` |
|
||||
| 运行日志 | `logs/backend-test.log` | `logs/backend-prod.log` |
|
||||
|
||||
**同机部署**:测试用 8112、生产用 8111,避免端口冲突;启停命令必须分开,勿混用 `start-test` / `start-prod`。
|
||||
|
||||
应用通过环境变量 `ENV_FILE` 加载配置(见 `backend/app/core/config.py`),启动脚本会 `export ENV_FILE` 后再拉起 uvicorn。
|
||||
应用通过 `ENV_FILE` 加载配置(见 `backend/app/core/config.py`),启动脚本会 `export ENV_FILE` 后再拉起 uvicorn。
|
||||
|
||||
---
|
||||
|
||||
## 2. 服务器目录
|
||||
## 6. 日常发版
|
||||
|
||||
```text
|
||||
/root/zkxw-backend/backend/
|
||||
├── start-test.sh # 测试启停
|
||||
├── start-prod.sh # 生产启停(勿与测试混用命令)
|
||||
├── .env.test # 测试配置(勿提交敏感信息到 git)
|
||||
├── .env.production # 生产配置
|
||||
├── .env.test.example # 测试配置模板
|
||||
├── .venv/ # 测试脚本默认虚拟环境
|
||||
├── venv/ # 生产脚本默认虚拟环境
|
||||
├── backend-test.pid
|
||||
├── backend-prod.pid
|
||||
└── logs/
|
||||
├── backend-test.log
|
||||
└── backend-prod.log
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 首次部署(测试)
|
||||
### 6.1 推荐:一键脚本
|
||||
|
||||
```bash
|
||||
cd /root/zkxw-backend/backend
|
||||
# 仓库根目录执行(默认:前端 build:test + rsync、后端 rsync、重启、验收)
|
||||
python3 测试环境部署/deploy_test_env.py
|
||||
|
||||
# 1) 测试环境变量
|
||||
cp .env.test.example .env.test
|
||||
# 编辑 .env.test:测试库、Redis DB、MinIO bucket、SECRET_KEY、CORS、API 加密等与前端一致
|
||||
|
||||
# 2) 虚拟环境与依赖(须 python3.9+,勿用系统默认 python3.6)
|
||||
python3.9 -m venv .venv
|
||||
./.venv/bin/pip install --upgrade pip
|
||||
./.venv/bin/pip install -r requirements.txt
|
||||
|
||||
# 3) 赋予脚本执行权限
|
||||
chmod +x start-test.sh
|
||||
|
||||
# 4) 启动
|
||||
./start-test.sh start
|
||||
./start-test.sh status
|
||||
curl -s http://127.0.0.1:8112/health
|
||||
python3 测试环境部署/deploy_test_env.py --frontend-only # 仅前端
|
||||
python3 测试环境部署/deploy_test_env.py --backend-only # 仅后端并重启
|
||||
python3 测试环境部署/deploy_test_env.py --verify # 仅验收
|
||||
python3 测试环境部署/deploy_test_env.py --upload-env # 额外上传本机 backend/.env → 远端 .env.test
|
||||
python3 测试环境部署/deploy_test_env.py --pip # 远端重新 pip install -r requirements.txt
|
||||
```
|
||||
|
||||
健康检查与文档:
|
||||
依赖本机:`python3`、`pnpm`、`rsync`、`ssh`/`scp`;密码登录需 `sshpass`。
|
||||
SSH 密码默认读仓库根 `测试环境机器的账号密码`,或环境变量 `ZKXW_TEST_SSH_PASS`(**勿提交密码到 git**)。
|
||||
|
||||
- 健康检查:`http://127.0.0.1:8112/health`
|
||||
- Swagger:`http://127.0.0.1:8112/docs`
|
||||
### 6.2 手工命令(等价)
|
||||
|
||||
```bash
|
||||
# 前端
|
||||
cd web-admin && pnpm build:test
|
||||
rsync -avz --delete dist/ root@101.35.245.122:/root/zkxw/web-admin/dist/
|
||||
|
||||
# 后端
|
||||
rsync -avz --exclude .venv --exclude venv --exclude '__pycache__' --exclude logs \
|
||||
backend/ root@101.35.245.122:/root/zkxw/backend/
|
||||
|
||||
ssh root@101.35.245.122 \
|
||||
'cd /root/zkxw/backend && HOST=127.0.0.1 PORT=10002 ./start-test.sh restart'
|
||||
```
|
||||
|
||||
**注意**:
|
||||
|
||||
- 前端须使用 `pnpm build:test`(读取 `web-admin/.env.test`),勿误用 `pnpm build`。
|
||||
- rsync 默认不覆盖服务器 `.env.test`;配置变更用脚本 `--upload-env` 或单独 scp。
|
||||
- rsync 排除 `.venv`,依赖变更时用脚本 `--pip` 或在机器上重新 `pip install -r requirements.txt`。
|
||||
|
||||
---
|
||||
|
||||
## 4. `start-test.sh` 命令
|
||||
## 7. 验收
|
||||
|
||||
```bash
|
||||
./start-test.sh [start|stop|restart|status|logs|activate|help]
|
||||
curl -s -o /dev/null -w '%{http_code}\n' http://101.35.245.122:10001/
|
||||
curl -s http://101.35.245.122:10001/health
|
||||
curl -s -o /dev/null -w '%{http_code}\n' --connect-timeout 5 http://101.35.245.122:10002/health || echo '10002_closed_ok'
|
||||
curl -s -o /dev/null -w '%{http_code}\n' http://101.35.245.122:10001/api/web/auth/login-rsa-public-key
|
||||
```
|
||||
|
||||
| 命令 | 说明 |
|
||||
预期:
|
||||
|
||||
- 首页与 `/health` 返回 **200**
|
||||
- 外网直连 `10002` **不可达**(输出 `10002_closed_ok` 或超时)
|
||||
- 登录 RSA 公钥接口经 Nginx 反代返回 **200**
|
||||
|
||||
---
|
||||
|
||||
## 8. 前端环境变量(`web-admin/.env.test`)
|
||||
|
||||
| 变量 | 说明 |
|
||||
|------|------|
|
||||
| `start` | 后台启动(默认) |
|
||||
| `stop` | 停止 |
|
||||
| `restart` | 重启 |
|
||||
| `status` | 查看运行状态 |
|
||||
| `logs` | 最近 100 行日志 |
|
||||
| `activate` | 激活 `.venv` 并进入交互 shell(等价 `source .venv/bin/activate`) |
|
||||
| `help` | 帮助 |
|
||||
|
||||
### 激活虚拟环境
|
||||
|
||||
```bash
|
||||
./start-test.sh activate
|
||||
# 或手动:
|
||||
source .venv/bin/activate
|
||||
```
|
||||
|
||||
`activate` 会进入已激活 venv 的交互 shell,输入 `exit` 退出。
|
||||
|
||||
### 可选环境变量
|
||||
|
||||
```bash
|
||||
HOST=0.0.0.0 PORT=8112 WORKERS=1 LOG_LEVEL=debug \
|
||||
ENV_FILE=./.env.test VENV_DIR=./.venv ./start-test.sh start
|
||||
```
|
||||
|
||||
若仍使用旧目录 `venv` 而非 `.venv`:
|
||||
|
||||
```bash
|
||||
VENV_DIR=./venv ./start-test.sh start
|
||||
```
|
||||
|
||||
若测试也必须占用 **8111**(需确保未启动生产实例):
|
||||
|
||||
```bash
|
||||
PORT=8111 ./start-test.sh start
|
||||
```
|
||||
| `VITE_BASE_URL` | `/`(部署在 10001 根路径) |
|
||||
| `VITE_API_URL` | `/api`(同域 Nginx 反代至 127.0.0.1:10002) |
|
||||
| `VITE_MINIO_BASE_URL` | `http://47.99.164.66:20004/zkxw`(浏览器直连,不经测试机 Nginx) |
|
||||
| `VITE_API_ENCRYPT_*` | 与 `backend/.env.test` 中 `API_ENCRYPT_*` 一致 |
|
||||
|
||||
---
|
||||
|
||||
## 5. 生产环境(对照,便于追查)
|
||||
|
||||
生产脚本:`backend/start-prod.sh`,默认端口 **8111**,读取 `.env.production`。
|
||||
|
||||
```bash
|
||||
cd /root/zkxw-backend/backend
|
||||
cp .env.example .env.production # 按生产填写
|
||||
python3 -m venv venv && ./venv/bin/pip install -r requirements.txt
|
||||
chmod +x start-prod.sh
|
||||
./start-prod.sh start
|
||||
curl -s http://127.0.0.1:8111/health
|
||||
```
|
||||
|
||||
生产命令:`start` / `stop` / `restart` / `status` / `logs`(无 `activate` 子命令)。
|
||||
|
||||
---
|
||||
|
||||
## 6. `.env.test` 配置要点
|
||||
|
||||
模板文件:`backend/.env.test.example`。
|
||||
|
||||
| 配置项 | 建议 |
|
||||
|--------|------|
|
||||
| `DEBUG` | 测试可为 `true` |
|
||||
| `DATABASE_URL` | **独立测试库**,勿直连生产库 |
|
||||
| `SECRET_KEY` | 与生产区分,勿提交仓库 |
|
||||
| `REDIS_DB` | 与生产区分 DB 编号 |
|
||||
| `MINIO_BUCKET` | 建议独立 bucket |
|
||||
| `API_ENCRYPT_*` | 与测试环境前端 `VITE_API_ENCRYPT_*` 一致 |
|
||||
| `BACKEND_CORS_ORIGINS` | 测试管理端 / 小程序 H5 域名(JSON 数组) |
|
||||
| 微信支付 | 测试可用 `WXPAY_SANDBOX=true` 或留空 |
|
||||
|
||||
`.env.test`、`.env.production` 含密钥,**不要提交到 git**。
|
||||
|
||||
---
|
||||
|
||||
## 7. Web 管理端(web-admin)测试打包与部署
|
||||
|
||||
### 7.1 环境拓扑
|
||||
|
||||
| 角色 | 地址 | 说明 |
|
||||
|------|------|------|
|
||||
| 前端 Nginx | `47.121.120.79:8112` | 静态资源 + `/api` 反代 |
|
||||
| 前端静态目录 | `/root/zkxw/dist` | `pnpm build:test` 产物上传至此 |
|
||||
| 测试后端 | `47.99.164.66:8112` | `backend/start-test.sh` 启动 |
|
||||
|
||||
浏览器访问:`http://47.121.120.79:8112`
|
||||
API 请求:`http://47.121.120.79:8112/api/...` → Nginx 转发 → `http://47.99.164.66:8112/api/...`
|
||||
|
||||
### 7.2 本地打包命令
|
||||
|
||||
```bash
|
||||
cd web-admin
|
||||
|
||||
# 首次或依赖变更
|
||||
pnpm install
|
||||
|
||||
# 测试环境打包(读取 web-admin/.env.test)
|
||||
pnpm build:test
|
||||
|
||||
# 产物目录
|
||||
ls dist/
|
||||
```
|
||||
|
||||
等价命令:
|
||||
|
||||
```bash
|
||||
pnpm exec vite build --mode test
|
||||
```
|
||||
|
||||
需要类型检查后再打包:
|
||||
|
||||
```bash
|
||||
pnpm build:typecheck --mode test # 若需单独 typecheck,可先 vue-tsc --noEmit
|
||||
```
|
||||
|
||||
### 7.3 上传到前端服务器
|
||||
|
||||
```bash
|
||||
# 在 web-admin 目录执行(示例)
|
||||
rsync -avz --delete dist/ root@47.121.120.79:/root/zkxw/dist/
|
||||
```
|
||||
|
||||
或使用 scp:
|
||||
|
||||
```bash
|
||||
scp -r dist/* root@47.121.120.79:/root/zkxw/dist/
|
||||
```
|
||||
|
||||
### 7.4 前端环境变量(`web-admin/.env.test`)
|
||||
|
||||
| 变量 | 测试值 | 说明 |
|
||||
|------|--------|------|
|
||||
| `VITE_BASE_URL` | `/` | 部署在 8112 根路径 |
|
||||
| `VITE_API_URL` | `/api` | 同域 Nginx 反代,避免跨域 |
|
||||
| `VITE_API_ENCRYPT_ENABLED` | `true` | 与后端 `API_ENCRYPT_ENABLED` 一致 |
|
||||
| `VITE_API_ENCRYPT_FIXED_SECRET` | 与后端相同 | 须与 `backend/.env.test` 一致 |
|
||||
| `VITE_MINIO_BASE_URL` | `/minio/zkxw` | 由 Nginx 反代 MinIO |
|
||||
|
||||
### 7.5 Nginx 配置
|
||||
|
||||
配置文件:`测试环境部署/nginx.conf`(已新增 **8112** 的 `ZKXW` server 块)。
|
||||
|
||||
部署到服务器:
|
||||
|
||||
```bash
|
||||
# 备份后覆盖或合并 include
|
||||
nginx -t && nginx -s reload
|
||||
```
|
||||
|
||||
关键配置摘要:
|
||||
|
||||
- `listen 8112`
|
||||
- `root /root/zkxw/dist`
|
||||
- `location ^~ /api/` → `http://47.99.164.66:8112`(完整 URI 转发)
|
||||
- `location ^~ /minio/zkxw/` → `http://47.99.164.66:20003/zkxw/`(MinIO 端口按后端实际映射调整)
|
||||
|
||||
### 7.6 后端 CORS(若直连后端 API 时需要)
|
||||
|
||||
推荐走 Nginx 同域反代,一般**无需**额外 CORS。若前端改为直连 `http://47.99.164.66:8112`,须在 `backend/.env.test` 增加:
|
||||
|
||||
```env
|
||||
BACKEND_CORS_ORIGINS=["http://47.121.120.79:8112"]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. 常见问题
|
||||
|
||||
### API 请求地址是 47.121.120.79 而不是 47.99.164.66(正常)
|
||||
|
||||
测试环境使用 **Nginx 同域反代**,浏览器 Network 里应看到:
|
||||
|
||||
```text
|
||||
http://47.121.120.79:8112/api/web/auth/login-rsa-public-key
|
||||
```
|
||||
|
||||
**不会**显示 `47.99.164.66`——由 Nginx 在服务端转发到后端,这是预期行为。
|
||||
|
||||
### API 返回 504 Gateway Time-out(当前最可能问题)
|
||||
|
||||
已实测:`http://47.121.120.79:8112/` 静态页 **200**,但 `/api/...` 返回 **504**,且从外网直连 `47.99.164.66:8112` **连接超时**。
|
||||
|
||||
说明 **Nginx 反代配置已生效**,但 **前端服务器连不上后端 8112**,请在后端机 `47.99.164.66` 与网络侧排查:
|
||||
|
||||
**① 后端服务是否已启动并监听 8112**
|
||||
|
||||
```bash
|
||||
cd /root/zkxw-backend/backend
|
||||
./start-test.sh status
|
||||
ss -lntp | grep 8112 # 应看到 0.0.0.0:8112
|
||||
curl -s http://127.0.0.1:8112/health
|
||||
curl -s http://127.0.0.1:8112/api/web/auth/login-rsa-public-key
|
||||
```
|
||||
|
||||
未启动则:
|
||||
|
||||
```bash
|
||||
cp .env.test.example .env.test # 若尚未配置
|
||||
./start-test.sh start
|
||||
./start-test.sh logs
|
||||
```
|
||||
|
||||
**② 从前端 Nginx 服务器测试能否访问后端(关键)**
|
||||
|
||||
在 **47.121.120.79** 上执行:
|
||||
|
||||
```bash
|
||||
curl -v --connect-timeout 5 http://47.99.164.66:8112/health
|
||||
```
|
||||
|
||||
- 若此处 **超时/拒绝**:不是前端打包问题,需放通 **云安全组 / 防火墙**,允许 **47.121.120.79 → 47.99.164.66:8112**(无需对公网全网开放 8112)。
|
||||
|
||||
阿里云安全组示例:入方向 TCP 8112,源 IP `47.121.120.79/32`。
|
||||
|
||||
本机 firewalld 示例(仅允许 Nginx 服务器):
|
||||
|
||||
```bash
|
||||
firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="47.121.120.79/32" port port="8112" protocol="tcp" accept'
|
||||
firewall-cmd --reload
|
||||
```
|
||||
|
||||
**③ Nginx 配置是否已 reload**
|
||||
|
||||
```bash
|
||||
nginx -t && nginx -s reload
|
||||
tail -f /var/log/nginx/zkxw_admin_8112_error.log
|
||||
```
|
||||
|
||||
**④ 确认前端用测试包打包**
|
||||
|
||||
须使用 `pnpm build:test`(读取 `.env.test`,`VITE_API_URL=/api`)。若误用 `pnpm build` 会走 `.env.production`,API 会打到生产域名。
|
||||
## 9. 常见问题
|
||||
|
||||
### 启动报「环境文件不存在」
|
||||
|
||||
```bash
|
||||
cp .env.test.example .env.test
|
||||
# 编辑后重试
|
||||
# 或从本机 backend/.env 复制后编辑
|
||||
./start-test.sh start
|
||||
```
|
||||
|
||||
### 脚本语法错误(`syntax error near unexpected token`)
|
||||
### API 504 / 502
|
||||
|
||||
`start-*.sh` 须为 **Unix 换行(LF)**,不能是 CRLF。在服务器上可执行:
|
||||
在测试机上排查:
|
||||
|
||||
```bash
|
||||
sed -i 's/\r$//' start-test.sh start-prod.sh
|
||||
./start-test.sh status
|
||||
curl -s http://127.0.0.1:10002/health
|
||||
sudo nginx -t
|
||||
sudo tail -f /var/log/nginx/zkxw_admin_10001_error.log
|
||||
```
|
||||
|
||||
### 测试与生产同时运行
|
||||
### 脚本语法错误(CRLF)
|
||||
|
||||
- 端口:8112 / 8111
|
||||
- PID:`backend-test.pid` / `backend-prod.pid`
|
||||
- 停止测试勿执行 `./start-prod.sh stop`,反之亦然。
|
||||
```bash
|
||||
sed -i 's/\r$//' start-test.sh
|
||||
```
|
||||
|
||||
### 虚拟环境未找到
|
||||
|
||||
```bash
|
||||
python3.9 -m venv .venv
|
||||
./.venv/bin/pip install --upgrade pip
|
||||
python3.12 -m venv .venv
|
||||
./.venv/bin/pip install -U pip
|
||||
./.venv/bin/pip install -r requirements.txt
|
||||
```
|
||||
|
||||
### Python 版本过低 / 依赖安装报 fastapi==0.109.0 找不到
|
||||
|
||||
**现象**:`系统 python3 版本: 3.6`,或 pip 版本 9.x,安装 fastapi 失败。
|
||||
|
||||
**原因**:系统默认 `python3` 为 3.6,而本项目需要 **Python 3.9+**。若曾用 3.6 创建过 `venv/` 或 `.venv/`,启动脚本会误用旧环境。
|
||||
|
||||
**处理**:
|
||||
|
||||
```bash
|
||||
cd /root/zkxw-backend/backend
|
||||
|
||||
# 确认可用版本
|
||||
python3.9 --version
|
||||
|
||||
# 检查是否误设了生产 venv 目录
|
||||
echo "VENV_DIR=${VENV_DIR:-<未设置>}"
|
||||
unset VENV_DIR # 若曾 export VENV_DIR=./venv,先取消
|
||||
|
||||
# 删除旧 venv,用 3.9 重建
|
||||
rm -rf .venv venv
|
||||
python3.9 -m venv .venv
|
||||
./.venv/bin/pip install --upgrade pip
|
||||
./.venv/bin/pip install -r requirements.txt
|
||||
|
||||
# 验证
|
||||
./.venv/bin/python --version # 应为 3.9.x
|
||||
./.venv/bin/python -c "import uvicorn; print('ok')"
|
||||
|
||||
./start-test.sh start
|
||||
```
|
||||
|
||||
若 shell 中曾 `export VENV_DIR=./venv`(生产用),测试启动前须 `unset VENV_DIR` 或显式指定:
|
||||
|
||||
```bash
|
||||
VENV_DIR=./.venv ./start-test.sh start
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. 仓库内相关文件索引
|
||||
## 10. 仓库内相关文件
|
||||
|
||||
| 路径 | 说明 |
|
||||
|------|------|
|
||||
| `backend/start-test.sh` | 测试环境启动脚本 |
|
||||
| `backend/start-prod.sh` | 生产环境启动脚本 |
|
||||
| `backend/.env.test.example` | 测试环境变量模板 |
|
||||
| `backend/.env.example` | 通用环境变量示例 |
|
||||
| `backend/app/core/config.py` | `ENV_FILE` 加载逻辑 |
|
||||
| `web-admin/.env.test` | 测试环境前端打包变量 |
|
||||
| `web-admin/package.json` | `pnpm build:test` |
|
||||
| `测试环境部署/nginx.conf` | 前端 8112 + API/MinIO 反代 |
|
||||
| `AGENTS.md` | 开发说明中的 `start-test` / `start-prod` 命令摘要 |
|
||||
| `测试环境部署/zkxw-test-10001.conf` | Nginx 10001 site 配置 |
|
||||
| `测试环境部署/deploy_test_env.py` | 日常发版一键脚本(前端打包 + rsync + 重启 + 验收) |
|
||||
| `docs/superpowers/specs/2026-08-06-test-env-bare-metal-deploy-design.md` | 设计说明 |
|
||||
|
||||
---
|
||||
|
||||
## 10. 修订记录
|
||||
## 11. 修订记录
|
||||
|
||||
| 日期 | 说明 |
|
||||
|------|------|
|
||||
| 2026-06-01 | 初版:测试脚本 `start-test.sh`(8112、`.env.test`、`.venv`)、`activate` 命令、与生产对照及部署步骤 |
|
||||
| 2026-06-01 | 补充:`web-admin` 测试打包(`pnpm build:test`)、Nginx 8112、`/root/zkxw/dist`、API 反代 `47.99.164.66:8112` |
|
||||
| 2026-08-06 | 裸机部署 10001/10002 于 101.35.245.122 |
|
||||
| 2026-08-06 | 新增 `deploy_test_env.py` 日常发版脚本 |
|
||||
Executable
+394
@@ -0,0 +1,394 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
测试环境日常发版脚本(裸机:Nginx 10001 + backend 127.0.0.1:10002)
|
||||
|
||||
在本机仓库根目录或任意目录执行均可(脚本会定位仓库根):
|
||||
|
||||
python3 测试环境部署/deploy_test_env.py # 前端打包 + 后端同步 + 重启
|
||||
python3 测试环境部署/deploy_test_env.py --frontend-only
|
||||
python3 测试环境部署/deploy_test_env.py --backend-only
|
||||
python3 测试环境部署/deploy_test_env.py --verify
|
||||
python3 测试环境部署/deploy_test_env.py --upload-env # 额外上传 backend/.env → 远端 .env.test
|
||||
python3 测试环境部署/deploy_test_env.py --pip # 远端重新 pip install -r requirements.txt
|
||||
|
||||
SSH 密码默认读取仓库根「测试环境机器的账号密码」(root密码:...),
|
||||
也可用环境变量 ZKXW_TEST_SSH_PASS,或 --password / --password-file。
|
||||
勿把密码写进本脚本或提交到 git。
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
from typing import List, Optional, Sequence
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 默认拓扑(与 docs/superpowers/specs/2026-08-06-test-env-bare-metal-deploy-design.md 一致)
|
||||
# ---------------------------------------------------------------------------
|
||||
DEFAULT_HOST = "101.35.245.122"
|
||||
DEFAULT_USER = "root"
|
||||
REMOTE_ROOT = "/root/zkxw"
|
||||
REMOTE_BACKEND = f"{REMOTE_ROOT}/backend"
|
||||
REMOTE_DIST = f"{REMOTE_ROOT}/web-admin/dist"
|
||||
BACKEND_HOST = "127.0.0.1"
|
||||
BACKEND_PORT = "10002"
|
||||
PUBLIC_HTTP_PORT = "10001"
|
||||
|
||||
BACKEND_EXCLUDES = (
|
||||
".venv",
|
||||
"venv",
|
||||
"__pycache__",
|
||||
"*.pyc",
|
||||
"logs",
|
||||
"graphify-out",
|
||||
".env",
|
||||
".env.*",
|
||||
".pytest_cache",
|
||||
".pycache",
|
||||
".DS_Store",
|
||||
)
|
||||
|
||||
|
||||
def repo_root() -> Path:
|
||||
here = Path(__file__).resolve().parent
|
||||
# 测试环境部署/ → 仓库根
|
||||
return here.parent
|
||||
|
||||
|
||||
def die(msg: str, code: int = 1) -> None:
|
||||
print(f"[ERROR] {msg}", file=sys.stderr)
|
||||
raise SystemExit(code)
|
||||
|
||||
|
||||
def info(msg: str) -> None:
|
||||
print(f"[INFO] {msg}")
|
||||
|
||||
|
||||
def run(
|
||||
cmd: Sequence[str],
|
||||
*,
|
||||
cwd: Optional[Path] = None,
|
||||
env: Optional[dict] = None,
|
||||
check: bool = True,
|
||||
) -> subprocess.CompletedProcess:
|
||||
print(f"[CMD] {' '.join(cmd)}")
|
||||
return subprocess.run(
|
||||
list(cmd),
|
||||
cwd=str(cwd) if cwd else None,
|
||||
env=env,
|
||||
check=check,
|
||||
)
|
||||
|
||||
|
||||
def load_password(args: argparse.Namespace, root: Path) -> Optional[str]:
|
||||
if args.password:
|
||||
return args.password
|
||||
env_pass = os.environ.get("ZKXW_TEST_SSH_PASS") or os.environ.get("SSHPASS")
|
||||
if env_pass:
|
||||
return env_pass
|
||||
path = Path(args.password_file) if args.password_file else root / "测试环境机器的账号密码"
|
||||
if not path.is_file():
|
||||
return None
|
||||
text = path.read_text(encoding="utf-8", errors="replace")
|
||||
m = re.search(r"^root密码[::]\s*(.+)\s*$", text, re.M)
|
||||
if m:
|
||||
return m.group(1).strip()
|
||||
# 兜底:第二行非空内容
|
||||
lines = [ln.strip() for ln in text.splitlines() if ln.strip() and not ln.strip().startswith("#")]
|
||||
if len(lines) >= 2 and "密码" in lines[1]:
|
||||
return re.sub(r"^.*?[::]\s*", "", lines[1]).strip()
|
||||
return None
|
||||
|
||||
|
||||
def require_bin(name: str) -> str:
|
||||
path = shutil.which(name)
|
||||
if not path:
|
||||
die(f"未找到命令 `{name}`,请先安装并加入 PATH")
|
||||
return path
|
||||
|
||||
|
||||
def ssh_argv(password: Optional[str]) -> List[str]:
|
||||
"""ssh 可执行前缀(含可选 sshpass)。"""
|
||||
ssh = require_bin("ssh")
|
||||
opts = ["-o", "StrictHostKeyChecking=accept-new", "-o", "RequestTTY=no"]
|
||||
if password:
|
||||
require_bin("sshpass")
|
||||
return ["sshpass", "-e", ssh, *opts]
|
||||
return [ssh, "-o", "BatchMode=yes", *opts]
|
||||
|
||||
|
||||
def scp_argv(password: Optional[str]) -> List[str]:
|
||||
scp = require_bin("scp")
|
||||
opts = ["-o", "StrictHostKeyChecking=accept-new", "-o", "RequestTTY=no"]
|
||||
if password:
|
||||
require_bin("sshpass")
|
||||
return ["sshpass", "-e", scp, *opts]
|
||||
return [scp, "-o", "BatchMode=yes", *opts]
|
||||
|
||||
|
||||
def with_sshpass_env(password: Optional[str], base_env: Optional[dict] = None) -> dict:
|
||||
env = dict(base_env or os.environ)
|
||||
if password:
|
||||
env["SSHPASS"] = password
|
||||
return env
|
||||
|
||||
|
||||
def remote_target(args: argparse.Namespace) -> str:
|
||||
return f"{args.user}@{args.host}"
|
||||
|
||||
|
||||
def ssh_run(args: argparse.Namespace, password: Optional[str], remote_cmd: str) -> None:
|
||||
cmd = [*ssh_argv(password), remote_target(args), remote_cmd]
|
||||
run(cmd, env=with_sshpass_env(password))
|
||||
|
||||
|
||||
def rsync_to(
|
||||
args: argparse.Namespace,
|
||||
password: Optional[str],
|
||||
local: str,
|
||||
remote_path: str,
|
||||
*,
|
||||
delete: bool = False,
|
||||
excludes: Sequence[str] = (),
|
||||
) -> None:
|
||||
require_bin("rsync")
|
||||
# rsync -e 需要单个 shell 字符串
|
||||
ssh_e = " ".join(ssh_argv(password))
|
||||
cmd: List[str] = ["rsync", "-avz"]
|
||||
if delete:
|
||||
cmd.append("--delete")
|
||||
for ex in excludes:
|
||||
cmd.extend(["--exclude", ex])
|
||||
cmd.extend(["-e", ssh_e, local, f"{remote_target(args)}:{remote_path}"])
|
||||
run(cmd, env=with_sshpass_env(password))
|
||||
|
||||
|
||||
def build_frontend(root: Path) -> None:
|
||||
web = root / "web-admin"
|
||||
if not (web / "package.json").is_file():
|
||||
die(f"未找到前端目录: {web}")
|
||||
require_bin("pnpm")
|
||||
env = dict(os.environ)
|
||||
# Vite 大项目默认堆易 OOM
|
||||
env.setdefault("NODE_OPTIONS", "--max-old-space-size=8192")
|
||||
run(["pnpm", "build:test"], cwd=web, env=env)
|
||||
index = web / "dist" / "index.html"
|
||||
if not index.is_file():
|
||||
die(f"打包未生成 {index}")
|
||||
|
||||
|
||||
def sync_frontend(args: argparse.Namespace, password: Optional[str], root: Path) -> None:
|
||||
dist = root / "web-admin" / "dist"
|
||||
if not (dist / "index.html").is_file():
|
||||
die("本地无 web-admin/dist/index.html,请先打包或去掉 --skip-build")
|
||||
rsync_to(
|
||||
args,
|
||||
password,
|
||||
f"{dist}/",
|
||||
f"{REMOTE_DIST}/",
|
||||
delete=True,
|
||||
)
|
||||
# Nginx 读 /root 下静态文件需要可穿越权限
|
||||
ssh_run(
|
||||
args,
|
||||
password,
|
||||
f"chmod 755 /root && chmod -R a+rX {REMOTE_ROOT}/web-admin",
|
||||
)
|
||||
|
||||
|
||||
def sync_backend(args: argparse.Namespace, password: Optional[str], root: Path) -> None:
|
||||
backend = root / "backend"
|
||||
if not (backend / "start-test.sh").is_file():
|
||||
die(f"未找到后端: {backend}/start-test.sh")
|
||||
rsync_to(
|
||||
args,
|
||||
password,
|
||||
f"{backend}/",
|
||||
f"{REMOTE_BACKEND}/",
|
||||
excludes=BACKEND_EXCLUDES,
|
||||
)
|
||||
|
||||
|
||||
def upload_env(args: argparse.Namespace, password: Optional[str], root: Path) -> None:
|
||||
src = root / "backend" / ".env"
|
||||
if not src.is_file():
|
||||
die(f"本机无 {src},无法上传 .env.test")
|
||||
require_bin("scp")
|
||||
with tempfile.NamedTemporaryFile(prefix="zkxw-env-", suffix=".test", delete=False) as tmp:
|
||||
tmp_path = Path(tmp.name)
|
||||
try:
|
||||
tmp_path.write_bytes(src.read_bytes())
|
||||
cmd = [
|
||||
*scp_argv(password),
|
||||
str(tmp_path),
|
||||
f"{remote_target(args)}:{REMOTE_BACKEND}/.env.test",
|
||||
]
|
||||
run(cmd, env=with_sshpass_env(password))
|
||||
info(f"已上传 {src} → {REMOTE_BACKEND}/.env.test")
|
||||
finally:
|
||||
tmp_path.unlink(missing_ok=True)
|
||||
|
||||
|
||||
def remote_pip(args: argparse.Namespace, password: Optional[str]) -> None:
|
||||
ssh_run(
|
||||
args,
|
||||
password,
|
||||
f"cd {REMOTE_BACKEND} && ./.venv/bin/pip install -r requirements.txt",
|
||||
)
|
||||
|
||||
|
||||
def restart_backend(args: argparse.Namespace, password: Optional[str]) -> None:
|
||||
ssh_run(
|
||||
args,
|
||||
password,
|
||||
f"cd {REMOTE_BACKEND} && chmod +x start-test.sh && "
|
||||
f"HOST={BACKEND_HOST} PORT={BACKEND_PORT} ./start-test.sh restart",
|
||||
)
|
||||
|
||||
|
||||
def verify(args: argparse.Namespace) -> None:
|
||||
require_bin("curl")
|
||||
base = f"http://{args.host}:{PUBLIC_HTTP_PORT}"
|
||||
checks = [
|
||||
("首页", f"{base}/", True),
|
||||
("health", f"{base}/health", False),
|
||||
("login-rsa", f"{base}/api/web/auth/login-rsa-public-key", True),
|
||||
]
|
||||
ok = True
|
||||
for name, url, code_only in checks:
|
||||
if code_only:
|
||||
cp = subprocess.run(
|
||||
["curl", "-s", "-o", "/dev/null", "-w", "%{http_code}", "--connect-timeout", "10", url],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
code = (cp.stdout or "").strip()
|
||||
print(f"[VERIFY] {name}: HTTP {code} ({url})")
|
||||
if code != "200":
|
||||
ok = False
|
||||
else:
|
||||
cp = subprocess.run(
|
||||
["curl", "-s", "--connect-timeout", "10", url],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
body = (cp.stdout or "").strip()
|
||||
print(f"[VERIFY] {name}: {body[:120]} ({url})")
|
||||
if "healthy" not in body and cp.returncode != 0:
|
||||
ok = False
|
||||
|
||||
# 10002 应对外不可达
|
||||
cp = subprocess.run(
|
||||
[
|
||||
"curl",
|
||||
"-s",
|
||||
"-o",
|
||||
"/dev/null",
|
||||
"-w",
|
||||
"%{http_code}",
|
||||
"--connect-timeout",
|
||||
"5",
|
||||
f"http://{args.host}:{BACKEND_PORT}/health",
|
||||
],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
code = (cp.stdout or "").strip()
|
||||
if code in ("", "000") or cp.returncode != 0:
|
||||
print(f"[VERIFY] 外网 10002: 不可达(符合预期) code={code or 'n/a'}")
|
||||
else:
|
||||
print(f"[VERIFY] 外网 10002: 意外可达 HTTP {code}(应仅本机监听)")
|
||||
ok = False
|
||||
|
||||
if not ok:
|
||||
die("验收未全部通过", code=2)
|
||||
info("验收通过")
|
||||
|
||||
|
||||
def parse_args(argv: Optional[Sequence[str]] = None) -> argparse.Namespace:
|
||||
p = argparse.ArgumentParser(
|
||||
description="ZKXW 测试环境发版(前端 build:test + rsync + backend restart)",
|
||||
)
|
||||
p.add_argument("--host", default=DEFAULT_HOST, help=f"测试机 IP(默认 {DEFAULT_HOST})")
|
||||
p.add_argument("--user", default=DEFAULT_USER, help=f"SSH 用户(默认 {DEFAULT_USER})")
|
||||
p.add_argument("--password", default=None, help="SSH 密码(不推荐;优先用密码文件或环境变量)")
|
||||
p.add_argument(
|
||||
"--password-file",
|
||||
default=None,
|
||||
help="密码文件路径(默认:仓库根/测试环境机器的账号密码)",
|
||||
)
|
||||
g = p.add_mutually_exclusive_group()
|
||||
g.add_argument("--frontend-only", action="store_true", help="仅前端打包并上传")
|
||||
g.add_argument("--backend-only", action="store_true", help="仅同步后端并重启")
|
||||
g.add_argument("--verify", action="store_true", help="仅跑验收 curl")
|
||||
p.add_argument("--skip-build", action="store_true", help="前端跳过 pnpm build:test,直接上传已有 dist")
|
||||
p.add_argument("--skip-restart", action="store_true", help="同步后端后不重启")
|
||||
p.add_argument("--upload-env", action="store_true", help="上传本机 backend/.env 为远端 .env.test")
|
||||
p.add_argument("--pip", action="store_true", help="远端执行 pip install -r requirements.txt")
|
||||
p.add_argument("--no-verify", action="store_true", help="发版后不自动验收")
|
||||
return p.parse_args(argv)
|
||||
|
||||
|
||||
def main(argv: Optional[Sequence[str]] = None) -> None:
|
||||
args = parse_args(argv)
|
||||
root = repo_root()
|
||||
os.chdir(root)
|
||||
info(f"仓库根目录: {root}")
|
||||
|
||||
if args.verify:
|
||||
verify(args)
|
||||
return
|
||||
|
||||
password = load_password(args, root)
|
||||
if not password and not os.environ.get("SSH_AUTH_SOCK"):
|
||||
info("未找到 SSH 密码;将尝试密钥登录(BatchMode)")
|
||||
elif password:
|
||||
info("已加载 SSH 密码(来自参数/环境变量/密码文件)")
|
||||
|
||||
do_fe = not args.backend_only
|
||||
do_be = not args.frontend_only
|
||||
|
||||
if do_fe:
|
||||
if not args.skip_build:
|
||||
info("开始前端打包 pnpm build:test …")
|
||||
build_frontend(root)
|
||||
else:
|
||||
info("跳过打包,使用现有 web-admin/dist")
|
||||
info("上传前端 dist …")
|
||||
sync_frontend(args, password, root)
|
||||
|
||||
if do_be:
|
||||
info("同步后端源码 …")
|
||||
sync_backend(args, password, root)
|
||||
if args.upload_env:
|
||||
upload_env(args, password, root)
|
||||
if args.pip:
|
||||
info("远端 pip install …")
|
||||
remote_pip(args, password)
|
||||
if not args.skip_restart:
|
||||
info("重启后端 …")
|
||||
restart_backend(args, password)
|
||||
else:
|
||||
info("已跳过后端重启")
|
||||
|
||||
if not args.no_verify:
|
||||
info("发版后验收 …")
|
||||
verify(args)
|
||||
|
||||
info("完成")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
main()
|
||||
except subprocess.CalledProcessError as e:
|
||||
die(f"命令失败(exit {e.returncode}): {e.cmd}")
|
||||
except KeyboardInterrupt:
|
||||
die("已中断", code=130)
|
||||
@@ -0,0 +1,59 @@
|
||||
# ZKXW 测试环境(裸机)— 部署到 101.35.245.122
|
||||
# 用法(按发行版二选一):
|
||||
# Ubuntu/Debian: 复制到 /etc/nginx/sites-available/zkxw-test-10001.conf
|
||||
# ln -sf ... /etc/nginx/sites-enabled/
|
||||
# RHEL/CentOS: 复制到 /etc/nginx/conf.d/zkxw-test-10001.conf
|
||||
# nginx -t && systemctl reload nginx
|
||||
|
||||
server {
|
||||
listen 10001;
|
||||
listen [::]:10001;
|
||||
server_name 101.35.245.122 localhost 127.0.0.1;
|
||||
|
||||
root /root/zkxw/web-admin/dist;
|
||||
index index.html;
|
||||
|
||||
client_max_body_size 100M;
|
||||
|
||||
access_log /var/log/nginx/zkxw_admin_10001_access.log;
|
||||
error_log /var/log/nginx/zkxw_admin_10001_error.log;
|
||||
|
||||
location = /health {
|
||||
proxy_pass http://127.0.0.1:10002/health;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_connect_timeout 10s;
|
||||
proxy_read_timeout 30s;
|
||||
}
|
||||
|
||||
location ^~ /docs {
|
||||
proxy_pass http://127.0.0.1:10002;
|
||||
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 = /openapi.json {
|
||||
proxy_pass http://127.0.0.1:10002/openapi.json;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
}
|
||||
|
||||
location ^~ /api/ {
|
||||
proxy_pass http://127.0.0.1:10002;
|
||||
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_connect_timeout 10s;
|
||||
proxy_send_timeout 120s;
|
||||
proxy_read_timeout 120s;
|
||||
}
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user