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

@@ -1,14 +1,15 @@
import type { Metadata } from "next";
import { cookies } from "next/headers";
import { cookies, headers } from "next/headers";
import { Plus_Jakarta_Sans, Noto_Sans_SC, JetBrains_Mono } from "next/font/google";
import "./globals.css";
import Header from "@/components/Header";
import Footer from "@/components/Footer";
import SiteChrome from "@/components/SiteChrome";
import Toaster from "@/components/Toaster";
import TipHost from "@/components/TipHost";
import AccountGuard from "@/components/AccountGuard";
import ThemeSync from "@/components/ThemeSync";
import RealtimeProvider from "@/components/RealtimeProvider";
import VisitBeacon from "@/components/VisitBeacon";
import PostLinkProvider from "@/components/PostLinkProvider";
import CodeFoldProvider from "@/components/markdown/CodeFoldProvider";
import MotionProvider from "@/components/MotionProvider";
import CommentsPolicyProvider from "@/components/CommentsPolicyProvider";
@@ -65,6 +66,11 @@ export default async function RootLayout({
children: React.ReactNode;
}) {
const cookieStore = await cookies();
const headerStore = await headers();
// middleware 写入 x-pathname;供 SiteChrome SSR 初值(软导航靠客户端 pathname)
const pathname = headerStore.get("x-pathname") || "";
const isAdmin = pathname === "/admin" || pathname.startsWith("/admin/");
// 浅色优先的设计系统,仅在显式选择暗色时挂 .dark
const theme = cookieStore.get("j13-theme")?.value === "dark" ? "dark" : "light";
@@ -113,35 +119,33 @@ export default async function RootLayout({
<a href="#main" className="skip-link">
跳到主要内容
</a>
<Header
initialUser={user}
initialUnread={unread}
initialChatUnread={chatUnread}
initialTheme={theme}
siteName={settings.site_name}
allowRegister={settings.allow_register}
/>
<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"
>
<MotionProvider initial={motionCfg}>
<CommentsPolicyProvider allowComments={settings.allow_comments !== false}>
<MotionProvider initial={motionCfg}>
<CommentsPolicyProvider allowComments={settings.allow_comments !== false}>
<PostLinkProvider openInNewTab={settings.post_link_new_tab !== false}>
<CodeFoldProvider
initialEnabled={settings.code_block_auto_fold}
initialFoldLines={settings.code_block_fold_lines}
>
{children}
<SiteChrome
initialIsAdmin={isAdmin}
user={user}
unread={unread}
chatUnread={chatUnread}
theme={theme}
siteName={settings.site_name}
allowRegister={settings.allow_register}
>
{children}
</SiteChrome>
</CodeFoldProvider>
</CommentsPolicyProvider>
</MotionProvider>
</main>
<Footer siteName={settings.site_name} />
</PostLinkProvider>
</CommentsPolicyProvider>
</MotionProvider>
<Toaster />
<TipHost />
<AccountGuard />
<ThemeSync initialAccent={accent || ""} />
<VisitBeacon />
<RealtimeProvider userId={user ? user.id : null} />
</body>
</html>