前言

因为使用了uptimerobot监控了网站,所以之前写了个工具在工具箱里,但是每次想要知道最近网站运行状态都需要访问工具箱,就很麻烦

刚好在WordPress建站的时候就看到了张洪HEO的网站底部有个uptimekuma的运行状态显示

于是自己就想搞一个,切记,这不是基于uptimekuma的,而是基于uptimerobot的

效果

监控卡片

教程

首先,我们需要在source/_data文件夹中创建一个aside.yml文件,然后将如下代码放进去

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
- name: uptime
title: 服务状态
icon: fas fa-server
no_head: true
content_html: |
<div class="uptime-dashboard">
<div class="uptime-header">
<div class="uptime-title">
<i class="fas fa-chart-line"></i>
<span>实时监控</span>
</div>
<div class="uptime-summary" id="uptimeSummary">
<span class="summary-text">加载中...</span>
</div>
</div>
<div class="uptime-services" id="uptimeServices">
<!-- 动态内容将由JavaScript填充 -->
</div>
</div>

再然后进入到_config.solitude.yml中,将如下代码添加到extendsbody

1
- <script src="/js/uptime-status.js"></script>

看到这里相信不少人知道了,接下来我们需要在source文件夹的js文件夹中创建一个uptime-status.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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
class UptimeStatus {
constructor() {
this.isInitialized = false;
this.apiKey = '';//你的api密钥
this.apiUrl = 'https://api.uptimerobot.com/v2/getMonitors';
this.config = {
statusText: {
normal: '正常',
warning: '异常',
error: '故障',
loading: '检查中...',
unknown: '未知',
noApi: '未配置API',
apiError: 'API错误',
networkError: '网络错误',
noService: '无监控服务'
}
};
this.init();
}

init() {
// 等待DOM加载完成
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => this.initStatus());
} else {
this.initStatus();
}

// Pjax兼容
document.addEventListener('pjax:complete', () => this.initStatus());
}

initStatus() {
const uptimeCard = document.querySelector('.uptime-dashboard');
if (!uptimeCard) return;

this.isInitialized = true;
this.fetchServices();

// 设置定时刷新
setInterval(() => {
if (this.isInitialized) {
this.fetchServices();
}
}, 30000); // 30秒刷新一次
}

async fetchServices() {
try {
// 获取服务列表和状态
const servicesData = await this.getServicesData();
if (servicesData && servicesData.length > 0) {
this.renderServices(servicesData);
this.updateSummary(servicesData);
} else {
console.warn('API返回的服务数据为空');
this.renderServices(this.getDefaultServices());
this.updateSummary([]);
}
} catch (error) {
console.error('获取服务状态失败:', error);
this.renderServices(this.getDefaultServices());
this.updateSummary([]);
}
}

async getServicesData() {
try {
console.log('正在调用UptimeRobot API...');

// 使用与用户提供的代码相同的请求方式
const requestData = {
api_key: this.apiKey,
format: "json",
logs: 1
};

const response = await fetch(this.apiUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(requestData)
});

console.log('API响应状态:', response.status);

if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}

const data = await response.json();
console.log('API返回数据:', data);

if (data.stat !== 'ok') {
throw new Error(`API error: ${data.error?.message || 'Unknown error'}`);
}

if (!data.monitors || data.monitors.length === 0) {
console.warn('API返回的monitors数组为空');
return [];
}

// 转换UptimeRobot数据格式
const services = data.monitors.map(monitor => ({
id: monitor.id,
name: monitor.friendly_name,
status: this.mapUptimeStatus(monitor.status),
uptime: parseFloat(monitor.all_time_uptime_ratio || '0'),
url: monitor.url
}));

console.log('转换后的服务数据:', services);
return services;

} catch (error) {
console.error('UptimeRobot API调用失败:', error);
throw error;
}
}

mapUptimeStatus(uptimeStatus) {
// UptimeRobot状态映射
// 0: paused, 1: not checked yet, 2: up, 8: seems down, 9: down
switch (uptimeStatus) {
case 2:
return 'normal';
case 8:
return 'warning';
case 9:
return 'error';
case 0:
return 'unknown';
default:
return 'loading';
}
}

getDefaultServices() {
return [
{
id: 'default',
name: '正在获取服务状态...',
status: 'loading',
uptime: 0,
url: '#'
}
];
}

renderServices(services) {
const container = document.getElementById('uptimeServices');
if (!container) return;

console.log('渲染服务数据:', services);

container.innerHTML = services.map(service => `
<div class="service-item" data-service="${service.id}">
<div class="service-name">${service.name}</div>
<div class="service-status">
<span class="status-indicator status-${service.status}"></span>
<span class="status-text">${this.getStatusText(service.status)}</span>
</div>
</div>
`).join('');
}

