feat(admin): 独立后台壳、站点分析与聊天监管,公开站拆出主题切换与访问埋点

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-09-17 04:00:14 +08:00
parent cda5fad2e5
commit 9a55c2f6f2
41 changed files with 5655 additions and 1810 deletions

View File

@@ -0,0 +1,72 @@
"use client";
import { useEffect, useState } from "react";
import { usePathname } from "next/navigation";
import Header from "@/components/Header";
import Footer from "@/components/Footer";
import type { User } from "@/lib/api";
function pathIsAdmin(pathname: string | null) {
if (!pathname) return false;
return pathname === "/admin" || pathname.startsWith("/admin/");
}
/**
* 公开站 chrome 闸门:/admin 不渲染 Header/Footer/1440 main。
* 首屏用 middleware 的 x-pathname 初值保证 hydration 一致;
* 挂载后同步跟 usePathname(渲染期判定,避免 useEffect 晚一拍闪框)。
*/
export default function SiteChrome({
initialIsAdmin,
user,
unread,
chatUnread,
theme,
siteName,
allowRegister,
children,
}: {
initialIsAdmin: boolean;
user: User | null;
unread: number;
chatUnread: number;
theme: "light" | "dark";
siteName: string;
allowRegister: boolean;
children: React.ReactNode;
}) {
const pathname = usePathname();
const [mounted, setMounted] = useState(false);
useEffect(() => {
setMounted(true);
}, []);
// 首屏跟 SSR;之后在渲染期直接读 pathname,软导航无 Header 闪动
const isAdmin = mounted ? pathIsAdmin(pathname) : initialIsAdmin;
if (isAdmin) {
return <>{children}</>;
}
return (
<>
<Header
initialUser={user}
initialUnread={unread}
initialChatUnread={chatUnread}
initialTheme={theme}
siteName={siteName}
allowRegister={allowRegister}
/>
<main
id="main"
tabIndex={-1}
className="flex-1 w-full max-w-[1440px] mx-auto px-4 sm:px-6 py-7 sm:py-9 outline-none"
>
{children}
</main>
<Footer siteName={siteName} />
</>
);
}