feat: refine subject visits and project workflows
Add early termination visit workflow with ordering, non-applicable visit handling, visit window display, and medication adherence support. Extend monitoring visit issue template fields, site scoping, setup draft project info handling, login security UI, attachment behavior, and related tests/migrations.
This commit is contained in:
@@ -1,48 +1,95 @@
|
||||
# CTMS 分支环境安装配置指南
|
||||
# CTMS 安装步骤向导
|
||||
|
||||
本文定义 `dev`、`main`、`release` 三类分支对应的推荐部署环境、配置文件、初始化步骤与验证命令。分支本身不会自动切换运行模式,实际模式由后端环境变量 `ENV` 决定。
|
||||
按目标分支选择对应章节执行:
|
||||
|
||||
## 分支与环境映射
|
||||
- [dev 分支安装](#dev-分支安装)
|
||||
- [main 分支安装](#main-分支安装)
|
||||
- [release 分支安装](#release-分支安装)
|
||||
|
||||
| 分支 | 分支定位 | 推荐环境 | 后端 `ENV` | 用途 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `dev` | 日常开发与集成 | 开发环境 | `development` | 功能开发、联调、自测、内部集成 |
|
||||
| `main` | 下一版本候选 | 预发布 / 验收环境 | `production` | 回归测试、验收、发布候选验证 |
|
||||
| `release` | 当前稳定生产线 | 生产环境 | `production` | 正式生产部署、生产 hotfix |
|
||||
## 安装前准备
|
||||
|
||||
推广路径保持为:
|
||||
三个分支都先完成本节。
|
||||
|
||||
```text
|
||||
feature/* -> dev -> main -> release
|
||||
### 1. 准备安装目录
|
||||
|
||||
```bash
|
||||
mkdir -p /opt/ctms
|
||||
cd /opt/ctms
|
||||
```
|
||||
|
||||
不要把 `release` 当作日常开发分支使用。`main` 应只接收已经准备进入候选版本范围的变更。
|
||||
### 2. 安装 Docker
|
||||
|
||||
## 公共前置条件
|
||||
Linux 可使用 Docker Engine 或 Docker Desktop。安装完成后确认 Docker Compose v2 可用:
|
||||
|
||||
所有环境都需要:
|
||||
|
||||
- Docker 与 Docker Compose
|
||||
- 可写的 `pg_data/` 数据目录
|
||||
- 端口 `8888`、`8000`、`5432` 未被占用
|
||||
- 后端镜像可安装 `backend/requirements.txt` 中的依赖
|
||||
- 前端镜像可执行 `npm ci` 与 `npm run build`
|
||||
|
||||
当前 compose 服务拓扑:
|
||||
|
||||
```text
|
||||
nginx -> backend -> db
|
||||
```bash
|
||||
docker --version
|
||||
docker compose version
|
||||
```
|
||||
|
||||
对外入口:
|
||||
如果当前用户不能执行 Docker 命令,将用户加入 `docker` 组后重新登录:
|
||||
|
||||
- 前端:`http://localhost:8888`
|
||||
- 后端 API:同域 `/api/v1/*`
|
||||
- 健康检查:`http://localhost:8888/health`
|
||||
```bash
|
||||
sudo usermod -aG docker "$USER"
|
||||
```
|
||||
|
||||
## dev 分支:开发环境
|
||||
macOS 安装 Docker Desktop 后执行:
|
||||
|
||||
`dev` 默认用于本地开发和内部集成,推荐使用 `ENV=development`。
|
||||
```bash
|
||||
docker --version
|
||||
docker compose version
|
||||
```
|
||||
|
||||
### 3. 安装基础工具
|
||||
|
||||
Linux:
|
||||
|
||||
```bash
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y git openssl curl lsof
|
||||
```
|
||||
|
||||
macOS:
|
||||
|
||||
```bash
|
||||
brew install git openssl curl
|
||||
```
|
||||
|
||||
### 4. 检查端口
|
||||
|
||||
```bash
|
||||
lsof -nP -iTCP:8888 -sTCP:LISTEN
|
||||
lsof -nP -iTCP:8000 -sTCP:LISTEN
|
||||
lsof -nP -iTCP:5432 -sTCP:LISTEN
|
||||
```
|
||||
|
||||
如果命令有输出,先停止占用这些端口的服务,或调整 `docker-compose.yaml` 中的端口映射。
|
||||
|
||||
### 5. 拉取代码
|
||||
|
||||
首次安装:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/chengchengzhou7/CTMS.git
|
||||
cd CTMS
|
||||
```
|
||||
|
||||
已有仓库:
|
||||
|
||||
```bash
|
||||
cd /opt/ctms/CTMS
|
||||
git fetch --prune origin
|
||||
```
|
||||
|
||||
### 6. 确认本地配置不会提交
|
||||
|
||||
```bash
|
||||
grep -n "^\\.env$" .gitignore
|
||||
grep -n "^\\.env\\.\\*$" .gitignore
|
||||
```
|
||||
|
||||
预期能看到 `.env` 和 `.env.*` 已被忽略。
|
||||
|
||||
## dev 分支安装
|
||||
|
||||
### 1. 切换分支
|
||||
|
||||
@@ -51,68 +98,83 @@ git checkout dev
|
||||
git pull --rebase origin dev
|
||||
```
|
||||
|
||||
### 2. 配置 `.env`
|
||||
### 2. 写入环境配置
|
||||
|
||||
根目录 `.env` 不提交到仓库。开发环境推荐:
|
||||
在仓库根目录创建或覆盖 `.env`:
|
||||
|
||||
```env
|
||||
```bash
|
||||
cat > .env <<'EOF'
|
||||
COMPOSE_PROJECT_NAME=ctms_dev
|
||||
ENV=development
|
||||
JWT_SECRET_KEY=dev-secret
|
||||
LOGIN_RSA_KEY_ID=default
|
||||
LOGIN_RSA_PRIVATE_KEY=
|
||||
EOF
|
||||
```
|
||||
|
||||
说明:
|
||||
|
||||
- `ENV=development` 会启用开发行为。
|
||||
- 未配置 `LOGIN_RSA_PRIVATE_KEY` 时,后端启动后会生成临时 RSA 私钥。
|
||||
- 后端重启后临时公钥会变化,已有登录页应刷新后重新登录。
|
||||
- `JWT_SECRET_KEY=dev-secret` 只允许本地开发使用。
|
||||
|
||||
### 3. 启动
|
||||
|
||||
```bash
|
||||
docker compose up -d --build backend nginx
|
||||
```
|
||||
|
||||
如果需要首次启动完整栈:
|
||||
### 3. 构建镜像并启动
|
||||
|
||||
```bash
|
||||
docker compose up -d --build
|
||||
```
|
||||
|
||||
### 4. 开发模式行为
|
||||
|
||||
`development` 模式下:
|
||||
|
||||
- FastAPI `debug=True`
|
||||
- 应用启动时会执行 `Base.metadata.create_all`
|
||||
- 应用启动时会确保默认管理员存在
|
||||
- 未配置 RSA 私钥时允许临时生成
|
||||
|
||||
这些行为只适合开发,不适合生产。
|
||||
|
||||
### 5. 验证
|
||||
### 4. 检查容器状态
|
||||
|
||||
```bash
|
||||
docker compose ps
|
||||
docker compose exec backend python -c "from app.core.config import settings; print(settings.ENV)"
|
||||
```
|
||||
|
||||
确认 `ctms_db`、`ctms_backend`、`ctms_nginx` 都是 `Up`,且 `ctms_db` 为 `healthy`。
|
||||
|
||||
### 5. 检查后端环境
|
||||
|
||||
```bash
|
||||
docker compose exec backend python -c "from app.core.config import settings; print('ENV=' + settings.ENV); print('JWT_SECRET_IS_DEV=' + str(settings.JWT_SECRET_KEY == 'dev-secret')); print('LOGIN_RSA_PRIVATE_KEY_SET=' + str(bool(settings.LOGIN_RSA_PRIVATE_KEY)))"
|
||||
```
|
||||
|
||||
预期输出:
|
||||
|
||||
```text
|
||||
ENV=development
|
||||
JWT_SECRET_IS_DEV=True
|
||||
LOGIN_RSA_PRIVATE_KEY_SET=False
|
||||
```
|
||||
|
||||
### 6. 检查接口
|
||||
|
||||
```bash
|
||||
curl -i http://127.0.0.1:8888/health
|
||||
curl -i http://127.0.0.1:8888/api/v1/auth/login-key
|
||||
```
|
||||
|
||||
预期:
|
||||
两个接口都应返回 `HTTP/1.1 200 OK`。
|
||||
|
||||
### 7. 打开系统
|
||||
|
||||
```text
|
||||
ENV=development
|
||||
GET /health -> 200
|
||||
GET /api/v1/auth/login-key -> 200, key_id=default
|
||||
http://localhost:8888
|
||||
```
|
||||
|
||||
## main 分支:预发布 / 验收环境
|
||||
### 8. 运行验证
|
||||
|
||||
`main` 是下一正式版本候选分支,应按生产模式运行,但不直接承载正式生产流量。
|
||||
后端:
|
||||
|
||||
```bash
|
||||
docker compose run --rm -v "$PWD/backend/tests:/code/tests:ro" backend python -m pytest
|
||||
```
|
||||
|
||||
前端:
|
||||
|
||||
```bash
|
||||
cd frontend
|
||||
npm install
|
||||
npm run test:unit
|
||||
npm run type-check
|
||||
npm run build
|
||||
cd ..
|
||||
```
|
||||
|
||||
## main 分支安装
|
||||
|
||||
### 1. 切换分支
|
||||
|
||||
@@ -121,81 +183,143 @@ git checkout main
|
||||
git pull --rebase origin main
|
||||
```
|
||||
|
||||
### 2. 配置 `.env`
|
||||
### 2. 生成密钥
|
||||
|
||||
预发布环境应使用 `ENV=production`,并配置独立于生产的密钥:
|
||||
|
||||
```env
|
||||
COMPOSE_PROJECT_NAME=ctms_staging
|
||||
ENV=production
|
||||
JWT_SECRET_KEY=<staging-strong-random-secret>
|
||||
LOGIN_RSA_KEY_ID=staging-YYYYMMDD
|
||||
LOGIN_RSA_PRIVATE_KEY=<staging-rsa-private-key-pem-with-\n-escaped-newlines>
|
||||
```
|
||||
|
||||
生成密钥示例:
|
||||
生成 JWT 密钥:
|
||||
|
||||
```bash
|
||||
openssl rand -hex 32
|
||||
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048
|
||||
```
|
||||
|
||||
要求:
|
||||
生成 RSA 私钥文件:
|
||||
|
||||
- `JWT_SECRET_KEY` 不得使用 `dev-secret`。
|
||||
- `LOGIN_RSA_PRIVATE_KEY` 必须固定保存,不能每次部署重新生成。
|
||||
- staging 私钥不得复用 production 私钥。
|
||||
```bash
|
||||
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out login_rsa_main.pem
|
||||
```
|
||||
|
||||
### 3. 初始化数据库
|
||||
生成可写入 `.env` 的 RSA 私钥单行文本:
|
||||
|
||||
生产模式不会自动建表或自动补管理员。首次部署或迁移前执行:
|
||||
```bash
|
||||
awk '{printf "%s\\n", $0}' login_rsa_main.pem
|
||||
```
|
||||
|
||||
### 3. 写入环境配置
|
||||
|
||||
在仓库根目录创建或覆盖 `.env`,将占位符替换为上一步生成的值:
|
||||
|
||||
```bash
|
||||
cat > .env <<'EOF'
|
||||
COMPOSE_PROJECT_NAME=ctms_main
|
||||
ENV=production
|
||||
JWT_SECRET_KEY=<替换为 JWT 密钥>
|
||||
LOGIN_RSA_KEY_ID=main-YYYYMMDD
|
||||
LOGIN_RSA_PRIVATE_KEY=<替换为 RSA 私钥单行文本>
|
||||
EOF
|
||||
```
|
||||
|
||||
### 4. 检查 Compose 配置
|
||||
|
||||
```bash
|
||||
docker compose config
|
||||
```
|
||||
|
||||
确认输出中包含:
|
||||
|
||||
```text
|
||||
ENV: production
|
||||
LOGIN_RSA_KEY_ID: main-YYYYMMDD
|
||||
LOGIN_RSA_PRIVATE_KEY: '-----BEGIN PRIVATE KEY-----...'
|
||||
```
|
||||
|
||||
### 5. 初始化数据库
|
||||
|
||||
```bash
|
||||
docker compose run --rm backend-init
|
||||
```
|
||||
|
||||
该步骤应运行 Alembic migration,并确保固定管理员账号存在。
|
||||
|
||||
### 4. 启动
|
||||
### 6. 构建镜像并启动
|
||||
|
||||
```bash
|
||||
docker compose up -d --build
|
||||
```
|
||||
|
||||
### 5. 验证
|
||||
### 7. 检查容器状态
|
||||
|
||||
```bash
|
||||
docker compose config
|
||||
docker compose ps
|
||||
docker compose exec backend python -c "from app.core.config import settings; print(settings.ENV); print(settings.JWT_SECRET_KEY == 'dev-secret'); print(bool(settings.LOGIN_RSA_PRIVATE_KEY))"
|
||||
```
|
||||
|
||||
确认 `ctms_db`、`ctms_backend`、`ctms_nginx` 都是 `Up`,且 `ctms_db` 为 `healthy`。
|
||||
|
||||
### 8. 检查后端环境
|
||||
|
||||
```bash
|
||||
docker compose exec backend python -c "from app.core.config import settings; print('ENV=' + settings.ENV); print('JWT_SECRET_IS_DEV=' + str(settings.JWT_SECRET_KEY == 'dev-secret')); print('LOGIN_RSA_PRIVATE_KEY_SET=' + str(bool(settings.LOGIN_RSA_PRIVATE_KEY))); print('LOGIN_RSA_KEY_ID=' + settings.LOGIN_RSA_KEY_ID)"
|
||||
```
|
||||
|
||||
预期输出:
|
||||
|
||||
```text
|
||||
ENV=production
|
||||
JWT_SECRET_IS_DEV=False
|
||||
LOGIN_RSA_PRIVATE_KEY_SET=True
|
||||
LOGIN_RSA_KEY_ID=main-YYYYMMDD
|
||||
```
|
||||
|
||||
### 9. 检查接口
|
||||
|
||||
本机:
|
||||
|
||||
```bash
|
||||
curl -i http://127.0.0.1:8888/health
|
||||
curl -i http://127.0.0.1:8888/api/v1/auth/login-key
|
||||
```
|
||||
|
||||
预期:
|
||||
远程域名:
|
||||
|
||||
```text
|
||||
ENV=production
|
||||
JWT_SECRET_KEY == dev-secret -> False
|
||||
LOGIN_RSA_PRIVATE_KEY_SET -> True
|
||||
GET /health -> 200
|
||||
GET /api/v1/auth/login-key -> 200
|
||||
```bash
|
||||
curl -i https://<main-domain>/health
|
||||
curl -i https://<main-domain>/api/v1/auth/login-key
|
||||
```
|
||||
|
||||
### 6. 验收门禁
|
||||
接口应返回 `HTTP/1.1 200 OK`。
|
||||
|
||||
在把 `main` 推进到 `release` 前,至少执行:
|
||||
### 10. 打开系统
|
||||
|
||||
本机:
|
||||
|
||||
```text
|
||||
http://localhost:8888
|
||||
```
|
||||
|
||||
远程访问:
|
||||
|
||||
```text
|
||||
https://<main-domain>
|
||||
```
|
||||
|
||||
远程访问必须使用 HTTPS。
|
||||
|
||||
### 11. 运行验证
|
||||
|
||||
后端:
|
||||
|
||||
```bash
|
||||
docker compose run --rm -v "$PWD/backend/tests:/code/tests:ro" backend python -m pytest
|
||||
cd frontend && npm run test:unit
|
||||
cd frontend && npm run type-check
|
||||
cd frontend && npm run build
|
||||
```
|
||||
|
||||
## release 分支:生产环境
|
||||
前端:
|
||||
|
||||
`release` 是正式生产稳定分支。只有正式发布和生产 hotfix 应进入该分支。
|
||||
```bash
|
||||
cd frontend
|
||||
npm install
|
||||
npm run test:unit
|
||||
npm run type-check
|
||||
npm run build
|
||||
cd ..
|
||||
```
|
||||
|
||||
## release 分支安装
|
||||
|
||||
### 1. 切换分支
|
||||
|
||||
@@ -204,125 +328,238 @@ git checkout release
|
||||
git pull --rebase origin release
|
||||
```
|
||||
|
||||
### 2. 配置 `.env`
|
||||
### 2. 准备 HTTPS 域名
|
||||
|
||||
生产环境必须使用生产专用配置:
|
||||
正式访问地址必须是:
|
||||
|
||||
```env
|
||||
COMPOSE_PROJECT_NAME=ctms_prod
|
||||
ENV=production
|
||||
JWT_SECRET_KEY=<production-strong-random-secret>
|
||||
LOGIN_RSA_KEY_ID=prod-YYYYMMDD
|
||||
LOGIN_RSA_PRIVATE_KEY=<production-rsa-private-key-pem-with-\n-escaped-newlines>
|
||||
```text
|
||||
https://<production-domain>
|
||||
```
|
||||
|
||||
要求:
|
||||
如果服务器前面有反向代理,确认代理规则:
|
||||
|
||||
- `.env` 必须只保存在部署机器或密钥管理系统中。
|
||||
- 不得提交 `.env`、私钥、JWT 密钥。
|
||||
- `JWT_SECRET_KEY` 轮换会使既有 token 失效,应安排维护窗口。
|
||||
- `LOGIN_RSA_PRIVATE_KEY` 轮换会影响新登录密钥获取,应同步更新 `LOGIN_RSA_KEY_ID`。
|
||||
```text
|
||||
/ -> CTMS nginx 8888
|
||||
/api/* -> CTMS nginx 8888
|
||||
/health -> CTMS nginx 8888
|
||||
```
|
||||
|
||||
### 3. HTTPS 要求
|
||||
### 3. 生成密钥
|
||||
|
||||
登录加密依赖浏览器 WebCrypto。生产访问必须使用 HTTPS:
|
||||
生成 JWT 密钥:
|
||||
|
||||
- `https://正式域名`
|
||||
- 本地 `localhost` 是浏览器安全上下文例外,但不能代表生产可用性
|
||||
```bash
|
||||
openssl rand -hex 32
|
||||
```
|
||||
|
||||
如果生产仍通过 `http://服务器IP:8888` 访问,前端会拒绝执行登录加密。
|
||||
生成 RSA 私钥文件:
|
||||
|
||||
### 4. 初始化与迁移
|
||||
```bash
|
||||
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out login_rsa_release.pem
|
||||
```
|
||||
|
||||
首次部署或每次包含 migration 的发布:
|
||||
生成可写入 `.env` 的 RSA 私钥单行文本:
|
||||
|
||||
```bash
|
||||
awk '{printf "%s\\n", $0}' login_rsa_release.pem
|
||||
```
|
||||
|
||||
将 `login_rsa_release.pem` 保存到服务器安全目录或密钥管理系统。
|
||||
|
||||
### 4. 写入环境配置
|
||||
|
||||
在仓库根目录创建或覆盖 `.env`,将占位符替换为生产值:
|
||||
|
||||
```bash
|
||||
cat > .env <<'EOF'
|
||||
COMPOSE_PROJECT_NAME=ctms_release
|
||||
ENV=production
|
||||
JWT_SECRET_KEY=<替换为生产 JWT 密钥>
|
||||
LOGIN_RSA_KEY_ID=release-YYYYMMDD
|
||||
LOGIN_RSA_PRIVATE_KEY=<替换为生产 RSA 私钥单行文本>
|
||||
EOF
|
||||
```
|
||||
|
||||
确认 `.env` 没有进入 Git 暂存区:
|
||||
|
||||
```bash
|
||||
git status --short .env
|
||||
```
|
||||
|
||||
预期无输出。
|
||||
|
||||
### 5. 检查 Compose 配置
|
||||
|
||||
```bash
|
||||
docker compose config
|
||||
```
|
||||
|
||||
确认输出中包含:
|
||||
|
||||
```text
|
||||
ENV: production
|
||||
JWT_SECRET_KEY: <不是 dev-secret>
|
||||
LOGIN_RSA_KEY_ID: release-YYYYMMDD
|
||||
LOGIN_RSA_PRIVATE_KEY: '-----BEGIN PRIVATE KEY-----...'
|
||||
```
|
||||
|
||||
### 6. 初始化数据库
|
||||
|
||||
```bash
|
||||
docker compose run --rm backend-init
|
||||
```
|
||||
|
||||
确认 migration 成功后再启动或滚动重启服务。
|
||||
确认命令成功后再启动服务。
|
||||
|
||||
### 5. 启动
|
||||
### 7. 构建镜像并启动
|
||||
|
||||
```bash
|
||||
docker compose up -d --build
|
||||
```
|
||||
|
||||
### 6. 生产验证
|
||||
### 8. 检查容器状态
|
||||
|
||||
```bash
|
||||
docker compose ps
|
||||
docker compose exec backend python -c "from app.core.config import settings; print(settings.ENV); print(settings.JWT_SECRET_KEY == 'dev-secret'); print(bool(settings.LOGIN_RSA_PRIVATE_KEY)); print(settings.LOGIN_RSA_KEY_ID)"
|
||||
```
|
||||
|
||||
确认 `ctms_db`、`ctms_backend`、`ctms_nginx` 都是 `Up`,且 `ctms_db` 为 `healthy`。
|
||||
|
||||
### 9. 检查后端环境
|
||||
|
||||
```bash
|
||||
docker compose exec backend python -c "from app.core.config import settings; print('ENV=' + settings.ENV); print('JWT_SECRET_IS_DEV=' + str(settings.JWT_SECRET_KEY == 'dev-secret')); print('LOGIN_RSA_PRIVATE_KEY_SET=' + str(bool(settings.LOGIN_RSA_PRIVATE_KEY))); print('LOGIN_RSA_KEY_ID=' + settings.LOGIN_RSA_KEY_ID)"
|
||||
```
|
||||
|
||||
预期输出:
|
||||
|
||||
```text
|
||||
ENV=production
|
||||
JWT_SECRET_IS_DEV=False
|
||||
LOGIN_RSA_PRIVATE_KEY_SET=True
|
||||
LOGIN_RSA_KEY_ID=release-YYYYMMDD
|
||||
```
|
||||
|
||||
### 10. 检查生产接口
|
||||
|
||||
```bash
|
||||
curl -i https://<production-domain>/health
|
||||
curl -i https://<production-domain>/api/v1/auth/login-key
|
||||
```
|
||||
|
||||
预期:
|
||||
两个接口都应返回 `HTTP/1.1 200 OK`。
|
||||
|
||||
### 11. 打开系统并登录
|
||||
|
||||
```text
|
||||
https://<production-domain>
|
||||
```
|
||||
|
||||
初始化管理员:
|
||||
|
||||
```text
|
||||
admin@huapont.cn / admin123
|
||||
```
|
||||
|
||||
首次登录后立即修改初始密码。
|
||||
|
||||
### 12. 发布后检查
|
||||
|
||||
查看容器:
|
||||
|
||||
```bash
|
||||
docker compose ps
|
||||
```
|
||||
|
||||
查看后端日志:
|
||||
|
||||
```bash
|
||||
docker compose logs --tail=200 backend
|
||||
```
|
||||
|
||||
查看 nginx 日志:
|
||||
|
||||
```bash
|
||||
docker compose logs --tail=200 nginx
|
||||
```
|
||||
|
||||
执行 smoke:
|
||||
|
||||
```bash
|
||||
BASE_URL=https://<production-domain> EMAIL=<admin-email> PASSWORD=<admin-password> bash docs/setup-config-curl-smoke.sh
|
||||
```
|
||||
|
||||
## 常见安装问题
|
||||
|
||||
### 端口被占用
|
||||
|
||||
检查:
|
||||
|
||||
```bash
|
||||
lsof -nP -iTCP:8888 -sTCP:LISTEN
|
||||
lsof -nP -iTCP:8000 -sTCP:LISTEN
|
||||
lsof -nP -iTCP:5432 -sTCP:LISTEN
|
||||
```
|
||||
|
||||
处理:停止占用进程,或调整 `docker-compose.yaml` 端口映射。
|
||||
|
||||
### 容器名冲突
|
||||
|
||||
检查:
|
||||
|
||||
```bash
|
||||
docker ps -a --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
|
||||
```
|
||||
|
||||
处理:
|
||||
|
||||
```bash
|
||||
docker compose down
|
||||
```
|
||||
|
||||
如果同一台机器要同时运行多套 CTMS,需为每套环境调整 `docker-compose.yaml` 中的 `container_name` 和端口。
|
||||
|
||||
### production 启动失败
|
||||
|
||||
检查:
|
||||
|
||||
```bash
|
||||
grep -n "ENV\\|JWT_SECRET_KEY\\|LOGIN_RSA" .env
|
||||
```
|
||||
|
||||
确认:
|
||||
|
||||
```text
|
||||
ENV=production
|
||||
JWT_SECRET_KEY == dev-secret -> False
|
||||
LOGIN_RSA_PRIVATE_KEY_SET -> True
|
||||
GET /health -> 200
|
||||
GET /api/v1/auth/login-key -> 200
|
||||
JWT_SECRET_KEY 不是 dev-secret
|
||||
LOGIN_RSA_PRIVATE_KEY 不是空值
|
||||
LOGIN_RSA_PRIVATE_KEY 包含 -----BEGIN PRIVATE KEY-----
|
||||
```
|
||||
|
||||
### 7. 多实例约束
|
||||
### 远程访问无法登录
|
||||
|
||||
当前登录 challenge 默认保存在后端进程内:
|
||||
检查当前浏览器地址。如果不是 `https://`,改用 HTTPS 域名访问:
|
||||
|
||||
- 单实例、单 worker:可直接使用
|
||||
- 多实例或多 worker:必须满足以下至少一项
|
||||
- 使用共享缓存保存 challenge,例如 Redis
|
||||
- 启用粘性会话,确保 `/login-key` 与 `/login` 命中同一后端进程
|
||||
- 将后端限制为单 worker 单副本
|
||||
|
||||
如果不满足,上线后可能出现偶发登录失败。
|
||||
|
||||
## 环境变量说明
|
||||
|
||||
| 变量 | dev | main/staging | release/production | 说明 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `COMPOSE_PROJECT_NAME` | `ctms_dev` | `ctms_staging` | `ctms_prod` | 防止不同环境容器名、网络名冲突 |
|
||||
| `ENV` | `development` | `production` | `production` | 后端运行模式 |
|
||||
| `JWT_SECRET_KEY` | `dev-secret` | 强随机 | 强随机 | JWT 签名密钥 |
|
||||
| `LOGIN_RSA_KEY_ID` | `default` | `staging-YYYYMMDD` | `prod-YYYYMMDD` | 登录 RSA 密钥版本 |
|
||||
| `LOGIN_RSA_PRIVATE_KEY` | 空 | staging 私钥 | production 私钥 | RSA 私钥,生产模式必填 |
|
||||
|
||||
## 常见问题
|
||||
|
||||
### 登录页提示当前浏览器环境不支持安全登录加密
|
||||
|
||||
原因:非 HTTPS、非 localhost 的访问环境不满足 WebCrypto 安全上下文要求。
|
||||
|
||||
处理:
|
||||
|
||||
- 本地使用 `http://localhost:8888`
|
||||
- staging/production 使用 HTTPS 域名
|
||||
|
||||
### production 模式启动失败,提示必须配置 LOGIN_RSA_PRIVATE_KEY
|
||||
|
||||
原因:`ENV=production` 时必须提供固定 RSA 私钥。
|
||||
|
||||
处理:
|
||||
|
||||
```bash
|
||||
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048
|
||||
```text
|
||||
https://<domain>
|
||||
```
|
||||
|
||||
将输出 PEM 写入 `.env` 的 `LOGIN_RSA_PRIVATE_KEY`,换行使用 `\n` 转义。
|
||||
本机 `dev` 可使用:
|
||||
|
||||
### 切换分支后环境不符合预期
|
||||
|
||||
分支不会自动修改 `.env`。切换分支后应重新确认:
|
||||
|
||||
```bash
|
||||
docker compose config
|
||||
docker compose exec backend python -c "from app.core.config import settings; print(settings.ENV)"
|
||||
```text
|
||||
http://localhost:8888
|
||||
```
|
||||
|
||||
必要时修改 `.env` 并重启:
|
||||
### 切换分支后环境不正确
|
||||
|
||||
重新执行对应分支的 `.env` 配置,然后重建:
|
||||
|
||||
```bash
|
||||
docker compose up -d --build backend nginx
|
||||
```
|
||||
|
||||
再检查:
|
||||
|
||||
```bash
|
||||
docker compose exec backend python -c "from app.core.config import settings; print(settings.ENV)"
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user