updateSummary(services) {
const summaryElement = document.getElementById('uptimeSummary');
if (!summaryElement) return;

if (services.length === 0) {
summaryElement.innerHTML = '<span class="summary-text">无监控服务</span>';
return;
}

const totalServices = services.length;
const normalServices = services.filter(s => s.status === 'normal').length;
const warningServices = services.filter(s => s.status === 'warning').length;
const errorServices = services.filter(s => s.status === 'error').length;

let summaryText = '';
if (errorServices > 0) {
summaryText = `${errorServices}个服务异常`;
} else if (warningServices > 0) {
summaryText = `${warningServices}个服务警告`;
} else if (normalServices === totalServices) {
summaryText = '所有服务正常';
} else {
summaryText = `${normalServices}/${totalServices}个服务正常`;
}

summaryElement.innerHTML = `<span class="summary-text">${summaryText}</span>`;
}

getStatusText(status) {
const statusMap = this.config.statusText;
return statusMap[status] || statusMap.unknown;
}
}

// 初始化
new UptimeStatus();

有人要问,代码中没有看到样式啊,接下来就是样式部分,在你的自定义css文件中添加如下代码

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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
/* Uptime 服务状态样式 - 全新设计 */
.uptime-dashboard {
background: var(--card-bg);
border-radius: 12px;
padding: 16px;
color: var(--font-color);
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
position: relative;
overflow: hidden;



.uptime-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16px;
position: relative;
z-index: 1;

.uptime-title {
display: flex;
align-items: center;
gap: 8px;
font-weight: 600;
font-size: 14px;

i {
font-size: 16px;
color: rgba(255, 255, 255, 0.9);
}
}

.uptime-summary {
.summary-text {
font-size: 12px;
opacity: 0.9;
font-weight: 500;
}
}
}

.uptime-services {
position: relative;
z-index: 1;
display: grid;
grid-template-columns: 1fr 1fr;
gap: 6px;

.service-item {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
padding: 8px 10px;
background: var(--card-bg);
border-radius: 8px;
border: 1px solid #e5e7eb;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);

&:hover {
transform: translateY(-1px);
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.15);
background: var(--hover-bg);
}

.service-name {
font-size: 11px;
font-weight: 500;
color: var(--font-color);
line-height: 1.2;
word-break: break-word;
flex: 1;
margin-right: 8px;
}

.service-status {
display: flex;
align-items: center;
gap: 4px;
flex-shrink: 0;

.status-indicator {
width: 8px;
height: 8px;
border-radius: 50%;
position: relative;
transition: all 0.3s ease;

&::after {
content: '';
position: absolute;
top: -1px;
left: -1px;
right: -1px;
bottom: -1px;
border-radius: 50%;
background: inherit;
opacity: 0.3;
filter: blur(2px);
}

&.status-normal {
background: #4ade80;
box-shadow: 0 0 0 1px rgba(74, 222, 128, 0.3);
}

&.status-warning {
background: #fbbf24;
box-shadow: 0 0 0 1px rgba(251, 191, 36, 0.3);
}

&.status-error {
background: #f87171;
box-shadow: 0 0 0 1px rgba(248, 113, 113, 0.3);
}

&.status-loading {
background: #60a5fa;
animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
}

&.status-unknown {
background: #9ca3af;
box-shadow: 0 0 0 1px rgba(156, 163, 175, 0.3);
}
}

.status-text {
font-size: 9px;
font-weight: 600;
color: var(--font-color);
text-transform: uppercase;
letter-spacing: 0.3px;
}
}

/* 单数服务居中显示 */
&:last-child:nth-child(odd) {
grid-column: 1 / -1;
max-width: 50%;
justify-self: center;
}
}
}
}

/* 深色模式适配 */
[data-theme="dark"] .uptime-dashboard {
background: linear-gradient(135deg, #1f2937 0%, #111827 100%);

.uptime-services .service-item {
background: rgba(255, 255, 255, 0.05);
border-color: rgba(255, 255, 255, 0.1);

&:hover {
background: rgba(255, 255, 255, 0.1);
}
}
}

@keyframes pulse {
0%, 100% {
opacity: 1;
}
50% {
opacity: 0.5;
}
}

最后将uptime放到_config.solitude.yml文件中的asidehomenoSticky中即可

至此,教程结束,一键三连就可以看到最终的效果啦