feat(deploy): D1 后端部署就绪(配置外置 + prod fail-fast + 自包含 Compose + 冒烟/回滚)

为什么:用户下午要部署,但全仓没有任何部署产物;且生产漏配密钥会静默使用代码里的 dev 默认值,
属上线阻塞项(04-部署架构 / 12-上线检查清单)。

- 配置外置(三件):DB_URL、JWT 双密钥(**发现 app-secret 此前没外置,一直用 dev 默认值**)、
  MEDIA_SIGNATURE_SECRET、MEDIA_SIGNATURE_REQUIRED、CORS_ALLOWED_ORIGINS、MEDIA_ROOT_DIR
- 新增三份 application-prod.yml:签名强制开启、CORS 收紧、媒资指向挂载点、日志级别
- 新增 ProdSecretGuard(@Profile("prod")):密钥缺失/仍是 dev 默认值/不足 32 位、签名未开、
  DB_URL 或 CORS 为空 → 一次列全并终止启动(exit 1)
- 新增 /healthz 轻量健康端点:查明 /actuator/health 的"超时"是旧 jar 未重启所致,项目本就
  没有 actuator 依赖,故不引新依赖;同时把 /healthz 加进安全白名单
- Docker 化:多阶段 Dockerfile(非 root、tini、依赖层缓存、ffmpeg 只给 worker)、.dockerignore、
  自包含 docker-compose.yml(含 PostgreSQL 18 + Redis + 命名卷 + 按序初始化 sql + healthcheck)
- tools/smoke.sh(失败非 0 退出)+ DEPLOY.md(Compose 主线 / 裸 jar 兜底 / 回滚 / 故障速查)
- 实测:mvn test 83 条全过;两入口 package 通过;prod 缺密钥退出码 1 且报错明确、配齐后正常启动;
  冒烟脚本全 PASS;本机无 Docker,镜像与 compose 未经构建验证(已在回执如实标注)
This commit is contained in:
jianghaifeng committed 2026-10-16 13:01:08 +08:00
1 parent 9f76302e53
commit b567365460
16 files changed
+814 -9

No files matched your search

+34
View File
@@ -0,0 +1,34 @@
# Author: fruit.com.cn
#
# 环境变量样例:复制成 .env 后填真实值(.env 不要提交,已在 .gitignore 里)。
# 生产用 prod profile 时,下面标「必填」的项缺失/仍是 dev 默认值/长度不足 32 位 → 启动直接失败。
# 详细说明见 DEPLOY.md。
# ── 数据库 ─────────────────────────────────────────────
# Docker Compose 单机部署:db 是 compose 里的服务名,保持默认即可;
# 若数据库在别的主机/用云托管,把 DB_URL 换成外部地址。
DB_NAME=fruitshorts
DB_URL=jdbc:postgresql://db:5432/fruitshorts
DB_USER=fruitshorts
DB_PASSWORD=
# ── 密钥(必填,各 32 位以上,且不能用代码里的 dev 默认值)──
# 生成:openssl rand -base64 48
JWT_ADMIN_SECRET=
JWT_APP_SECRET=
MEDIA_SIGNATURE_SECRET=
# ── 生产开关 ───────────────────────────────────────────
# prod profile 会强制 true(缺了签名则启动失败)
MEDIA_SIGNATURE_REQUIRED=true
# 后台前端来源,逗号分隔;生产不许用 *
CORS_ALLOWED_ORIGINS=https://admin.example.com
# 媒资根目录:容器里是挂载点
MEDIA_ROOT_DIR=/data/uploads
# 是否开放 Swagger UI(生产默认 false,只留 /v3/api-docs)
SWAGGER_ENABLED=false
# ── 可选:Google Play 商店票据校验(不配则保持 disabled)──
STORE_VERIFIER=disabled
GOOGLE_PLAY_PACKAGE_NAME=
GOOGLE_PLAY_SERVICE_ACCOUNT_JSON=