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 仍能起来
60 lines
2.9 KiB
TypeScript
60 lines
2.9 KiB
TypeScript
/**
|
||
* API 地址。
|
||
*
|
||
* ⚠️ **H5 与小程序必须用不同的策略**,写死一个值一定会在某一边出错:
|
||
*
|
||
* · **H5**:与接口**同域**(H5 由 nginx 托管、`/api` 由同一个 nginx 反代到后端 8080,
|
||
* 见 deploy/nginx/xt.conf),所以用**相对路径(空串)**。这样:
|
||
* ① 完全不经过 CORS —— 换域名也不用改配置;
|
||
* ② 不会出现"手机能从 A 域名加载页面、却向后端 B 域名发请求"这种毛病。
|
||
* ⚠️ 曾经的写法是硬编码 `http://localhost:8080`,于是从公网域名打开时
|
||
* 变成了跨域请求 → 被 CORS 拦 → 页面显示"网络请求失败",
|
||
* 而**从本机打开却是好的**。这个 bug 只有走真实穿透/真机才会暴露。
|
||
*
|
||
* · **小程序**:没有"同域"这个概念,必须写绝对地址。
|
||
* 开发者工具里可以用 `http://localhost:8080`(需勾"不校验合法域名");
|
||
* 真机要用局域网 IP;正式版必须是 HTTPS + 已在小程序后台配好的域名。
|
||
*
|
||
* 用 uni-app 的条件编译区分两端,而不是运行期判断 —— 后者会把两套地址都打进包里。
|
||
*/
|
||
// #ifdef H5
|
||
/**
|
||
* H5:优先取 `VITE_API_BASE`,没有才用**同源的** `location.origin`。
|
||
*
|
||
* ⚠️ 为什么要有 `VITE_API_BASE` 这一档:
|
||
*
|
||
* `location.origin` 是"与页面同源",这在**由 nginx 托管构建产物**时是对的
|
||
* (`http://localhost` 上页面与接口同源;2026-10 之前是后端 8080 托管,现在那个端口只有 /api)。
|
||
* 但 `pnpm dev:h5` 起的是一个**独立的 vite dev server**(另一个端口)——
|
||
* 那时 `location.origin` 指的是 dev server 自己,接口就打到 dev server 上去了,
|
||
* 于是只能"build 一次再看",改一行要等一次完整构建。
|
||
*
|
||
* 所以 dev server 下把这个值设成**空串**(见 `.env.development`),
|
||
* 让请求走相对路径 `/api/...`,再由 vite 的 `server.proxy` 转发到后端。
|
||
*
|
||
* ⚠️ 另外:`uni.request` **不能**传相对路径(实测会 `request:fail`,
|
||
* 而同样地址用原生 XHR/fetch 是 200)。所以空串时补上 `location.origin`,
|
||
* 保持"绝对地址"这个前提不变 —— 只是那个 origin 由代理兜住。
|
||
*/
|
||
const VITE_API_BASE = (import.meta.env?.VITE_API_BASE ?? '') as string
|
||
export const API_BASE_URL =
|
||
VITE_API_BASE !== '' ? VITE_API_BASE
|
||
: (typeof location !== 'undefined' ? location.origin : '')
|
||
// #endif
|
||
|
||
// #ifndef H5
|
||
export const API_BASE_URL = 'http://localhost:8080'
|
||
// #endif
|
||
|
||
/** 主题色(与 PC 管理端同一套腾讯云 TDesign 色板,两端视觉要一致)。 */
|
||
export const THEME = {
|
||
primary: '#0052D9',
|
||
success: '#00A870',
|
||
warning: '#ED7B2F',
|
||
danger: '#E34D59',
|
||
pageBg: '#F3F3F3',
|
||
textPrimary: '#000000E6',
|
||
textSecondary: '#00000099',
|
||
textPlaceholder: '#0000004D'
|
||
} as const
|