From c124e082ae01afbc8504e2c30b82b36f9cf3c4b7 Mon Sep 17 00:00:00 2001 From: Andrey Epifancev Date: Wed, 14 Oct 2026 20:12:35 +0400 Subject: [PATCH] =?UTF-8?q?fix(web):=20=D1=82=D0=BE=D0=BA=D0=B5=D0=BD=20?= =?UTF-8?q?=D1=81=20=D0=BD=D0=B5=D0=B4=D0=BE=D0=BF=D1=83=D1=81=D1=82=D0=B8?= =?UTF-8?q?=D0=BC=D1=8B=D0=BC=D0=B8=20=D1=81=D0=B8=D0=BC=D0=B2=D0=BE=D0=BB?= =?UTF-8?q?=D0=B0=D0=BC=D0=B8=20=E2=80=94=20=D1=8D=D0=BA=D1=80=D0=B0=D0=BD?= =?UTF-8?q?=20=D0=B2=D1=85=D0=BE=D0=B4=D0=B0,=20=D0=B0=20=D0=BD=D0=B5=20?= =?UTF-8?q?=C2=AB=D1=85=D0=B0=D0=B1=20=D0=BD=D0=B5=20=D0=BE=D1=82=D0=B2?= =?UTF-8?q?=D0=B5=D1=87=D0=B0=D0=B5=D1=82=C2=BB=20(#21)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Сохранённый в localStorage токен с кириллицей, пробелом или переводом строки давал TypeError у fetch, который превращался в HubDownError. Теперь call() проверяет TOKEN_RE до запроса и бросает AuthError. Co-Authored-By: Claude Haiku 5.5 --- web/src/api/client.test.ts | 7 +++++++ web/src/api/client.ts | 4 +++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/web/src/api/client.test.ts b/web/src/api/client.test.ts index 4417154..9f8070e 100644 --- a/web/src/api/client.test.ts +++ b/web/src/api/client.test.ts @@ -41,6 +41,13 @@ describe("api", () => { expect(fetch).not.toHaveBeenCalled(); }); + it("токен с недопустимыми символами — AuthError без запроса", async () => { + setToken("ток ен"); + const fetch = mockFetch(() => json(200, {})); + await expect(api.get("/board")).rejects.toBeInstanceOf(AuthError); + expect(fetch).not.toHaveBeenCalled(); + }); + it("checkToken проверяет переданный токен, не сохранённый", async () => { const fetch = mockFetch(() => json(200, { kind_labels: {} })); await checkToken("candidate"); diff --git a/web/src/api/client.ts b/web/src/api/client.ts index 9c796d5..8ea73d6 100644 --- a/web/src/api/client.ts +++ b/web/src/api/client.ts @@ -1,6 +1,6 @@ // единственный клиент HTTP API хаба: тот же хост, клиентский токен в Authorization, ответы — JSON. // Ошибки: AuthError — 401 или нет токена; HubDownError — сеть или 5xx; ApiError — прочие 4xx с {error, detail} -import { getToken } from "../auth/token.ts"; +import { getToken, TOKEN_RE } from "../auth/token.ts"; import type { HubSettings } from "./types.ts"; export class AuthError extends Error {} @@ -29,6 +29,8 @@ function withParams(path: string, params?: Params): string { async function call(method: string, path: string, body?: unknown, token = getToken()): Promise { if (!token) throw new AuthError("нет токена"); + // токен с недопустимыми символами (например, оставленный прежней доской) — сразу на экран входа, а не «хаб не отвечает» + if (!TOKEN_RE.test(token)) throw new AuthError("недопустимые символы в токене"); const headers: Record = { Authorization: "Bearer " + token, Accept: "application/json" }; if (body !== undefined) headers["Content-Type"] = "application/json; charset=utf-8"; let resp: Response;