feat: add traffic analytics configuration (Umami/GA/Custom)

Add traffic analytics support to the admin panel with three provider
options:
- Umami (open-source, self-hosted) — script URL + website ID
- Google Analytics 4 — Measurement ID (G-XXXXXXXXXX)
- Custom — any third-party analytics script (百度统计, Plausible, 51la, etc.)

Changes:
- admin.types.ts: Add AnalyticsEnabled, AnalyticsProvider,
  AnalyticsScriptUrl, AnalyticsWebsiteId, AnalyticsCustomScript to
  SiteConfig interface
- config.ts: Add default values in getInitConfig() and configSelfCheck()
- api/admin/site/route.ts: Add analytics fields to destructuring, type
  validation, and SiteConfig update
- admin/page.tsx: Add SiteConfig interface fields, state initialization,
  config sync, and analytics configuration UI with provider-specific
  input fields
- layout.tsx: Inject analytics scripts into <head> based on provider
  type (Umami: async script with data-website-id; GA: gtag.js + config;
  Custom: dangerouslySetInnerHTML)
This commit is contained in:
mtvpls-turso
2026-07-11 18:47:11 +00:00
parent e624ffb087
commit d98f5082a5
5 changed files with 269 additions and 1 deletions
+42
View File
@@ -115,6 +115,11 @@ export default async function RootLayout({
let liveEnabled = true;
let webLiveEnabled = false;
let customAdFilterVersion = 0;
let analyticsEnabled = false;
let analyticsProvider: 'umami' | 'google' | 'custom' = 'umami';
let analyticsScriptUrl = '';
let analyticsWebsiteId = '';
let analyticsCustomScript = '';
let musicFeatureEnabled = false;
let suwayomiEnabled = false;
let booksEnabled =
@@ -202,6 +207,12 @@ export default async function RootLayout({
webLiveEnabled = config.WebLiveEnabled ?? false;
// 自定义去广告代码版本号
customAdFilterVersion = config.SiteConfig?.CustomAdFilterVersion || 0;
// 流量统计配置
analyticsEnabled = config.SiteConfig?.AnalyticsEnabled || false;
analyticsProvider = config.SiteConfig?.AnalyticsProvider || 'umami';
analyticsScriptUrl = config.SiteConfig?.AnalyticsScriptUrl || '';
analyticsWebsiteId = config.SiteConfig?.AnalyticsWebsiteId || '';
analyticsCustomScript = config.SiteConfig?.AnalyticsCustomScript || '';
// 音乐功能配置
musicFeatureEnabled = config.MusicConfig?.Enabled || false;
musicProxyEnabled = config.MusicConfig?.ProxyEnabled ?? true;
@@ -335,6 +346,37 @@ export default async function RootLayout({
__html: `window.RUNTIME_CONFIG = ${JSON.stringify(runtimeConfig)};`,
}}
/>
{/* 流量统计脚本 */}
{analyticsEnabled && analyticsProvider === 'umami' && analyticsScriptUrl && (
<>
{/* eslint-disable-next-line @next/next/no-sync-scripts */}
<script
async
defer
data-website-id={analyticsWebsiteId}
src={analyticsScriptUrl}
/>
</>
)}
{analyticsEnabled && analyticsProvider === 'google' && analyticsWebsiteId && (
<>
{/* eslint-disable-next-line @next/next/no-sync-scripts */}
<script
async
src={`https://www.googletagmanager.com/gtag/js?id=${analyticsWebsiteId}`}
/>
<script
dangerouslySetInnerHTML={{
__html: `window.dataLayer = window.dataLayer || [];function gtag(){dataLayer.push(arguments);}gtag('js', new Date());gtag('config', '${analyticsWebsiteId}');`,
}}
/>
</>
)}
{analyticsEnabled && analyticsProvider === 'custom' && analyticsCustomScript && (
<script
dangerouslySetInnerHTML={{ __html: analyticsCustomScript }}
/>
)}
</head>
<body
className={`${inter.className} min-h-screen bg-white text-gray-900 dark:bg-black dark:text-gray-200`}