前言
之前就写了用cloudflare检测友链的状态,但是cloudflare最近访问有点困难,就想到用github Action来解决这一事情
效果

教程
首先就是添加links.js文件,并且添加如下代码
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的文件,并且放入代码
// 友链状态检测脚本
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文件中添加代码
.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
}
最后就是一键三连即可看到效果了