2ff17cd0c9
用户决定:后端不再对外发静态文件,学生端 H5 交给网关。两者**仍然同源**
(微信只给一个授权回调域名;H5 的接口地址取自 location.origin),
所以前端业务代码、微信后台、application-dev.yml 的 h5-base-url 都不用改。
后端
- 删掉 dev/test/prod 三份 profile 里的 spring.web.resources.static-locations
- 主配置加 spring.web.resources.add-mappings=false —— 用开关而不是"指到空目录":
后者会连带启用 Spring Boot 的默认位置(classpath:/static 等),谁丢个文件进去就悄悄对外可见
- StaticCacheHeaderFilter → ApiNoStoreFilter,只剩 /api/** 的 no-store
(静态资源的缓存策略整体归 nginx,避免"改一处漏一处")
- 补 NoHandlerFoundException → 404:关掉静态映射后未匹配路径不再经过
ResourceHttpRequestHandler,抛的是 NoHandlerFoundException,只处理
NoResourceFoundException 时访问 / 会变成 500「系统繁忙」并刷 ERROR 日志
deploy
- 新增 xt-nginx(80):发 frontend-mp/dist/build/h5 + /api 反代 8080(host.docker.internal,
Linux 靠 extra_hosts host-gateway),端口与产物目录走 .env 的 H5_PORT/H5_DIR
- 三条"删了就会出事"的都已带上理由:client_max_body_size 50m(默认 1m,超了回 413
而后端日志一个字都没有)、/assets 的 immutable 与 *.html 的 no-cache(§一·十 那个
"发版后学生看到旧页面"的坑)、健康检查写 127.0.0.1(容器里 localhost 先解析到 IPv6,
nginx 只听 IPv4 → 永远 starting 而首页是好的)
CI 与文档
- CI:学生端 H5 构建从"e2e 的输入"改为编译检查,不再留 artifact(e2e 不消费它)
- 同步 AGENTS.md / README.md / docs/09(启动步骤、CI 表、发布注意、排障表)/ docs/11
- 交接摘要新增 §一·四十三(含理由、验证数字、要人工做的一步与环境限制)
验证
- mvn clean package 通过(必须 clean:删掉的旧类会留在 target/classes 被组件扫描)
- 后端直连:/ 与 /assets/* → 404({"code":40001,"msg":"接口不存在"})、
/api/.../captcha → 200 + no-store、/doc.html → 302→200、/v3/api-docs 与 /actuator/health → 200
- 经 nginx(80):首页 200 + no-cache、/assets/*.js 200 + immutable、/nope.js 404(无回退)、
/api 200 + no-store、gzip 生效;2MB multipart 上传 → 401(与直连一致 ⇒ 未踩 1m 的坑)、
55MB → 413
- e2e(XT_BASE=http://localhost,全程走 nginx):e2e-m0.py 37/37、e2e-m3-h5-auth.py 24/24、
e2e-m2-scope.py 38/38
- verify-all.py --quick 6/6;check-test-profile.py:test profile 仍能起来
150 lines
5.7 KiB
YAML
150 lines
5.7 KiB
YAML
# ═══════════════════════════════════════════════════════════
|
||
# 生产环境(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://1Panel-postgresql-v7DP:5432/shenxue?stringtype=unspecified
|
||
username: shenxue
|
||
password: RC6Bhnmbz8mk4p4z
|
||
hikari:
|
||
# 生产把池子放大一些:导出打包是长事务,会占住连接
|
||
minimum-idle: 10
|
||
maximum-pool-size: 40
|
||
data:
|
||
redis:
|
||
host: 1Panel-redis-mIkk
|
||
port: 6379
|
||
password: redis_GWnh6E
|
||
database: 0
|
||
flyway:
|
||
# ⚠️⚠️ 生产**禁止 clean**。它是"清空整个库再重建",
|
||
# 在生产上执行等于把所有学校的填报数据删掉。
|
||
# 流水线里也不要用 flyway clean —— 见部署文档的回滚一节。
|
||
clean-disabled: true
|
||
# 迁移文件发布后不许改(改了校验失败)。这是保护:它保证
|
||
# "生产跑的迁移"与"仓库里的迁移"逐字节一致
|
||
validate-on-migrate: true
|
||
# ⚠️ 这里**没有** static-locations:H5 由 nginx/网关托管,不落在后端
|
||
# (2026-10 架构调整,见 application.yml 的 spring.web.resources.add-mappings)。
|
||
# 发布时要注意的是**换了个东西发**:H5 产物由部署流程放到 nginx 的站点目录,
|
||
# 仍然要"和后端一起发" —— 前后端不同步还是会出现"页面调用了还不存在的接口"。
|
||
#
|
||
# 历史(留着是有用的):它曾经是 `file:/opt/xt/h5/`,由本服务直接对外发 H5。
|
||
|
||
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
|