feat: 站点访问统计与可配置侧边栏组件

- 新增访问统计:visitstats 服务与查询、visit_capture 中间件、爬虫/来源识别、管理端 analytics 页面
- 新增侧边栏:sidebar 服务与 handler、管理端 sidebar 配置页、前端侧边栏组件
- 新增 widget 嵌入代码生成(widgetCode)与 widgetApi
- 遥测逻辑迁移至 handler 层,删除 service/telemetry
- env 示例补充 Docker 部署可信代理 IP 说明
This commit is contained in:
2026-09-24 18:53:45 +08:00
parent 3e55b5d230
commit 7b327bc8cc
60 changed files with 6539 additions and 572 deletions

View File

@@ -0,0 +1,46 @@
import type { Metadata } from "next";
import { Suspense } from "react";
import { cookies } from "next/headers";
import SidebarAdmin from "./SidebarAdmin";
import { authCookieHeader } from "@/lib/cookies";
import {
fetchAdminSidebar,
type SidebarConfig,
type SidebarWidgetRecord,
} from "@/lib/api";
import { getMeCached } from "@/lib/serverData";
import { isSuperOrOwner } from "@/lib/roles";
import Forbidden from "../Forbidden";
export const metadata: Metadata = {
title: "侧栏管理",
};
export default async function AdminSidebarPage() {
const cookie = authCookieHeader(await cookies());
const me = await getMeCached(cookie || undefined);
if (!me.user || !isSuperOrOwner(me.user.role)) {
return <Forbidden text="侧栏管理仅超级管理员及以上角色可操作。" />;
}
let config: SidebarConfig | null = null;
let widgets: SidebarWidgetRecord[] = [];
let initError = "";
try {
const res = await fetchAdminSidebar(cookie || undefined);
config = res.config;
widgets = res.widgets ?? [];
} catch (e) {
initError = e instanceof Error ? e.message : "加载失败";
}
return (
<Suspense fallback={<p className="meta px-1 py-6">加载中…</p>}>
<SidebarAdmin
initialConfig={config}
initialWidgets={widgets}
initError={initError}
/>
</Suspense>
);
}