diff --git a/website/src/components/Footer.tsx b/website/src/components/Footer.tsx index 55f104c..6fb54a1 100644 --- a/website/src/components/Footer.tsx +++ b/website/src/components/Footer.tsx @@ -1,3 +1,4 @@ +import { useEffect, useState } from "react"; import { recommendLinks } from "../data/recommendLinks"; import { useLang } from "../i18n"; import { asset } from "../lib/asset"; @@ -12,8 +13,62 @@ const logoFor: Record = { "基元律动": "logos/tokenrhythm.svg", }; +// 下载统计:只拉 GitHub(公开 API 带 CORS),镜像站 Gitea 未开 CORS 不计入。 +const STATS_URL = + "https://api.github.com/repos/billowliu2/KimiSwitch/releases?per_page=100"; +const STATS_CACHE_KEY = "kimi-switch-downloads"; +const STATS_CACHE_TTL = 60 * 60 * 1000; // 1 hour + +/** GitHub Release 全部资产 download_count 之和;拉取失败返回 null(不展示)。 */ +function useDownloadCount(): number | null { + const [count, setCount] = useState(null); + useEffect(() => { + let cancelled = false; + try { + const cached = localStorage.getItem(STATS_CACHE_KEY); + if (cached) { + const { ts, total } = JSON.parse(cached) as { ts: number; total: number }; + if (Date.now() - ts < STATS_CACHE_TTL) { + setCount(total); + return; + } + } + } catch { + /* ignore corrupt cache */ + } + fetch(STATS_URL) + .then((r) => (r.ok ? r.json() : Promise.reject(new Error(String(r.status))))) + .then((releases: Array<{ assets?: Array<{ download_count?: number }> }>) => { + if (cancelled) return; + const total = (releases ?? []).reduce( + (sum, rel) => + sum + + (rel.assets ?? []).reduce((s, a) => s + (a.download_count ?? 0), 0), + 0 + ); + setCount(total); + try { + localStorage.setItem( + STATS_CACHE_KEY, + JSON.stringify({ ts: Date.now(), total }) + ); + } catch { + /* ignore quota errors */ + } + }) + .catch(() => { + /* 拉取失败则不显示 */ + }); + return () => { + cancelled = true; + }; + }, []); + return count; +} + export default function Footer() { const { t } = useLang(); + const downloadCount = useDownloadCount(); return ( ); diff --git a/website/src/i18n.tsx b/website/src/i18n.tsx index 8aaf0d8..dc8382c 100644 --- a/website/src/i18n.tsx +++ b/website/src/i18n.tsx @@ -345,6 +345,7 @@ const zh = { footer: { recommend: "推荐供应商", mirror: "国内镜像", + downloads: "GitHub 累计下载 {n} 次", creditBefore: "仪表盘与会话管理功能基于 ", creditAfter: " (MIT, © JochenYang) 移植", recommendText: { @@ -698,6 +699,7 @@ const en: Dict = { footer: { recommend: "Recommended Providers", mirror: "China mirror", + downloads: "Total GitHub downloads: {n}", creditBefore: "Dashboard & session management adapted from ", creditAfter: " (MIT, © JochenYang)", recommendText: {