前言

之前用WordPress的时候就用cloudflare检测友链的状态,现在换到hexo了,就想到用github Action来解决这一事情

效果

友链检测

教程

首先就是添加links.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
const fs = require('fs');
const path = require('path');

hexo.extend.generator.register('links-api', function(locals) {
const linksData = hexo.locals.get('data').links;

if (!linksData) {
return;
}

// 获取实际的链接数据
const actualLinksData = linksData.links;

if (!actualLinksData || !Array.isArray(actualLinksData)) {
return;
}

// 查找"小伙伴"分类
let xiaohuobanCategory = null;

for (const category of actualLinksData) {
if (category.class_name === '小伙伴') {
xiaohuobanCategory = category;
break;
}
}

if (!xiaohuobanCategory || !xiaohuobanCategory.link_list) {
return;
}

// 构建API数据 - 转换为指定格式
const friends = xiaohuobanCategory.link_list.map(link => [
link.name, // 网站名称
link.link, // 网站链接
link.avatar // 网站图标
]);

const apiData = {
friends: friends
};

// 确保目录存在
const apiDir = path.join(hexo.public_dir, 'api');
if (!fs.existsSync(apiDir)) {
fs.mkdirSync(apiDir, { recursive: true });
}

// 写入JSON文件
const filePath = path.join(apiDir, 'links.json');
fs.writeFileSync(filePath, JSON.stringify(apiData, null, 2), 'utf8');

return {
path: '/links.json',
data: JSON.stringify(apiData, null, 2)
};
});

此代码的作用就是生成以小伙伴分类为基础的友链json文件,你可以根据实际情况进行修改

然后就是到github中fork我的项目links-status到自己的仓库

点击编辑config.yml中的内容就可以修改设置了。

找到Actions找到检测友链状态的脚本点击RUN workflow就可以了

因为不会写代码,一切都是AI写的,所以在生成文件到page分支的时候很慢,大约需要三分钟

构建好以后可以用vercel,edgeone pages等平台选择page分支部署就可以了,然后将代码中的json文件地址替换成你自己的

如果你有更好的方法可以留言告诉我,一起进步!~

最后就是solitude主题怎么引入呢?

很简单,就是在source文件夹的js文件夹中添加一个links-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
// 友链状态检测脚本
let retryCount = 0;
const MAX_RETRIES = 3;
const STATUS_URL = 'https://links.ityr.xyz/status.json';

function fetchLinkStatus() {
return fetch(STATUS_URL)
.then(response => {
if (!response.ok) {
throw new Error('网络请求失败');
}
return response.json();
});
}

function updateLinkStatus(data) {
if (data && data.link_status) {
const statusMap = {};
data.link_status.forEach(link => {
statusMap[link.name] = link;
});

// 更新每个链接的状态
const statusElements = document.querySelectorAll('.site-card-status');
statusElements.forEach(el => {
const linkName = el.getAttribute('data-name');
if (statusMap[linkName]) {
const status = statusMap[linkName];
let statusClass = '';
let statusText = '';

// 根据success字段和latency判断状态
if (!status.success || status.latency === -1) {
statusClass = 'status-error';
const errorCount = status.error_count || 0;
statusText = "异常[" + errorCount + "]";
} else {
// 显示响应时间
const latency = status.latency;
if (latency <= 3) {
statusClass = 'status-normal';
} else {
statusClass = 'status-slow';
}
statusText = latency + 's';
}

// 更新状态
el.className = 'site-card-status ' + statusClass;
el.textContent = statusText;
el.classList.remove('status-loading');
}
});
} else {
throw new Error('无效的状态数据');
}
}

function handleError() {
retryCount++;
if (retryCount <= MAX_RETRIES) {
setTimeout(fetchAndUpdateStatus, 2000 * retryCount); // 2秒、4秒、6秒后重试
} else {
// 将所有状态设为错误
document.querySelectorAll('.site-card-status.status-loading').forEach(el => {
el.className = 'site-card-status status-error';
el.textContent = '获取失败';
});
}
}

function fetchAndUpdateStatus() {
fetchLinkStatus()
.then(data => updateLinkStatus(data))
.catch(error => {
console.error('获取友链状态失败:', error);
handleError();
});
}

// 页面加载后立即执行
document.addEventListener('DOMContentLoaded', fetchAndUpdateStatus);

// 支持PJAX重新加载
document.addEventListener('pjax:complete', fetchAndUpdateStatus);

再然后到_config.solitude.yml文件的body中引入该文件,并且在自定义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
.site-card {
position: relative
}

.flink-list-item {
position: relative
}

.site-card-status {
position: absolute;
bottom: 0;
right: 0;
z-index: 10;
padding: 3px 8px;
border-radius: 8px 0 8px 0;
font-size: 12px;
font-weight: 500;
color: #fff;
background-color: rgba(0, 0, 0, 0.6);
backdrop-filter: blur(4px);
transition: all 0.3s ease
}

.site-card-status.status-loading {
background-color: rgba(100, 100, 100, 0.8);
animation: pulse 1.5s infinite
}

.site-card-status.status-normal {
background-color: rgba(82, 196, 26, 0.9)
}

.site-card-status.status-slow {
background-color: rgba(250, 173, 20, 0.9)
}

.site-card-status.status-error {
background-color: rgba(255, 77, 79, 0.9)
}

@keyframes pulse {

0%,
100% {
opacity: 1
}

50% {
opacity: 0.5
}
}

[data-theme="dark"] .site-card-status {
background-color: rgba(255, 255, 255, 0.1);
color: #fff
}

[data-theme="dark"] .site-card-status.status-loading {
background-color: rgba(100, 100, 100, 0.8)
}

[data-theme="dark"] .site-card-status.status-normal {
background-color: rgba(82, 196, 26, 0.8)
}

[data-theme="dark"] .site-card-status.status-slow {
background-color: rgba(250, 173, 20, 0.8)
}

[data-theme="dark"] .site-card-status.status-error {
background-color: rgba(255, 77, 79, 0.8)
}

.flink-templates {
margin-top: 2rem;
padding: 1.5rem;
background: var(--efu-card-bg);
border-radius: 12px;
border: var(--style-border-always);
box-shadow: var(--efu-shadow-border)
}

.flink-templates h3 {
margin-bottom: 1rem;
font-size: 1.2rem;
font-weight: 600;
color: var(--efu-fontcolor)
}

.template-buttons {
display: flex;
gap: 1rem;
flex-wrap: wrap
}

.template-btn {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.75rem 1.5rem;
border: none;
border-radius: 8px;
font-size: 14px;
font-weight: 500;
cursor: pointer;
transition: all 0.3s ease;
background: var(--efu-theme);
color: #fff
}

.template-btn:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15)
}

.template-btn i {
font-size: 16px
}

.template-btn.markdown-btn {
background: #10b981
}

.template-btn.markdown-btn:hover {
background: #059669
}

[data-theme="dark"] .flink-templates {
background: var(--efu-card-bg);
border-color: var(--efu-border)
}

[data-theme="dark"] .template-btn {
background: var(--efu-theme)
}

[data-theme="dark"] .template-btn.markdown-btn {
background: #10b981
}

最后就是一键三连即可看到效果了