import { ReactNode, useCallback, useEffect, useMemo, useState } from "react"; import { NavLink, Navigate, Outlet, useLocation } from "react-router-dom"; import clsx from "clsx"; import { useQueryClient } from "react-query"; import { Routes } from "~/app/router/constants"; import { useAdminLogin, useAdminLogout, useAdminOverview, } from "~/shared/services/admin"; import { clearAdminAuth, getAdminAuthSnapshot } from "~/shared/libs/admin-auth"; import { isUnauthorizedError } from "~/shared/services/admin"; import { ADMIN_SECTIONS, DEFAULT_ADMIN_SECTION, type AdminSection } from "./config"; import { AdminContext } from "./context"; import type { AuthState, FlashMessage } from "./types"; const initialAuthState: AuthState = getAdminAuthSnapshot().token ? "checking" : "unauthorized"; const resolveErrorMessage = (error: unknown) => { if (error && typeof error === "object" && "message" in error && typeof (error as any).message === "string") { return (error as any).message as string; } return "Неизвестная ошибка"; }; const AdminFlash = ({ flash, onClose }: { flash: FlashMessage | null; onClose: () => void }) => { if (!flash) { return null; } const tone = flash.type === "success" ? "border-emerald-500/60 bg-emerald-500/10 text-emerald-100" : flash.type === "error" ? "border-rose-500/60 bg-rose-500/10 text-rose-100" : "border-sky-500/60 bg-sky-500/10 text-sky-100"; return (
{flash.message}
); }; const AdminLoginForm = ({ token, onTokenChange, onSubmit, isSubmitting, }: { token: string; onTokenChange: (value: string) => void; onSubmit: () => void; isSubmitting: boolean; }) => { return (

Админ-панель узла

Введите секретный токен ADMIN_API_TOKEN, чтобы получить доступ к мониторингу и управлению.

{ event.preventDefault(); onSubmit(); }} >
Cookie max-age {Math.round(172800 / 3600)} ч
); }; const MobileNav = ({ sections, currentPath }: { sections: AdminSection[]; currentPath: string }) => ( ); const DesktopNav = ({ sections, currentPath, }: { sections: AdminSection[]; currentPath: string; }) => { return ( ); }; const AdminLayoutFrame = ({ children, flash, onFlashClose }: { children: ReactNode; flash: FlashMessage | null; onFlashClose: () => void }) => { const location = useLocation(); const currentPath = location.pathname; return (

MY Admin

Мониторинг и управление узлом

{children}
); }; export const AdminShell = () => { const queryClient = useQueryClient(); const [authState, setAuthState] = useState(initialAuthState); const [token, setToken] = useState(""); const [flash, setFlash] = useState(null); useEffect(() => { if (!flash) { return; } const timeout = setTimeout(() => setFlash(null), 6000); return () => clearTimeout(timeout); }, [flash]); useEffect(() => { if (authState === "unauthorized") { queryClient.removeQueries({ predicate: (query) => Array.isArray(query.queryKey) && query.queryKey[0] === "admin", }); } }, [authState, queryClient]); const pushFlash = useCallback((message: FlashMessage) => { setFlash(message); }, []); const clearFlash = useCallback(() => { setFlash(null); }, []); const handleRequestError = useCallback( (error: unknown, fallbackMessage: string) => { if (isUnauthorizedError(error)) { clearAdminAuth(); setAuthState("unauthorized"); pushFlash({ type: "info", message: "Сессия истекла. Введите админ-токен заново." }); return; } const tail = resolveErrorMessage(error); pushFlash({ type: "error", message: `${fallbackMessage}: ${tail}`, }); }, [pushFlash], ); const invalidateAll = useCallback(async () => { await queryClient.invalidateQueries({ predicate: (query) => Array.isArray(query.queryKey) && query.queryKey[0] === "admin", }); }, [queryClient]); useAdminOverview({ enabled: authState === "checking", retry: false, onSuccess: () => { setAuthState("authorized"); }, onError: (error) => { handleRequestError(error, "Не удалось проверить админ-сессию"); }, }); const loginMutation = useAdminLogin({ onSuccess: async () => { setAuthState("authorized"); setToken(""); pushFlash({ type: "success", message: "Админ-сессия активирована" }); await invalidateAll(); }, onError: (error) => { if (isUnauthorizedError(error)) { clearAdminAuth(); pushFlash({ type: "error", message: "Неверный токен" }); return; } handleRequestError(error, "Ошибка входа"); }, }); const logoutMutation = useAdminLogout({ onSuccess: async () => { setAuthState("unauthorized"); pushFlash({ type: "info", message: "Сессия завершена" }); await invalidateAll(); }, onError: (error) => { handleRequestError(error, "Ошибка выхода"); }, }); const contextValue = useMemo( () => ({ authState, isAuthorized: authState === "authorized", pushFlash, clearFlash, handleRequestError, invalidateAll, }), [authState, pushFlash, clearFlash, handleRequestError, invalidateAll], ); return ( {authState !== "authorized" ? (
{ if (!token) { pushFlash({ type: "info", message: "Введите токен" }); return; } loginMutation.mutate({ secret: token }); }} isSubmitting={loginMutation.isLoading} />
) : (
Авторизован
)}
); }; export const AdminPage = AdminShell; export const AdminIndexRedirect = () => { return ; };