# 部署说明

## 唯一部署入口

小虾 SaaS 当前唯一有效 Docker 部署入口是：

- `infra/docker/compose.yml`
- `infra/docker/api.Dockerfile`
- `infra/docker/worker.Dockerfile`
- `infra/docker/web.Dockerfile`
- `infra/docker/deploy-staging.sh`

- `infra/docker/infra-production.yml`（production DB/Redis 独立基础设施）

仓库根目录的 `Dockerfile` 和 `docker-compose.yml` 已废弃，只作为防误用哨兵保留，不允许用于部署。

---

## 环境加载规则

### 开发环境
- 默认读取 `.env`
- 没有 `.env` 时回退到代码默认值

### Staging
- 服务器环境文件：`/var/lib/xiaoxia-saas-staging/.env`
- `APP_ENV=staging`
- `WEB_PORT=3001`
- 由 Gitea Actions 调用 `infra/docker/deploy-staging.sh`

### Production
- 生产环境文件：`/var/lib/xiaoxia-saas-production/.env`
- `APP_ENV=production`
- Production DB/Redis 独立于 staging：`xiaoxia-postgres-production:5432`、`xiaoxia-redis-production:6379`
- 宿主端口：Postgres `5433`、Redis `6380`、API `8001`、Web `3002`
- 只允许通过 tag/release 触发生产应用部署
- API/Worker 必须来自专用构建机或 CI 产出的 runtime image tar：`/var/lib/xiaoxia-saas-production/runtime-images-<tag>.tar`
- Web 必须来自专用构建机或 CI 产出的 prebuilt dist/release artifact，不允许在业务服务器执行前端构建
- 生产机只能接收产物、解压 release artifact、`docker load` runtime image tar、执行迁移、重启容器和健康检查
- 禁止在生产机上构建 API/Worker/Web 镜像；当前生产机资源有限且承载业务服务，不是构建机
- 必须替换所有密钥和 `CHANGE_ME` 项

---

## 本地/手动部署

从仓库根目录执行：

```bash
docker compose -f infra/docker/compose.yml up -d --build
```

Staging 手动部署应复用同一入口：

```bash
cp /var/lib/xiaoxia-saas-staging/.env .env
WEB_PORT=3001 docker compose -f infra/docker/compose.yml up -d --build
```

服务器自动部署使用：

```bash
infra/docker/deploy-staging.sh
```

部署脚本会在服务启动前执行 Alembic：

- 已存在 `alembic_version` 时执行 `alembic upgrade head`
- 已存在业务表但缺少 `alembic_version` 时执行 `alembic stamp head && alembic upgrade head`
- 空库时执行 `alembic upgrade head`

staging 已在 2026-06-21 验证：`alembic_version = 002`，健康检查通过。

---

## 外部服务配置

认证邮件和 session 存储为配置驱动：

- `ENABLE_EMAIL_DELIVERY=false` 时使用 no-op 邮件服务，不对外发送邮件。
- `ENABLE_EMAIL_DELIVERY=true` 时必须配置 `SMTP_HOST`、`SMTP_PORT`、`SMTP_USER`、`SMTP_PASSWORD`、`SMTP_FROM_EMAIL`、`SMTP_FROM_NAME`、`SMTP_USE_TLS`。
- `ENABLE_REDIS_SESSIONS=false` 时登录不会写 Redis session，JWT 仍可用于当前认证链路。
- `ENABLE_REDIS_SESSIONS=true` 时通过 `REDIS_URL` 创建 Redis session store。

Staging 当前可以保持 no-op；Production 开启前必须先验证 SMTP/Redis 连接和密钥。

---

## 生成文件存储与保留

- Staging 未配置 OSS 凭证时，生成视频落盘到 `/var/lib/xiaoxia-saas-staging/generated`，并通过 Nginx `/generated-files/` 公开访问。
- Docker volume host path 由 `GENERATED_FILES_HOST_DIR` 控制，默认仅适用于 staging：`/var/lib/xiaoxia-saas-staging/generated`。
- Production 优先使用 OSS；若临时启用本地 fallback，必须配置独立持久化目录、Nginx 只读公开路径和磁盘告警。
- OSS lifecycle rule 必须在生产 bucket 上配置并记录 rule id：临时/失败任务产物建议 7 天删除；订单/购买关联产物由业务保留策略单独保护。
- 保留策略建议：staging 生成文件保留 7 天或保留最近 20GB；production 按业务套餐/订单状态定义，禁止无上限增长。
- 清理脚本上线前必须先 dry-run 输出待删列表，再按 workspace/project 维度删除，避免误删仍被 GeneratedVideo 记录引用的文件。
- 当前脚本：`python scripts/cleanup_generated_files.py --dir /var/lib/xiaoxia-saas-staging/generated --days 7` 仅 dry-run；确认后再加 `--apply`。

---

## 生产发布清单

生产发布前必须按 `docs/PRODUCTION-RELEASE-CHECKLIST.md` 执行：先备份，后 Alembic，最后 smoke；禁止靠临场记忆操作数据库。

---

## Gitea Actions 约定

- `develop` 分支触发 staging 部署。
- tag `v*` 才允许触发 production 部署。
- Actions 先同步代码到 `/var/lib/xiaoxia-saas-staging/repo`。
- Actions 再复制 `/var/lib/xiaoxia-saas-staging/.env` 到部署工作目录。
- Actions 最终调用 `infra/docker/deploy-staging.sh`。
- Production tag deploy 会传入 `RELEASE_VERSION=${GITHUB_REF_NAME}`。
- Production deploy 必须先上传 runtime image tar；缺少 tar 时 `infra/docker/deploy-production.sh` 必须失败，防止代码已更新但 API/Worker 仍运行旧镜像。
- API/Worker runtime image tar 构建命令：`scripts/build_release_images.sh <tag>`。
- runtime image tar 生产部署入口：`scripts/deploy_release_images_production.sh <tag> <tar>` 或由 tag deploy 调用 `infra/docker/deploy-production.sh` 加载 `/var/lib/xiaoxia-saas-production/runtime-images-<tag>.tar`。

---

## 注意事项

- 不要使用根目录 `Dockerfile`。
- 不要使用根目录 `docker-compose.yml`。
- `.env.production` 不要提交真实密钥。
- OSS、数据库、Redis、JWT 密钥必须通过服务器环境文件注入。
- 生产环境必须设置 `DEBUG=false`。
- staging / production 不允许开启 `AUTO_CREATE_SCHEMA` 绕过 Alembic。
- 生产机禁止构建 API/Worker runtime 镜像；如发现需要构建，先补构建机/CI 产物流程，不得临时开启生产构建。
- `scripts/build_release_images.sh` 会检测生产容器；如果当前机器运行生产服务，默认拒绝构建 runtime 镜像。
