From b15678b3f62944a2c92d19a11060e925bcba992d Mon Sep 17 00:00:00 2001 From: shangfangjian Date: Tue, 22 Sep 2026 10:44:12 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20http=20client=20URL=20=E6=9E=84=E9=80=A0?= =?UTF-8?q?=E5=BF=85=E9=A1=BB=E7=BB=9F=E4=B8=80=E5=8A=A0=20/api/v1=20?= =?UTF-8?q?=E5=89=8D=E7=BC=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 公开页调用 /matches(无 /api/v1 前缀),被 nginx 当 SPA 回退返回 index.html, 前端 JSON 解析报 "Unexpected token <"。 修复:build_url 函数确保所有相对路径统一走 /api/v1, 已含前缀或绝对 URL 保持不变。 首页加载比赛恢复正常。 --- frontend/src/lib/http.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/frontend/src/lib/http.ts b/frontend/src/lib/http.ts index 29217ff..251fdbc 100644 --- a/frontend/src/lib/http.ts +++ b/frontend/src/lib/http.ts @@ -17,6 +17,14 @@ export const UNAUTHORIZED_EVENT = 'profeto:unauthorized' const API_BASE = '/api/v1' const DEFAULT_TIMEOUT = 30_000 +/** 所有 API 路径统一走 /api/v1,避免浏览器直接请求 /matches 被 nginx 当 SPA 回退 */ +function build_url(path: string): string { + if (path.startsWith('http')) return path + if (path.startsWith(API_BASE)) return path + if (path.startsWith('/')) return `${API_BASE}${path}` + return `${API_BASE}/${path}` +} + export class ApiError extends Error { constructor( message: string, @@ -37,7 +45,7 @@ interface RequestOptions { } async function request(path: string, options: RequestOptions = {}): Promise { - const url = path.startsWith('http') ? path : path.startsWith('/') ? path : `${API_BASE}${path}` + const url = build_url(path) const { timeoutMs = DEFAULT_TIMEOUT, skipAuthHandling, signal } = options const controller = new AbortController()