diff --git a/src/lib/components/admin/AnalyticsPanel.svelte b/src/lib/components/admin/AnalyticsPanel.svelte
new file mode 100644
index 0000000..b76b9fd
--- /dev/null
+++ b/src/lib/components/admin/AnalyticsPanel.svelte
@@ -0,0 +1,266 @@
+
+
+
+
+
+
文章总数
+
{dbStats.posts}
+
+
+
页面总数
+
{dbStats.pages}
+
+
+
分类 / 标签
+
{dbStats.categories} / {dbStats.tags}
+
+
+
+ {#if loading}
+
+
+
正在从 Cloudflare 获取最近 30 天流量数据...
+
+ {:else if error}
+
+ {error}
+
+ {:else if !analyticsData || analyticsData.length === 0}
+
+ 没有获取到有效的流量数据,请确保 CF_API_TOKEN 和 CF_ZONE_ID 配置正确,且网站已有访问。
+
+ {:else}
+
+
+
总请求数 (30天)
+
{totalRequests.toLocaleString()}
+
+
+
总下发流量 (30天)
+
{formatBytes(totalBytes)}
+
+
+
缓存命中率
+
{hitRate.toFixed(1)}%
+
+
+
+
+
+ {/if}
+
+
+
diff --git a/src/routes/admin/settings/+page.server.ts b/src/routes/admin/settings/+page.server.ts
index 024935a..a09d1ab 100644
--- a/src/routes/admin/settings/+page.server.ts
+++ b/src/routes/admin/settings/+page.server.ts
@@ -2,26 +2,44 @@
import { fail } from '@sveltejs/kit';
import type { PageServerLoad, Actions } from './$types';
+import { fetchAnalytics } from '$lib/server/cloudflare';
export const load: PageServerLoad = async ({ locals, platform }) => {
const db = platform?.env?.DB;
const settings: Record = {};
+ let dbStats = { posts: 0, pages: 0, tags: 0, categories: 0 };
if (db) {
try {
const { results } = await db.prepare('SELECT key, value FROM settings').all();
results.forEach((row: any) => {
settings[row.key] = row.value;
});
+
+ const [postsRes, pagesRes, tagsRes, categoriesRes] = await db.batch([
+ db.prepare('SELECT COUNT(*) as count FROM posts'),
+ db.prepare('SELECT COUNT(*) as count FROM pages'),
+ db.prepare('SELECT COUNT(*) as count FROM tags'),
+ db.prepare('SELECT COUNT(*) as count FROM categories')
+ ]);
+
+ dbStats = {
+ posts: (postsRes.results[0] as any).count || 0,
+ pages: (pagesRes.results[0] as any).count || 0,
+ tags: (tagsRes.results[0] as any).count || 0,
+ categories: (categoriesRes.results[0] as any).count || 0
+ };
} catch (err: any) {
- console.error('Failed to load settings', err);
+ console.error('Failed to load settings or stats', err);
}
}
return {
user: locals.user,
platformEnv: db ? 'Cloudflare D1 (Connected)' : 'Local Mock',
- settings
+ settings,
+ dbStats,
+ analytics: fetchAnalytics(platform) // Streaming promise
};
};
diff --git a/src/routes/admin/settings/+page.svelte b/src/routes/admin/settings/+page.svelte
index f44c83b..204a380 100644
--- a/src/routes/admin/settings/+page.svelte
+++ b/src/routes/admin/settings/+page.svelte
@@ -7,6 +7,7 @@
import DatePicker from '$lib/components/admin/DatePicker.svelte';
import TextInput from '$lib/components/admin/TextInput.svelte';
import Button from '$lib/components/admin/Button.svelte';
+ import AnalyticsPanel from '$lib/components/admin/AnalyticsPanel.svelte';
import { untrack } from 'svelte';
let { data, form }: { data: PageData; form: ActionData } = $props();
@@ -44,7 +45,8 @@
tabs={[
{ id: 'basic', label: '基础信息' },
{ id: 'appearance', label: '外观与主页' },
- { id: 'terms', label: '术语设定' }
+ { id: 'terms', label: '术语设定' },
+ { id: 'analytics', label: '数据统计' }
]}
bind:activeTab
/>
@@ -318,6 +320,8 @@
+ {:else if activeTab === 'analytics'}
+
{/if}