[Share Experiences] 使用Vercel自建Umami数据统计
Tofloor
poster avatar
青稚
deepin
2025-04-17 11:01
Author

摘要

这篇文章讲述了:如何利用Vercel和Aiven的免费数据库服务,从零开始搭建一个开源的网站数据分析平台Umami。通过分步指导,读者可以学习到完整的部署流程,包括数据库配置、应用部署和域名绑定等关键环节。并通过Cloudflare的Workers来接入自建Umami的方法,将自建的Umami统计服务安全地对外公开,同时保持数据隐私性。文中提供了worker.js文件的详细修改指南,并说明了如何将其与自定义域名进行绑定,最终实现将网站访问统计数据可视化展示。

介绍

本教程以Vercer和Aiven免费数据库为例,不止可以以Vercel托管,也可以选择Netlify等,数据库也可以选择其他免费数据库服务,详细的请观看官方文档

准备

注册Github,Vercel,Aiven(如何注册就不写了,注册非常简单)

创建数据库

注册好Aiven后创建 MySQL 数据库

data.jpg

设置MySQL数据库,数据库位置选择亚太地区

data2.jpg

设置Vercel

注册好Github后,Fork Umami仓库

https://github.com/umami-software/umam

打开Vercel选择Fork的仓库

verceld.jpg

复制 Service URI 到Vercel的环境变量中添加 DATABASE_URL

data3.jpg

设置DATABASE_URL环境变量

(可选)非必要环境变量TRACKER_SCRIPT_NAME,设置umami跟踪器名称(可填写自己喜欢的值),避免默认名称被广告拦截器拦截导致追踪失败。

verceld2.jpg

设置自定义域名(由于Vercel自分配域名被墙,国内访问不到)

11.jpg

vercelurl2.jpg

设置Umami

默认用户名为 admin ,默认密码为 umami,可在登录后自行修改(推荐修改默认密码,防止他人作恶)

创建新用户

进入设置-用户-创建新用户并设置密码,设置权限为仅浏览量(防止后续教程默认用户的Token被人作恶)

user.jpg

创建团队

进入设置-团队-创建团队

后面是把需要统计的网站添加到团队,切换到团队-设置-网站-添加网站即可,这个比较简单,不过多描述。

67ff9effb0628.jpg

记录访问代码

进入设置-团队-点击创建的团队-查看详细信息

team1.jpg

登陆新用户

退出登陆-登陆新用户-输入新创建的用户名和密码,之后进入设置-团队-加入团队-输入访问代码

team2.jpg

Cloudflare-Workers搭建Umami数据接口

前往 Hoppscotch获取Token

输入新创建的用户名和密码

hoppscotch.jpg

成功后返回Token信息

hoppscotch2.jpg

前往Cloudflare,创建一个Workers,设置好名称,确认部署并等待

部署完成,点击右上角编辑代码,修改worker.js的内容(部分内容需修改为自己的数据)

addEventListener('fetch', event => {
  event.respondWith(handleRequest(event));
});

const API_BASE_URL = 'umami地址';
const TOKEN = '获取到的token';
const WEBSITE_ID = '网站在umami中的website ID';
const CACHE_KEY = 'umami_cache'; // 缓存
const CACHE_TIME = 600; // 缓存时间,单位秒

async function fetchUmamiData(startAt, endAt) {
  const url = `${API_BASE_URL}/api/websites/${WEBSITE_ID}/stats?startAt=${startAt}&endAt=${endAt}`;
  const response = await fetch(url, {
    headers: {
      'Authorization': `Bearer ${TOKEN}`,
      'Content-Type': 'application/json'
    }
  });

  if (!response.ok) {
    console.error(`Error fetching data: ${response.statusText}`);
    return null;
  }

  return response.json();
}

async function handleRequest(event) {
  const cache = await caches.open(CACHE_KEY);
  const cachedResponse = await cache.match(event.request);

  if (cachedResponse) {
    return cachedResponse;
  }

  const now = Date.now();
  const todayStart = new Date(now).setHours(0, 0, 0, 0);
  const yesterdayStart = new Date(now - 86400000).setHours(0, 0, 0, 0);
  const lastMonthStart = new Date(now).setMonth(new Date(now).getMonth() - 1);
  const lastYearStart = new Date(now).setFullYear(new Date(now).getFullYear() - 1);

  const [todayData, yesterdayData, lastMonthData, lastYearData] = await Promise.all([
    fetchUmamiData(todayStart, now),
    fetchUmamiData(yesterdayStart, todayStart),
    fetchUmamiData(lastMonthStart, now),
    fetchUmamiData(lastYearStart, now)
  ]);

  const responseData = {
    today_uv: todayData?.visitors?.value ?? null,
    today_pv: todayData?.pageviews?.value ?? null,
    yesterday_uv: yesterdayData?.visitors?.value ?? null,
    yesterday_pv: yesterdayData?.pageviews?.value ?? null,
    last_month_pv: lastMonthData?.pageviews?.value ?? null,
    last_year_pv: lastYearData?.pageviews?.value ?? null
  };

  const jsonResponse = new Response(JSON.stringify(responseData), {
    headers: {
      'Content-Type': 'application/json',
      'Access-Control-Allow-Origin': '*',
      'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
      'Access-Control-Allow-Headers': 'Content-Type, Authorization'
    }
  });

  event.waitUntil(cache.put(event.request, jsonResponse.clone()));

  return jsonResponse;
}

完成编辑后,保存并部署

(可选)绑定自定义域名。在Workers中点击设置,在域和路由项下添加自定义域名(受CloudFlare限制,域名必须已托管至CloudFlare才可绑定)

结尾

原文

https://blog.linux-qitong.top/posts/86dde596

引用

  1. 星港
  2. 叶泯希
  3. 梦爱吃鱼

感谢三位哥哥,因为三位哥哥成功在自己的About页面添加访问统计,接入的教程十分简单,本文不再详细写明。

Reply Favorite View the author
All Replies

No replies yet