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
+24 -1
View File
@@ -83,6 +83,11 @@ export async function POST(request: NextRequest) {
OIDCClientSecret,
OIDCButtonText,
OIDCMinTrustLevel,
AnalyticsEnabled,
AnalyticsProvider,
AnalyticsScriptUrl,
AnalyticsWebsiteId,
AnalyticsCustomScript,
} = body as {
SiteName: string;
Announcement: string;
@@ -138,6 +143,11 @@ export async function POST(request: NextRequest) {
OIDCClientSecret?: string;
OIDCButtonText?: string;
OIDCMinTrustLevel?: number;
AnalyticsEnabled?: boolean;
AnalyticsProvider?: 'umami' | 'google' | 'custom';
AnalyticsScriptUrl?: string;
AnalyticsWebsiteId?: string;
AnalyticsCustomScript?: string;
};
// 参数校验
@@ -224,7 +234,15 @@ export async function POST(request: NextRequest) {
(OIDCClientSecret !== undefined &&
typeof OIDCClientSecret !== 'string') ||
(OIDCButtonText !== undefined && typeof OIDCButtonText !== 'string') ||
(OIDCMinTrustLevel !== undefined && typeof OIDCMinTrustLevel !== 'number')
(OIDCMinTrustLevel !== undefined && typeof OIDCMinTrustLevel !== 'number') ||
(AnalyticsEnabled !== undefined && typeof AnalyticsEnabled !== 'boolean') ||
(AnalyticsProvider !== undefined &&
AnalyticsProvider !== 'umami' &&
AnalyticsProvider !== 'google' &&
AnalyticsProvider !== 'custom') ||
(AnalyticsScriptUrl !== undefined && typeof AnalyticsScriptUrl !== 'string') ||
(AnalyticsWebsiteId !== undefined && typeof AnalyticsWebsiteId !== 'string') ||
(AnalyticsCustomScript !== undefined && typeof AnalyticsCustomScript !== 'string')
) {
return NextResponse.json({ error: '参数格式错误' }, { status: 400 });
}
@@ -295,6 +313,11 @@ export async function POST(request: NextRequest) {
OIDCClientSecret,
OIDCButtonText,
OIDCMinTrustLevel,
AnalyticsEnabled,
AnalyticsProvider,
AnalyticsScriptUrl,
AnalyticsWebsiteId,
AnalyticsCustomScript,
};
// 写入数据库