Featured image of post 通过 Cloudflare Worker 封装你的 Umami 服务

通过 Cloudflare Worker 封装你的 Umami 服务

上一篇文章 我们搭建了 Umami 分析平台服务,并初步的避免了 token 泄露带来的问题。

但文章末尾我们抛出了一个问题:

只能在内网环境下登录查看统计数据

所以在这篇文章中,我们将利用 Cloudflare Worker 来封装我们的 /api/websites/xxx/stats 请求,彻底解决这个问题。

创建 Cloudflare Worker

首先我们登录 Cloudflare Dashboard,选择左侧的 Compute (Workers),在弹出的 Workers & Pages 页面点击 Create 创建。

由于我们的 worker 比较简单,直接选择 Hello world,随意填写一个名称(例如 my-umami-stats)后我们点击 Deploy 发布。

在 worker 首页右上角点击 Edit code 按钮编辑代码,填入如下代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
const UmamiUrl = 'https://u.example.com';   // 这里替换成你的 umami 服务地址
const AllowOrigins = ['https://example.com', 'https://www.example.com'];  // 这里是允许跨域的域名列表

async function createResponse(response, mimeType, origin) {
  const data = await response.text();
  let corsHeader;
  if (origin) {
    if (AllowOrigins.indexOf(origin) < 0) {
      origin = '';
    }
    corsHeader = {
      'access-control-allow-origin': origin,
      'access-control-allow-methods': 'GET,HEAD,POST,OPTIONS',
      // 'access-control-max-age': '86400',
      'access-control-allow-headers': 'content-type'
    };
  }
  response = new Response(data, {
    headers: {
      ...response.headers,
      'content-type': mimeType,
      ...corsHeader
    }
  });
  return response;
}

async function getScript(request) {
  let response = await caches.default.match(request);
  if (!response) {
    response = await fetch(`${UmamiUrl}/script.js`, request);
    response = await createResponse(response, 'application/javascript');
    response.headers.append('cache-control', 'public, max-age=86400');

    await caches.default.put(request, response.clone());
  }
  return response;
}

async function sendApi(request, url, bearer) {
  request = new Request(request);
  // request.headers.delete('cookie');
  if (bearer) {
    /* 注意这里替换成你的登录 token,可以通过 POST /api/auth 来获取
     * {
     *   "username": "<user name>",
     *   "password": "<password>"
     * }
     */
    request.headers.append('authorization', 'Bearer xxxyyyzzz');
  }
  let response = await fetch(`${UmamiUrl}${url}`, request);
  response = await createResponse(response, 'application/json', request.headers.get('origin'));
  return response;
}

export default {
  async fetch(request) {
    const url = new URL(request.url);
    const pathname = url.pathname;
    switch (pathname) {
      case '/script.js': return await getScript(request);
      // `/api/send` 请求用于提交访问信息,不需要 token
      case '/api/send': return await sendApi(request, '/api/send');
    }
    // `/api/websites/xxx/stats` 用于获取站点统计信息,需要 token
    if (/^\/api\/websites\/[0-9a-f-]+\/stats$/.test(pathname)) {
      return await sendApi(request, `${pathname}${url.search}`, true);
    }
    return new Response(null, { status: 404 });
  },
};

完成,点击右上角的 Deploy 发布此版本。

创建 Worker 路由

如此,我们无需 token 便可以通过类似于 https://my-umami-stats.example.workers.dev/ 的地址来获取我们的站点统计信息。

.workers.dev 后缀在国内很多地区被屏蔽从而导致无法访问,我们还需要在自定义域名中添加一条 Worker 路由来使用自己的域名。

进入你的域名管理首页,左侧菜单中选择 Workers Routes,点击右侧的 Add route 按钮,路由处写 u.example.com/*(注意换成你自己的域名),Worker 选择刚才创建的 my-umami-stats

还需要在 DNS 里给 u.example.com 添加一条解析记录,值不重要,但是一定要勾选 Proxied (我设置成了 CNAME 到 my-umami-stats.example.workers.dev)。

大功告成,你可以访问 https://u.example.com/script.js 来检查代理是否已成功建立。

使用 Hugo 构建
主题 StackJimmy 设计