前言

之前在基于uptime-status搭建自己的网站状态页已经使用CloudFlare Worker搭建了一个UptimeRobot地接口代理,但是效果并不好,官方接口1分钟只能请求10次,导致多次刷新网站就会出现429错误。

这次不仅更新了Worker的代码,还重构了Uptime-Status项目,改动非常的大。

可以参观我的新状态页面InsectMk的在线状态

项目

insectmk/uptimerobot-status根据yb/uptime-status所重构,更先进的技术,更规范的代码,更易于阅读和修改,欢迎大家使用/修改。

项目异同:

  1. 界面UI:完全一致的样式(Ctrl + C来的)

  2. 使用技术:yb/uptime-status采用reactinsectmk/uptimerobot-status采用Vite + Vue3 + TS

  3. 配置文件:配置一致,insectmk/uptimerobot-status扩展了时间线排序代理接口的配置。

  4. 易用性:yb/uptime-status支持用户级的配置,修改配置文件后无需再次编译;insectmk/uptimerobot-status阉割了用户级配置,采用项目级配置(每次编译后需重新部署打包文件)。

Worker

这次优化了一下Worker代码,不再一味纯代,加入了缓存支持,不会出现429的报错,建议缓存1分钟以上,默认缓存1分钟。

cloudflare控制台,找到Workers 和 Pages菜单,点击创建-> 创建Workers创建一个Workers

在编辑页面为Worker取一个名字,我取名为uptimerobot,在worker.js中写入以下代码:

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
73
74
75
76
77
78
79
80
81
82
83
export default {
async fetch(request, env, ctx) {
const cache = caches.default;
const targetURL = 'https://api.uptimerobot.com/v2/getMonitors';

// 处理 OPTIONS 预检请求
if (request.method === 'OPTIONS') {
return new Response(null, {
status: 204, // No Content
headers: {
'Access-Control-Allow-Origin': '*', // 允许所有来源
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS', // 允许的 HTTP 方法
'Access-Control-Allow-Headers': 'Content-Type, Authorization', // 允许的请求头部
},
});
}

let requestBody = '';
if (request.method === 'POST') {
// 读取请求体
requestBody = await request.text();
try {
// 尝试解析为 JSON,并进行标准化排序
const json = JSON.parse(requestBody);
requestBody = JSON.stringify(sortObject(json));
} catch (error) {
console.error('Failed to parse request body as JSON:', error);
}
}

// 构造缓存键,包含 URL 和标准化的请求体
const cacheKey = new Request(targetURL + '|' + requestBody);

// 尝试从缓存中获取响应
let response = await cache.match(cacheKey);
if (response) {
console.log('Cache hit!');
} else {
console.log('Cache miss. Fetching from target...');

// 转发请求到目标接口
const modifiedRequest = new Request(targetURL, {
method: 'POST',
headers: request.headers, // 传递请求头部
body: requestBody, // 转发标准化后的请求体
});

const fetchResponse = await fetch(modifiedRequest);
if (fetchResponse.ok) {
response = new Response(fetchResponse.body, fetchResponse);
response.headers.set('Cache-Control', 'max-age=60'); // 设置缓存 1 分钟
ctx.waitUntil(cache.put(cacheKey, response.clone())); // 异步存入缓存
} else {
console.error(`Target returned status: ${fetchResponse.status}`);
response = new Response(fetchResponse.body, fetchResponse);
}
}

// 设置跨域头部
response = new Response(response.body, response); // 克隆响应
response.headers.set('Access-Control-Allow-Origin', '*'); // 允许所有来源
response.headers.set('Access-Control-Allow-Methods', 'GET, POST, OPTIONS'); // 允许的 HTTP 方法
response.headers.set('Access-Control-Allow-Headers', 'Content-Type, Authorization'); // 允许的请求头部

return response;
},
};

// 辅助函数:递归排序对象键
function sortObject(obj) {
if (Array.isArray(obj)) {
return obj.map(sortObject);
} else if (obj !== null && typeof obj === 'object') {
return Object.keys(obj)
.sort()
.reduce((result, key) => {
result[key] = sortObject(obj[key]);
return result;
}, {});
}
return obj;
}

编辑好后,点击部署即可,现在就可以使用了,你会在面板上看到一个链接,这个链接就是你的代理地址。

如果你的CloudFlare绑定了域名的话,你可以在Worker的设置中绑定域名。

参考文档

Uptime Robot-API 官方文档

Cloud Flare-Workers 官方文档