chore(ci): CI/CD 三阶段 6 job + test/prod 环境 + 启动前的配置自检
.gitlab-ci.yml:契约与实体对账 / 后端编译 / 管理端类型检查与构建 / 学生端 H5 构建 /
接口断言(全套 E2E 打真库)/ 生产环境自检;密钥每次随机生成,顺带证明应用没有偷偷
依赖 dev 默认值。
ProdEnvCheck 是 EnvironmentPostProcessor,跑在任何 bean 之前,拦 6 类「配错了也能启动」;
判「没配」与「配了」要 try { getProperty } catch(F-86)——
getProperty 会解析占位符、失败就抛,只读 PropertySource 又读不到 env 的值。
删掉 Gi 模板残留的 GitHub Pages 工作流(远端是 GitLab、用 pnpm,且它发布的是没有 API 的静态页)。
验证:scripts/e2e-prodguard.py 9/9(三种误配形态都拒绝启动)。
This commit is contained in:
@@ -0,0 +1,12 @@
|
||||
# Spring Boot 的 EnvironmentPostProcessor 注册(META-INF/spring.factories)。
|
||||
#
|
||||
# ⚠️ 为什么在 spring.factories 而不是 .../AutoConfiguration.imports:
|
||||
# `.imports` 是给**自动配置类**用的(它们跑在环境准备好之后);
|
||||
# 而 EnvironmentPostProcessor 要求**在环境准备的过程中**被加载 ——
|
||||
# 这个时机只有 spring.factories 能表达。
|
||||
#
|
||||
# ⚠️ 注册错的表现是"检查静默不生效":应用照常启动、没有任何报错,
|
||||
# 而你以为生产自检在起作用。所以 scripts/e2e-prodguard.py 会**真的起一次**
|
||||
# 来验证它被调用了(不是只看代码在不在)。
|
||||
org.springframework.boot.env.EnvironmentPostProcessor=\
|
||||
cn.tianbaotong.fill.config.ProdEnvCheck
|
||||
@@ -0,0 +1,147 @@
|
||||
# ═══════════════════════════════════════════════════════════
|
||||
# 生产环境(prod)
|
||||
#
|
||||
# ⚠️⚠️ 这个文件里**一个密钥都没有,也永远不要加**。
|
||||
# 全部由环境变量提供;缺失时 **ProdGuard 会拒绝启动**
|
||||
# (见 config/ProdGuard.java —— 那 6 条都是"配错了也能启动、
|
||||
# 而且运行时完全不报错"的配置)。
|
||||
#
|
||||
# ── 必需的环境变量 ──────────────────────────────────────
|
||||
# 数据库 PG_HOST PG_PORT PG_DB PG_USER PG_PASSWORD
|
||||
# 缓存 REDIS_HOST REDIS_PORT REDIS_PASSWORD
|
||||
# 密钥 XT_JWT_SECRET HS512,≥ 64 字节随机值
|
||||
# XT_AES_KEY AES-256,base64,**用于证件号加密**
|
||||
# XT_HMAC_KEY HMAC,用于开放接口签名
|
||||
# XT_RSA_PRIVATE_KEY PKCS#8 单行 base64(登录密码解密)
|
||||
# 存储 XT_STORAGE_PROVIDER=qiniu
|
||||
# QINIU_ACCESS_KEY QINIU_SECRET_KEY QINIU_BUCKET QINIU_DOMAIN
|
||||
# 微信 WX_MP_APP_ID WX_MP_APP_SECRET (小程序)
|
||||
# WX_H5_APP_ID WX_H5_APP_SECRET (公众号网页授权)
|
||||
# WX_H5_CALLBACK WX_H5_BASE_URL (必须是 https 正式域名)
|
||||
# 其他 H5_STATIC_DIR(H5 构建产物目录)
|
||||
#
|
||||
# ⚠️ 密钥怎么生成、怎么轮换、轮换时要注意什么,见
|
||||
# docs/09-部署与运维.md §3「密钥与凭据」。
|
||||
# ═══════════════════════════════════════════════════════════
|
||||
|
||||
spring:
|
||||
datasource:
|
||||
url: jdbc:postgresql://${PG_HOST}:${PG_PORT:5432}/${PG_DB:filldb}?stringtype=unspecified
|
||||
username: ${PG_USER}
|
||||
password: ${PG_PASSWORD}
|
||||
hikari:
|
||||
# 生产把池子放大一些:导出打包是长事务,会占住连接
|
||||
minimum-idle: 10
|
||||
maximum-pool-size: 40
|
||||
data:
|
||||
redis:
|
||||
host: ${REDIS_HOST}
|
||||
port: ${REDIS_PORT:6379}
|
||||
password: ${REDIS_PASSWORD}
|
||||
database: 0
|
||||
flyway:
|
||||
# ⚠️⚠️ 生产**禁止 clean**。它是"清空整个库再重建",
|
||||
# 在生产上执行等于把所有学校的填报数据删掉。
|
||||
# 流水线里也不要用 flyway clean —— 见部署文档的回滚一节。
|
||||
clean-disabled: true
|
||||
# 迁移文件发布后不许改(改了校验失败)。这是保护:它保证
|
||||
# "生产跑的迁移"与"仓库里的迁移"逐字节一致
|
||||
validate-on-migrate: true
|
||||
web:
|
||||
resources:
|
||||
# H5 产物由部署流程放到这个目录(**不进仓库**)
|
||||
static-locations: ${H5_STATIC_DIR:file:/opt/xt/h5/},classpath:/static/
|
||||
|
||||
server:
|
||||
# 生产在反向代理后面:让 Spring 认 X-Forwarded-* ,
|
||||
# 否则生成的回调地址会是 http://内网IP:8080
|
||||
forward-headers-strategy: framework
|
||||
tomcat:
|
||||
# 上传是长连接,生产放宽(学生可能从聊天记录里选一个 40MB 的文件)
|
||||
connection-timeout: 120s
|
||||
threads:
|
||||
max: 200
|
||||
min-spare: 20
|
||||
|
||||
logging:
|
||||
level:
|
||||
root: warn
|
||||
cn.tianbaotong.fill: info
|
||||
file:
|
||||
# ⚠️ 日志按天滚动并保留 30 天:审计要能回答"上周三谁看过那份证件号"
|
||||
name: ${XT_LOG_DIR:/var/log/xt}/xt-server.log
|
||||
logback:
|
||||
rollingpolicy:
|
||||
max-history: 30
|
||||
max-file-size: 200MB
|
||||
total-size-cap: 20GB
|
||||
|
||||
# 生产关掉接口文档:它会把全部接口与字段暴露给公网。
|
||||
# ProdGuard 会检查这一条 —— 开着就拒绝启动。
|
||||
springdoc:
|
||||
api-docs:
|
||||
enabled: false
|
||||
swagger-ui:
|
||||
enabled: false
|
||||
|
||||
management:
|
||||
endpoints:
|
||||
web:
|
||||
exposure:
|
||||
# 生产只暴露健康检查(给负载均衡探活)
|
||||
include: health
|
||||
endpoint:
|
||||
health:
|
||||
show-details: never
|
||||
|
||||
fill:
|
||||
jwt:
|
||||
secret: ${XT_JWT_SECRET}
|
||||
crypto:
|
||||
aes-key: ${XT_AES_KEY}
|
||||
hmac-key: ${XT_HMAC_KEY}
|
||||
rsa:
|
||||
private-key: ${XT_RSA_PRIVATE_KEY}
|
||||
|
||||
security:
|
||||
login-max-fail: 5
|
||||
login-lock-minutes: 15
|
||||
captcha-after-fails: 2
|
||||
|
||||
storage:
|
||||
# 生产必须 qiniu(local 在多副本下必然丢文件,ProdGuard 会拦)
|
||||
provider: qiniu
|
||||
prefix: xiaotutongxue
|
||||
keep-raw: ${XT_STORAGE_KEEP_RAW:false}
|
||||
self-check-on-startup: ${XT_STORAGE_SELF_CHECK:true}
|
||||
qiniu:
|
||||
access-key: ${QINIU_ACCESS_KEY}
|
||||
secret-key: ${QINIU_SECRET_KEY}
|
||||
bucket: ${QINIU_BUCKET}
|
||||
domain: ${QINIU_DOMAIN}
|
||||
region: ${QINIU_REGION:auto}
|
||||
signed-url-expire-seconds: 1800
|
||||
|
||||
mini-program:
|
||||
app-id: ${WX_MP_APP_ID}
|
||||
app-secret: ${WX_MP_APP_SECRET}
|
||||
# ⚠️ 正式发布后用 release。用 trial/develop 生成的码扫开会提示
|
||||
# 「小程序未发布」,而**接口不报错** —— 只会让人以为码生成得不对
|
||||
env-version: ${WX_MP_ENV_VERSION:release}
|
||||
page: pages/index/index
|
||||
public-base-url: ${WX_MP_PUBLIC_BASE_URL}
|
||||
code-cache-days: 30
|
||||
|
||||
mp-h5:
|
||||
app-id: ${WX_H5_APP_ID}
|
||||
app-secret: ${WX_H5_APP_SECRET}
|
||||
callback-url: ${WX_H5_CALLBACK}
|
||||
h5-base-url: ${WX_H5_BASE_URL}
|
||||
# ⚠️⚠️ 生产**必须** false。开了它就是"任何人可登录任何学生"。
|
||||
# 这是 ProdGuard 的第一条检查,写成 true 直接拒绝启动。
|
||||
stub-enabled: false
|
||||
|
||||
tenant-domain:
|
||||
enabled: ${XT_TENANT_DOMAIN_ENABLED:false}
|
||||
base: ${XT_TENANT_DOMAIN_BASE:}
|
||||
scheme: https
|
||||
@@ -0,0 +1,87 @@
|
||||
# ═══════════════════════════════════════════════════════════
|
||||
# 测试环境(test)
|
||||
#
|
||||
# 用途:CI 流水线里的 E2E(`scripts/e2e-*.py` 打真实接口)与联调环境。
|
||||
#
|
||||
# ⚠️⚠️ 这个文件**不含任何密钥**,全部由环境变量提供。
|
||||
# 理由不是"洁癖":test 环境的库会被反复清空重建,
|
||||
# 而**密钥不该跟着库一起被当成一次性的**。更实际的是 ——
|
||||
# CI 上每次跑用**当次随机生成**的密钥(见 .gitlab-ci.yml),
|
||||
# 这样才能证明"应用没有偷偷依赖某个 dev 默认值"。
|
||||
#
|
||||
# 必需的环境变量(缺任何一个都起不来,且报错会指名道姓):
|
||||
# PG_HOST PG_PORT PG_DB PG_USER PG_PASSWORD
|
||||
# REDIS_HOST REDIS_PORT REDIS_PASSWORD
|
||||
# XT_JWT_SECRET XT_AES_KEY XT_HMAC_KEY XT_RSA_PRIVATE_KEY
|
||||
# ═══════════════════════════════════════════════════════════
|
||||
|
||||
spring:
|
||||
datasource:
|
||||
# CI 里 PG 是 services 里的别名(默认 `postgres`),本地跑给 localhost 即可
|
||||
url: jdbc:postgresql://${PG_HOST:localhost}:${PG_PORT:5432}/${PG_DB:filldb}?stringtype=unspecified
|
||||
username: ${PG_USER:fill}
|
||||
password: ${PG_PASSWORD}
|
||||
data:
|
||||
redis:
|
||||
host: ${REDIS_HOST:localhost}
|
||||
port: ${REDIS_PORT:6379}
|
||||
password: ${REDIS_PASSWORD}
|
||||
database: 0
|
||||
flyway:
|
||||
# test 允许 clean 后重建:每次流水线从一个空库开始,
|
||||
# **这样才能验证"所有迁移从 V1 依次跑到最新"是通的** ——
|
||||
# 而增量环境里永远验不到这件事(旧库已经是最新了)
|
||||
clean-disabled: false
|
||||
web:
|
||||
resources:
|
||||
# H5 产物在 CI 里是构建出来的;本地指向同一个相对路径
|
||||
static-locations: ${H5_STATIC_DIR:file:../frontend-mp/dist/build/h5/},classpath:/static/
|
||||
|
||||
logging:
|
||||
level:
|
||||
root: info
|
||||
cn.tianbaotong.fill: info
|
||||
# CI 日志里带 SQL 会淹没断言输出,所以 test 不打开 SQL 日志
|
||||
|
||||
fill:
|
||||
jwt:
|
||||
secret: ${XT_JWT_SECRET}
|
||||
crypto:
|
||||
aes-key: ${XT_AES_KEY}
|
||||
hmac-key: ${XT_HMAC_KEY}
|
||||
rsa:
|
||||
private-key: ${XT_RSA_PRIVATE_KEY}
|
||||
|
||||
storage:
|
||||
# ⚠️ test 用 local 是**刻意的**:CI 里没有对象存储,而且
|
||||
# "上传→取回→预览"这条链路用本地磁盘就能完整验证。
|
||||
# 真实验证七牛要走 staging(那需要真凭据),见 docs/09-部署与运维.md
|
||||
provider: ${XT_STORAGE_PROVIDER:local}
|
||||
keep-raw: ${XT_STORAGE_KEEP_RAW:false}
|
||||
self-check-on-startup: false
|
||||
|
||||
mini-program:
|
||||
app-id: ${WX_MP_APP_ID:}
|
||||
app-secret: ${WX_MP_APP_SECRET:}
|
||||
env-version: develop
|
||||
|
||||
mp-h5:
|
||||
app-id: ${WX_H5_APP_ID:}
|
||||
app-secret: ${WX_H5_APP_SECRET:}
|
||||
callback-url: ${WX_H5_CALLBACK:http://localhost:8080/api/v1/mp/h5/callback}
|
||||
h5-base-url: ${WX_H5_BASE_URL:http://localhost:8080}
|
||||
# ⚠️ test 允许打桩登录(E2E 里有 23 条断言靠它跑 H5 授权链路)。
|
||||
# 它只在**非生产**可用:代码里另有一道"profile 不含 prod"的判断。
|
||||
stub-enabled: ${WX_H5_STUB:true}
|
||||
|
||||
tenant-domain:
|
||||
enabled: ${XT_TENANT_DOMAIN_ENABLED:false}
|
||||
base: ${XT_TENANT_DOMAIN_BASE:localhost}
|
||||
scheme: http
|
||||
|
||||
# test 也开 Swagger:联调的人要用它照着调
|
||||
springdoc:
|
||||
api-docs:
|
||||
enabled: true
|
||||
swagger-ui:
|
||||
enabled: true
|
||||
Reference in New Issue
Block a user