gendesign/frontend/src/lib/useMe.ts

66 lines
2.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use client";
/**
* `useMe()` — TanStack-хук для GET /api/v1/me.
*
* Backend возвращает `UserScope` (см. `backend/app/core/auth.py`):
* { username, role: "admin"|"pilot"|"analyst", allowed_paths: string[], deny_paths: string[] }
*
* Failure modes:
* - 401 — нет X-Authenticated-User (локально без Caddy basic_auth). Фронт
* трактует это как «dev-режим» и в этом случае route-guard ничего не
* блокирует (см. RouteGuard).
* - 403 — header есть, но юзер не в `auth/roles.yaml`. Фронт показывает
* полноэкранный NoAccessScreen и больше ничего.
*
* Кешируем «вечно» (`staleTime: Infinity`, без рефетча): роль не меняется
* в рамках сессии. Если basic_auth обновится — юзер перезагрузит страницу.
*/
import { useQuery } from "@tanstack/react-query";
import { apiFetchWithStatus, HTTPError } from "@/lib/api";
// Error type для useQuery: либо `HTTPError` (от apiFetchWithStatus при 4xx/5xx),
// либо обычный `Error` (network fail, fetch reject). RouteGuard / TopNav проверяют
// статус через `error instanceof HTTPError` — это работает в runtime независимо
// от type-level decl.
// Должен совпадать с `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;
role: Role;
allowed_paths: string[];
deny_paths: string[];
}
export const ME_QUERY_KEY = ["auth", "me"] as const;
async function fetchMe(): Promise<UserScope> {
const { body } = await apiFetchWithStatus<UserScope>("/api/v1/me");
return body;
}
export function useMe() {
return useQuery<UserScope, Error>({
queryKey: ME_QUERY_KEY,
queryFn: fetchMe,
staleTime: Infinity,
gcTime: Infinity,
refetchOnWindowFocus: false,
refetchOnReconnect: false,
refetchOnMount: false,
// 401 (dev без Caddy) и 403 (unknown user) — финальные состояния,
// ретраить бесполезно. Ретраим только сетевые сбои.
retry: (failureCount, error) => {
if (error instanceof HTTPError && (error.status === 401 || error.status === 403)) {
return false;
}
return failureCount < 2;
},
});
}