- Adapter اکانتمحور با چرخش round-robin و بنچشدن خودکار روی ۴۲۹/۴۰۱ - ورود ادمین از طریق URL بازگشتی گوگل (PKCE) و ذخیرهٔ رمزنگاریشدهٔ refresh token - صفحهٔ مدیریت اکانتهای agy در پنل ادمین به همراه تست و فعال/غیرفعالسازی - مهاجرت Alembic b1c2d3e4f5a6 برای ستونهای اکانت agy - seed همیشه کاتالوگ agy را میسازد و فقط با AI_DEFAULT_PROVIDER=agy مسیریابی میشود
72 lines
2.3 KiB
TypeScript
72 lines
2.3 KiB
TypeScript
"use client";
|
|
|
|
import Link from "next/link";
|
|
import { usePathname } from "next/navigation";
|
|
import * as React from "react";
|
|
import { EmptyState, LoadingState } from "@agy/ui";
|
|
import { useSession } from "@/lib/session";
|
|
|
|
/**
|
|
* Guard + chrome for the platform-owner console.
|
|
*
|
|
* The API enforces ownership on every endpoint; this layout only avoids
|
|
* rendering a console that would 401 on every request. It is a convenience,
|
|
* never the security boundary.
|
|
*/
|
|
|
|
const SECTIONS = [
|
|
{ href: "/admin", label: "نمای کلی" },
|
|
{ href: "/admin/agy-accounts", label: "اکانتهای agy" },
|
|
{ href: "/admin/providers", label: "ارائهدهندهها" },
|
|
{ href: "/admin/models", label: "مدلها" },
|
|
{ href: "/admin/routing", label: "مسیریابی" },
|
|
{ href: "/admin/rate-cards", label: "تعرفهها" },
|
|
{ href: "/admin/usage", label: "مصرف سراسری" },
|
|
{ href: "/admin/organizations", label: "سازمانها" },
|
|
{ href: "/admin/errors", label: "کنسول خطا" },
|
|
] as const;
|
|
|
|
export default function AdminLayout({ children }: { children: React.ReactNode }) {
|
|
const { user, ready } = useSession();
|
|
const pathname = usePathname();
|
|
|
|
if (!ready) return <LoadingState label="در حال بررسی دسترسی…" />;
|
|
|
|
if (!user?.is_platform_owner) {
|
|
return (
|
|
<EmptyState
|
|
title="دسترسی محدود"
|
|
description="این بخش فقط برای مالک سامانه قابل دسترسی است."
|
|
/>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="anim-stagger flex flex-col gap-5">
|
|
<nav className="flex flex-wrap gap-1 border-b border-surface-border">
|
|
{SECTIONS.map((section) => {
|
|
const active =
|
|
section.href === "/admin"
|
|
? pathname === "/admin"
|
|
: pathname.startsWith(section.href);
|
|
return (
|
|
<Link
|
|
key={section.href}
|
|
href={section.href}
|
|
className={
|
|
"border-b-2 px-3 py-2 text-sm transition-colors " +
|
|
(active
|
|
? "border-accent text-slate-100"
|
|
: "border-transparent text-slate-400 hover:text-slate-200")
|
|
}
|
|
>
|
|
{section.label}
|
|
</Link>
|
|
);
|
|
})}
|
|
</nav>
|
|
{children}
|
|
</div>
|
|
);
|
|
}
|