From d8a2ad78547cbfc99d27295d95f344b4c9192f83 Mon Sep 17 00:00:00 2001 From: Light1YT Date: Sat, 13 Jun 2026 15:03:15 +0500 Subject: [PATCH] =?UTF-8?q?fix(auth):=20=D0=BE=D1=82=D0=BE=D0=B1=D1=80?= =?UTF-8?q?=D0=B0=D0=B7=D0=B8=D1=82=D1=8C=20=D1=80=D0=BE=D0=BB=D1=8C=20ana?= =?UTF-8?q?lyst=20=D0=B2=20UserMenu,=20=D1=80=D0=B0=D1=81=D1=88=D0=B8?= =?UTF-8?q?=D1=80=D0=B8=D1=82=D1=8C=20Role=20union=20=D0=B2=20useMe=20(#12?= =?UTF-8?q?37)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/components/auth/UserMenu.tsx | 17 ++++++++++++++--- frontend/src/lib/useMe.ts | 7 +++++-- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/frontend/src/components/auth/UserMenu.tsx b/frontend/src/components/auth/UserMenu.tsx index 2a37dbc3..abea2f62 100644 --- a/frontend/src/components/auth/UserMenu.tsx +++ b/frontend/src/components/auth/UserMenu.tsx @@ -5,7 +5,7 @@ * * Avatar-кнопка с инициалом из username + dropdown: * - текущий username - * - role badge («Админ» / «Пилот») + * - role badge («Админ» / «Пилот» / «Аналитик») * - кнопка «Выйти» — invalidate Caddy basic_auth cached creds в browser * * Используется в `TopNav.rightSlot`. Если /me грузится или 401 (dev без @@ -24,7 +24,18 @@ import { useEffect, useRef, useState } from "react"; import { LogOut } from "lucide-react"; -import { useMe } from "@/lib/useMe"; +import { useMe, type Role } from "@/lib/useMe"; + +/** + * Локализация ролей для badge. Должен покрывать все варианты `Role` из + * `lib/useMe.ts` — TypeScript-проверка через `Record` + * сломает build, если backend добавит новую роль без обновления map. + */ +const ROLE_LABELS: Record = { + admin: "Админ", + pilot: "Пилот", + analyst: "Аналитик", +}; /** * Logout — invalidate basic_auth cache + reload. @@ -89,7 +100,7 @@ export function UserMenu() { if (isLoading || error || !data) return null; const initial = data.username[0]?.toUpperCase() ?? "?"; - const roleLabel = data.role === "admin" ? "Админ" : "Пилот"; + const roleLabel = ROLE_LABELS[data.role]; return (
diff --git a/frontend/src/lib/useMe.ts b/frontend/src/lib/useMe.ts index 951edb04..e0a90e30 100644 --- a/frontend/src/lib/useMe.ts +++ b/frontend/src/lib/useMe.ts @@ -4,7 +4,7 @@ * `useMe()` — TanStack-хук для GET /api/v1/me. * * Backend возвращает `UserScope` (см. `backend/app/core/auth.py`): - * { username, role: "admin"|"pilot", allowed_paths: string[], deny_paths: string[] } + * { username, role: "admin"|"pilot"|"analyst", allowed_paths: string[], deny_paths: string[] } * * Failure modes: * - 401 — нет X-Authenticated-User (локально без Caddy basic_auth). Фронт @@ -26,7 +26,10 @@ import { apiFetchWithStatus, HTTPError } from "@/lib/api"; // статус через `error instanceof HTTPError` — это работает в runtime независимо // от type-level decl. -export type Role = "admin" | "pilot"; +// Должен совпадать с `Role` в `backend/app/core/auth.py` (Literal["admin", "pilot", "analyst"]). +// Источник правды — `auth/roles.yaml`; при добавлении новой роли расширить тут и +// `ROLE_LABELS` в `components/auth/UserMenu.tsx`. +export type Role = "admin" | "pilot" | "analyst"; export interface UserScope { username: string;