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 && (
{/*
*/}