Files
fuwari/public/js/umami-share.js
二叉树树 bb4aacb07b style: biome
优化访客统计动画逻辑
2026-01-01 21:39:51 +08:00

117 lines
3.0 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

((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 = (baseUrl, shareId) => {
if (!global.__umamiSharePromise) {
global.__umamiSharePromise = fetchShareData(baseUrl, shareId).catch(
(err) => {
delete global.__umamiSharePromise;
throw err;
},
);
}
return global.__umamiSharePromise;
};
global.clearUmamiShareCache = () => {
localStorage.removeItem(cacheKey);
delete global.__umamiSharePromise;
};
// 初始化全局缓存 Map
if (!global.__umamiDataCache) {
global.__umamiDataCache = new Map();
}
/**
* 获取 Umami 统计数据
* 自动处理 token 获取和过期重试
* @param {string} baseUrl
* @param {string} shareId
* @param {object} queryParams
* @returns {Promise<any>}
*/
global.fetchUmamiStats = async (baseUrl, shareId, queryParams) => {
// 生成缓存键baseUrl + shareId + queryParams的字符串表示
const cacheKey = `${baseUrl}|${shareId}|${JSON.stringify(queryParams)}`;
// 检查全局内存缓存
if (global.__umamiDataCache.has(cacheKey)) {
const data = global.__umamiDataCache.get(cacheKey);
// 标记数据来自缓存
return { ...data, _fromCache: true };
}
async function doFetch(isRetry = false) {
const { websiteId, token } = await global.getUmamiShareData(
baseUrl,
shareId,
);
const currentTimestamp = Date.now();
const params = new URLSearchParams({
startAt: 0,
endAt: currentTimestamp,
unit: "hour",
timezone: queryParams.timezone || "Asia/Shanghai",
compare: false,
...queryParams,
});
const statsUrl = `${baseUrl}/api/websites/${websiteId}/stats?${params.toString()}`;
const res = await fetch(statsUrl, {
headers: {
"x-umami-share-token": token,
},
});
if (!res.ok) {
if (res.status === 401 && !isRetry) {
global.clearUmamiShareCache();
return doFetch(true);
}
throw new Error("获取统计数据失败");
}
const data = await res.json();
// 写入全局缓存
global.__umamiDataCache.set(cacheKey, data);
return data;
}
return doFetch();
};
})(window);