refactor(h5)!: H5 改由 nginx 托管,后端退回纯 API
用户决定:后端不再对外发静态文件,学生端 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 仍能起来
This commit is contained in:
@@ -5,6 +5,10 @@
|
||||
# copy .env.example .env (首次)
|
||||
# docker compose up -d
|
||||
#
|
||||
# ⚠️ nginx 挂的是 H5 **构建产物**,所以第一次起它之前要先:
|
||||
# cd frontend-mp && pnpm build:h5
|
||||
# (产物不入库;目录不存在时 docker 会建一个空目录 → 页面 404,容器却一切正常)
|
||||
#
|
||||
# ⚠️ 容器名统一加 xt- 前缀,端口刻意避开 55432 / 6379
|
||||
# (本机已有其他项目的 pg-verify 与 redis-local 在跑,不要动它们)
|
||||
#
|
||||
@@ -70,6 +74,37 @@ services:
|
||||
retries: 12
|
||||
start_period: 5s
|
||||
|
||||
# ── nginx:学生端 H5 站点 + /api 反代 ──────────────
|
||||
# 2026-10 起 H5 **不再由后端托管**(原来是 spring.web.resources.static-locations),
|
||||
# 改由本服务对外提供,后端退回纯 API。两者仍然**同源**,理由见 nginx/xt.conf 头部。
|
||||
nginx:
|
||||
image: nginx:1.27-alpine
|
||||
container_name: xt-nginx
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "${H5_PORT:-80}:80"
|
||||
volumes:
|
||||
# H5 构建产物(**产物不入库**):先在 frontend-mp 里跑 `pnpm build:h5`。
|
||||
# ⚠️ 目录不存在时 docker 会**建一个空目录**,于是页面 404 而容器一切正常
|
||||
# (`docker compose ps` 是 healthy、nginx 日志里也没有错)—— 先 build 再起。
|
||||
- ${H5_DIR:-../frontend-mp/dist/build/h5}:/usr/share/nginx/html:ro
|
||||
- ./nginx/xt.conf:/etc/nginx/conf.d/default.conf:ro
|
||||
extra_hosts:
|
||||
# 后端是**宿主机上的 jar**,不在这个 compose 里。
|
||||
# Docker Desktop 自带 host.docker.internal;Linux 上必须有下面这行才有这个名字
|
||||
- "host.docker.internal:host-gateway"
|
||||
healthcheck:
|
||||
# 探的是"首页拿不拿得到",而不是"进程在不在" ——
|
||||
# 挂进来的产物目录是空的时候,前者才有意义。
|
||||
# ⚠️ 必须写 **127.0.0.1**,不能写 localhost:容器里 localhost 会先解析到 IPv6
|
||||
# ::1,而 nginx 的 `listen 80` 只监听 IPv4 → wget 报 Connection refused、
|
||||
# 健康检查永远停在 unhealthy/starting(首页从宿主机看却是好的,极易误判)。
|
||||
test: ["CMD-SHELL", "wget -q --spider http://127.0.0.1/ || exit 1"]
|
||||
interval: 10s
|
||||
timeout: 3s
|
||||
retries: 6
|
||||
start_period: 5s
|
||||
|
||||
volumes:
|
||||
pg-data:
|
||||
redis-data:
|
||||
|
||||
Reference in New Issue
Block a user