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/15 text-emerald-50"
: flash.type === "error"
? "border-rose-500/60 bg-rose-500/15 text-rose-50"
: "border-sky-500/60 bg-sky-500/15 text-sky-50";
return (
);
};
const AdminLoginForm = ({
token,
onTokenChange,
onSubmit,
isSubmitting,
}: {
token: string;
onTokenChange: (value: string) => void;
onSubmit: () => void;
isSubmitting: boolean;
}) => {
return (
Админ-панель узла
Введите секретный токен ADMIN_API_TOKEN, чтобы получить доступ к мониторингу и управлению.
);
};
const MobileNav = ({ sections, currentPath }: { sections: AdminSection[]; currentPath: string }) => (
);
const DesktopNav = ({
sections,
currentPath,
}: {
sections: AdminSection[];
currentPath: string;
}) => {
return (
);
};
const AdminLayoutFrame = ({ children }: { children: ReactNode }) => {
const location = useLocation();
const currentPath = location.pathname;
return (
MY Admin
Мониторинг и управление узлом
);
};
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 ;
};