diff --git a/public/js/umami-share.js b/public/js/umami-share.js new file mode 100644 index 000000000..4e090c909 --- /dev/null +++ b/public/js/umami-share.js @@ -0,0 +1,47 @@ +(function (global) { + const cacheKey = 'umami-share-cache'; + const cacheTTL = 3600_000; // 1h + + async function fetchShareData(baseUrl, shareId) { + const cached = localStorage.getItem(cacheKey); + if (cached) { + try { + const parsed = JSON.parse(cached); + if (Date.now() - parsed.timestamp < cacheTTL) { + return parsed.value; + } + } catch { + localStorage.removeItem(cacheKey); + } + } + const res = await fetch(`${baseUrl}/api/share/${shareId}`); + if (!res.ok) { + throw new Error('获取 Umami 分享信息失败'); + } + const data = await res.json(); + localStorage.setItem(cacheKey, JSON.stringify({ timestamp: Date.now(), value: data })); + return data; + } + + /** + * 获取 Umami 分享数据(websiteId、token)。 + * 在缓存 TTL 内复用;并用全局 Promise 避免并发请求。 + * @param {string} baseUrl + * @param {string} shareId + * @returns {Promise<{websiteId: string, token: string}>} + */ + global.getUmamiShareData = function (baseUrl, shareId) { + if (!global.__umamiSharePromise) { + global.__umamiSharePromise = fetchShareData(baseUrl, shareId).catch((err) => { + delete global.__umamiSharePromise; + throw err; + }); + } + return global.__umamiSharePromise; + }; + + global.clearUmamiShareCache = function () { + localStorage.removeItem(cacheKey); + delete global.__umamiSharePromise; + }; +})(window); \ No newline at end of file diff --git a/src/components/PostCard.astro b/src/components/PostCard.astro index c1f26f66f..686d5964e 100644 --- a/src/components/PostCard.astro +++ b/src/components/PostCard.astro @@ -107,13 +107,8 @@ const { remarkPluginFrontmatter } = await entry.render(); } try { - // 第一步:获取网站ID和token - const shareResponse = await fetch(`${umamiConfig.baseUrl}/api/share/${umamiConfig.shareId}`); - if (!shareResponse.ok) { - throw new Error('获取分享信息失败'); - } - const shareData = await shareResponse.json(); - const { websiteId, token } = shareData; + // 调用全局工具获取 Umami 分享数据 + const { websiteId, token } = await getUmamiShareData(umamiConfig.baseUrl, umamiConfig.shareId); // 第二步:获取统计数据 const currentTimestamp = Date.now(); @@ -125,6 +120,11 @@ const { remarkPluginFrontmatter } = await entry.render(); } }); + if (statsResponse.status === 401) { + clearUmamiShareCache(); + return await fetchPostCardViews(slug); + } + if (!statsResponse.ok) { throw new Error('获取统计数据失败'); } diff --git a/src/components/PostMeta.astro b/src/components/PostMeta.astro index e83007601..2e8070e16 100644 --- a/src/components/PostMeta.astro +++ b/src/components/PostMeta.astro @@ -97,19 +97,14 @@ const className = Astro.props.class; {slug && ( {/* */}