
关于页数据统计接入自建umami
文章摘要
Stars Harbor Deepseek
拼命加载中···
本教程已于2026年1月15日针对umami3的部分调整进行了更新,如您仍在使用umami2请及时升级或参考其它教程
另感谢:梦爱吃鱼的新版数据追踪代码
本文教程仅适用于通过vercel自建的umami追踪,通过服务器自托管的仅提供部分第三方教程,敬请谅解
搭建数据源
vercel部署umami
此步骤中针对storage的选择部分因vercel有过更新调整,因此实际情况与本教程存在一些差异,但整体思路类似,可简单参考。后续将视情况对此步骤教程稍加调整
- fork一份官方的仓库
- 登录vercel控制台(若此前尚未注册vercel,请自行借助搜索引擎搜索教程,此处不做赘述)引用站外链接登录vercel登录vercel账号
- 在控制台首页顶栏中找到「Storage」,点击进入
- 点击「Create Database」新建数据库,选择「Postgres」
- 设置名称并确认
- 进入数据库控制台,此时页面上会显示一个用于连接数据库的字符串,点击
Show Secret按钮后复制双引号中的内容(无需包含双引号) - 新建项目,选择刚刚fork的umami仓库
- 设置必要环境变量(数据库)
DATABASE_URL,数据库值设置为第5步完成后复制的长字符串 - (可选)非必要环境变量
TRACKER_SCRIPT_NAME,可设置umami跟踪器名称,以避免默认名称被广告拦截器拦截导致追踪失败。该处可任意填写自己喜欢的值 - 等待部署完成即可
- (可选)绑定自定义域名,此处不做赘述
- 访问umami地址,初次登录时默认用户名为
admin,默认密码为umami,可在登录后自行修改
Cloudflare-Workers搭建数据接口
以下教程来自「梦爱吃鱼」,特此鸣谢!原文地址:
引用站外链接CF Worker部署Umami的API通过CF Worker部署Umami API,实现白嫖方案
- 前往 Hoppscotch 获取token
- 成功后返回token信息
- 前往Cloudflare,创建一个Workers,设置好名称,确认部署并等待
- 部署完成,点击右上角编辑代码,修改worker.js的内容(注意,部分内容需修改为你的数据!!!)
const CONFIG = { baseUrl: '你的umami地址', token: '老地方获取token', websiteId: '需要监听的站点ID' }; // 时间范围配置 const TIME_CONFIGS = { today: () => { const now = new Date(); return { start: new Date(now.getFullYear(), now.getMonth(), now.getDate()), end: new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1), unit: 'hour' }; }, yesterday: () => { const now = new Date(); return { start: new Date(now.getFullYear(), now.getMonth(), now.getDate() - 1), end: new Date(now.getFullYear(), now.getMonth(), now.getDate()), unit: 'day' }; }, lastMonth: () => { const now = new Date(); return { start: new Date(now.getFullYear(), now.getMonth() - 1, 1), end: new Date(now.getFullYear(), now.getMonth(), 1), unit: 'month' }; }, total: () => { return { start: new Date(2000, 0, 1), end: new Date(), unit: 'year' }; }, now: () => { const now = new Date(); return { start: new Date(now.getTime() - 15 * 60 * 1000), // 最近15分钟 end: now, unit: 'hour' }; } }; // 获取统计数据 async function fetchStats(timeRange) { const config = TIME_CONFIGS[timeRange](); if (!config) return { visits: 0, pageviews: 0 }; const params = new URLSearchParams({ startAt: Math.floor(config.start.getTime()), endAt: Math.floor(config.end.getTime()), unit: config.unit }); const url = CONFIG.baseUrl + '/api/websites/' + CONFIG.websiteId + '/stats?' + params.toString(); try { const response = await fetch(url, { headers: { 'Authorization': 'Bearer ' + CONFIG.token, 'Content-Type': 'application/json' } }); if (!response.ok) { throw new Error('HTTP错误!状态码: ' + response.status); } const data = await response.json(); // 提取访问数据(兼容不同API返回格式) const extractMetric = (key) => { // 对于实时数据,特殊处理:即使主数据字段为0,也检查comparison字段 if (timeRange === 'now' && data.comparison?.[key]) { const comparisonValue = parseInt(data.comparison[key]) || 0; if (comparisonValue > 0) { return comparisonValue; } } // 主数据字段 if (data[key] !== undefined && data[key] !== null) { return typeof data[key] === 'object' ? (data[key].value || data[key].count || 0) : parseInt(data[key]) || 0; } // 指标字段 if (data.metrics?.[key]) { return data.metrics[key].value || data.metrics[key].count || 0; } // 比较数据字段 if (data.comparison?.[key]) { return parseInt(data.comparison[key]) || 0; } return 0; }; return { visits: extractMetric('visits'), pageviews: extractMetric('pageviews') }; } catch (error) { console.error('获取统计数据失败: ' + error.message); return { visits: 0, pageviews: 0 }; } } // 处理请求 async function handleRequest(request) { try { // 设置CORS头 const corsHeaders = { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Methods': 'GET, OPTIONS', 'Access-Control-Allow-Headers': 'Origin, Content-Type, Accept' }; // 处理OPTIONS请求 if (request.method === 'OPTIONS') { return new Response(null, { headers: corsHeaders }); } // 并行获取所有统计数据 const timeRanges = ['today', 'yesterday', 'lastMonth', 'total', 'now']; const statsData = await Promise.all(timeRanges.map(range => fetchStats(range))); // 构建统计数据对象 const stats = Object.fromEntries(timeRanges.map((range, index) => [range, statsData[index]])); // 返回统计数据 const responseData = { today_uv: stats.today.visits, today_pv: stats.today.pageviews, online_users: stats.now.visits, yesterday_uv: stats.yesterday.visits, yesterday_pv: stats.yesterday.pageviews, last_month_pv: stats.lastMonth.pageviews, total_uv: stats.total.visits, total_pv: stats.total.pageviews }; return new Response(JSON.stringify(responseData), { headers: { ...corsHeaders, 'Content-Type': 'application/json; charset=utf-8' } }); } catch (error) { return new Response(JSON.stringify({ error: error.message }), { status: 500, headers: { 'Content-Type': 'application/json; charset=utf-8' } }); } } // 注册事件监听器 addEventListener('fetch', event => { event.respondWith(handleRequest(event.request)); }); - 完成编辑后,保存并部署
- (可选)绑定自定义域名。在workers中点击设置,在
域和路由项下添加自定义域名(受cloudflare限制,域名必须已托管至cloudflare才可绑定)
将数据引入about页面
修改./source/_data/about.yml文件中的统计部分
tj: # 统计
provider: custom # 51la/custom
url: 你的数据接口地址
img: https://7.isyangs.cn/1/65eb2e9109826-1.png # 背景
desc: # 卡片左下角描述配置完成~
写在最后
下次一定不还拖更
- 感谢您的赞赏
赞赏名单
因为有你们,让我更加有创作的动力
本文是原创文章,采用CC BY-NC-SA 4.0协议,完整转载请注明来自❖星港◎Star☆
评论 ()

















