为什么:用户下午要部署,但全仓没有任何部署产物;且生产漏配密钥会静默使用代码里的 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 未经构建验证(已在回执如实标注)
92 lines
3.5 KiB
YAML
92 lines
3.5 KiB
YAML
# Author: fruit.com.cn
|
||
|
||
server:
|
||
port: 8081
|
||
|
||
spring:
|
||
application:
|
||
name: fruitshorts-app-api
|
||
autoconfigure:
|
||
exclude: org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration
|
||
datasource:
|
||
driver-class-name: org.postgresql.Driver
|
||
# 地址必须外置:不同环境(本机 / 测试 / 生产)连的库不一样,
|
||
# 生产请用 DB_URL 注入(prod profile 会校验它非空)
|
||
url: ${DB_URL:jdbc:postgresql://127.0.0.1:5432/fruitshorts}
|
||
username: ${DB_USER:fruitshorts}
|
||
password: ${DB_PASSWORD:fruitshorts}
|
||
jackson:
|
||
date-format: yyyy-MM-dd HH:mm:ss
|
||
time-zone: Asia/Shanghai
|
||
|
||
mybatis-plus:
|
||
configuration:
|
||
map-underscore-to-camel-case: true
|
||
global-config:
|
||
banner: false
|
||
db-config:
|
||
# 主键由应用侧雪花算法生成(S0),数据库不再自增
|
||
id-type: assign_id
|
||
logic-delete-field: deleted
|
||
logic-delete-value: 1
|
||
logic-not-delete-value: 0
|
||
sequence:
|
||
# 单机开发用固定值;上多实例前必须改成按实例分配(环境变量注入),否则会撞 ID
|
||
worker-id: 1
|
||
datacenter-id: 1
|
||
|
||
# 接口文档:/doc.html 打开 Swagger UI,/v3/api-docs 是机器可读契约(前端联调按它写)
|
||
springdoc:
|
||
swagger-ui:
|
||
path: /doc.html
|
||
api-docs:
|
||
path: /v3/api-docs
|
||
|
||
fruitshorts:
|
||
interaction:
|
||
# MACHINE_FIRST:机审通过即发布,命中 REVIEW 级敏感词才转人工
|
||
# PRE_REVIEW :全部先审后发(生产建议)
|
||
comment-audit-mode: MACHINE_FIRST
|
||
comment-max-length: 500
|
||
comment-per-minute: 5
|
||
download:
|
||
# 这些值会通过 /app/downloads/policy 下发给客户端,改这里不用发版
|
||
max-per-device: 20
|
||
license-days: 30
|
||
wifi-only-default: true
|
||
max-quality: AUTO
|
||
commerce:
|
||
store:
|
||
# 商店票据校验实现:disabled = 明确报"未配置"(默认,无凭据时对外行为不变)
|
||
# google-play = 走 Google Play Developer API
|
||
verifier: ${STORE_VERIFIER:disabled}
|
||
# 下面两项属付费凭据:只从环境变量/配置注入,不入库、不提交、不打进镜像
|
||
google-play-package-name: ${GOOGLE_PLAY_PACKAGE_NAME:}
|
||
# 服务账号 JSON:文件路径 \| JSON 内容 \| base64(建议用环境变量给 base64)
|
||
google-play-service-account-json: ${GOOGLE_PLAY_SERVICE_ACCOUNT_JSON:}
|
||
google-play-timeout-seconds: 10
|
||
media:
|
||
# 媒体根目录:容器里指向挂载卷;本地默认 ./data/uploads
|
||
root-dir: ${MEDIA_ROOT_DIR:./data/uploads}
|
||
public-prefix: /files
|
||
chunk-size: 5242880
|
||
max-file-size: 2147483648
|
||
# 播放地址签名:生产必开(docs/12-上线检查清单),prod profile 里强制为 true
|
||
signature-secret: ${MEDIA_SIGNATURE_SECRET:fruitshorts-media-dev-secret-change-me-in-production-32b}
|
||
signature-required: ${MEDIA_SIGNATURE_REQUIRED:false}
|
||
signature-ttl-minutes: 15
|
||
jwt:
|
||
# 双密钥:管理端与客户端物理隔离,token 不能互换(AGENTS.md 强制约束 2)
|
||
# 注意 app-secret 以前没写在这里,一直用代码里的 dev 默认值 —— 生产必须注入 JWT_APP_SECRET
|
||
admin-secret: ${JWT_ADMIN_SECRET:fruitshorts-admin-dev-secret-change-me-in-production-32b}
|
||
admin-ttl-hours: 12
|
||
app-secret: ${JWT_APP_SECRET:fruitshorts-app-dev-secret-change-me-in-production-32bytes}
|
||
app-access-ttl-minutes: 120
|
||
app-refresh-ttl-days: 30
|
||
cors:
|
||
allowed-origins: ${CORS_ALLOWED_ORIGINS:*}
|
||
|
||
logging:
|
||
level:
|
||
com.fruitshorts: info
|