前言

之前就写了用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
}

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