diff --git a/scripts/check_links.js b/scripts/check_links.js new file mode 100644 index 00000000..cefbe146 --- /dev/null +++ b/scripts/check_links.js @@ -0,0 +1,139 @@ +const fs = require('fs'); +const path = require('path'); + +// Configuration +const LINKS_FILE = path.join(__dirname, '../themes/Ying/static/json/link_lite.json'); +const CONCURRENCY = 10; // Check 10 links at a time +const TIMEOUT = 10000; // 10 seconds timeout + +async function checkLink(link) { + const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort(), TIMEOUT); + + try { + // Some blogs block headless requests, so we fake a User-Agent + const response = await fetch(link.url, { + method: 'HEAD', // Try HEAD first to save bandwidth + signal: controller.signal, + headers: { + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36' + } + }); + + clearTimeout(timeoutId); + + if (response.ok) { + return { link, status: 'OK', code: response.status }; + } else if (response.status === 405 || response.status === 403 || response.status === 503) { + // Some servers reject HEAD or block bots, try GET + const controllerGet = new AbortController(); + const timeoutIdGet = setTimeout(() => controllerGet.abort(), TIMEOUT); + try { + const responseGet = await fetch(link.url, { + method: 'GET', + signal: controllerGet.signal, + headers: { + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36' + } + }); + clearTimeout(timeoutIdGet); + if (responseGet.ok) { + return { link, status: 'OK', code: responseGet.status }; + } else { + return { link, status: 'FAIL', code: responseGet.status }; + } + } catch (err) { + clearTimeout(timeoutIdGet); + return { link, status: 'ERROR', error: err.message }; + } + } else { + return { link, status: 'FAIL', code: response.status }; + } + } catch (error) { + clearTimeout(timeoutId); + // Retry with GET if HEAD failed with network error (sometimes happens) + if (error.name !== 'AbortError') { + const controllerGet = new AbortController(); + const timeoutIdGet = setTimeout(() => controllerGet.abort(), TIMEOUT); + try { + const responseGet = await fetch(link.url, { + method: 'GET', + signal: controllerGet.signal, + headers: { + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36' + } + }); + clearTimeout(timeoutIdGet); + if (responseGet.ok) { + return { link, status: 'OK', code: responseGet.status }; + } else { + return { link, status: 'ERROR', error: error.message }; // Return original error if GET also fails or is not tried properly + } + } catch (err) { + clearTimeout(timeoutIdGet); + return { link, status: 'ERROR', error: error.message }; + } + } + return { link, status: 'ERROR', error: error.message }; + } +} + +async function main() { + console.log('Starting friend link check...'); + console.log(`Reading links from: ${LINKS_FILE}`); + + if (!fs.existsSync(LINKS_FILE)) { + console.error('Error: Link file not found!'); + process.exit(1); + } + + let links; + try { + const fileContent = fs.readFileSync(LINKS_FILE, 'utf8'); + links = JSON.parse(fileContent); + } catch (e) { + console.error('Error parsing JSON:', e.message); + process.exit(1); + } + + console.log(`Found ${links.length} links. Checking...`); + + const results = []; + const failedLinks = []; + + // Process in chunks + for (let i = 0; i < links.length; i += CONCURRENCY) { + const chunk = links.slice(i, i + CONCURRENCY); + const chunkResults = await Promise.all(chunk.map(checkLink)); + + chunkResults.forEach(res => { + if (res.status === 'OK') { + process.stdout.write('.'); // Progress indicator + } else { + process.stdout.write('F'); + failedLinks.push(res); + } + results.push(res); + }); + } + + console.log('\n\nCheck completed!'); + console.log('---------------------------------------------------'); + + if (failedLinks.length === 0) { + console.log('✅ All links are accessible!'); + } else { + console.log(`⚠️ Found ${failedLinks.length} inaccessible links:\n`); + failedLinks.forEach((item, index) => { + console.log(`${index + 1}. ${item.link.name} (${item.link.url})`); + if (item.code) { + console.log(` Status Code: ${item.code}`); + } else { + console.log(` Error: ${item.error}`); + } + console.log(''); + }); + } +} + +main(); diff --git a/themes/Ying/layouts/_default/new-links-lite.html b/themes/Ying/layouts/_default/new-links-lite.html index 56176cc2..c92b9574 100644 --- a/themes/Ying/layouts/_default/new-links-lite.html +++ b/themes/Ying/layouts/_default/new-links-lite.html @@ -70,13 +70,49 @@
+ + {{ end }} diff --git a/themes/Ying/layouts/_default/new-links.html b/themes/Ying/layouts/_default/new-links.html index 6ed9dca7..3a5404f7 100644 --- a/themes/Ying/layouts/_default/new-links.html +++ b/themes/Ying/layouts/_default/new-links.html @@ -1,7 +1,8 @@ {{ define "main" }}
-
+ {{ end }} \ No newline at end of file diff --git a/themes/Ying/static/json/link_lite.json b/themes/Ying/static/json/link_lite.json new file mode 100644 index 00000000..9df65f21 --- /dev/null +++ b/themes/Ying/static/json/link_lite.json @@ -0,0 +1,242 @@ +[ + { + "name": "吾柯", + "url": "https://blog.keepke.com", + "description": "保持思考与理性", + "image": "https://blog.keepke.com/logo.png" + }, + { + "name": "叶开", + "url": "https://qq.md", + "description": "福建三明尤溪人士", + "image": "https://cravatar.cn/avatar/4741124456fb02fa4da30494dbc6c99e?d=monsterid&s=80" + }, + { + "name": "一泽", + "url": "https://yang99.fun", + "description": "感谢相识", + "image": "https://q.qlogo.cn/g?b=qq&nk=3369963811@qq.com&s=100" + }, + { + "name": "七栀", + "url": "https://qydzz.cn", + "description": "用文字记录那些时间带走的东西", + "image": "https://qydzz.cn/logo.png" + }, + { + "name": "若志随笔", + "url": "https://www.rz.sb", + "description": "一个若志的网络自留地", + "image": "https://q1.qlogo.cn/g?b=qq&nk=80360650&s=640" + }, + { + "name": "黑桃三", + "url": "https://heitaosan.com", + "description": "逆着光行走,任风吹雨打", + "image": "https://s2.ax1x.com/2020/01/03/lUmf81.png" + }, + { + "name": "十七的小栈", + "url": "https://blog.allnull.cn", + "description": "别让世俗淹没了生活", + "image": "https://cravatar.cn/avatar/56238a7f177c534928fd3cf4161ab59e?s=65&r=G&d=mm" + }, + { + "name": "Vian", + "url": "https://vian.top", + "description": "想要的都拥有,得不到的都释怀!", + "image": "https://www.vian.top/avatar.ico" + }, + { + "name": "油油", + "url": "https://blog.200011.net", + "description": "A website for myself", + "image": "https://q1.qlogo.cn/g?b=qq&nk=1376652515&s=640" + }, + { + "name": "段先森", + "url": "https://duanxiansen.com", + "description": "迄今所有人生都大写着失败,但并不妨碍我继续向前", + "image": "https://q.qlogo.cn/g?b=qq&nk=2443266417@qq.com&s=100" + }, + { + "name": "Fivk博客", + "url": "https://blog.fivk.cn", + "description": "相逢的人会再相逢", + "image": "https://q1.qlogo.cn/g?b=qq&nk=11470226&s=640" + }, + { + "name": "百品博客", + "url": "https://baipin.pw", + "description": "我欲穿花寻路,直入白云深处,浩气展虹霓", + "image": "https://www.baipin.pw/wp-content/uploads/2019/07/cropped-ABUIABACGAAg_bP2wwUorbiplQQwgAU4gAU.jpg" + }, + { + "name": "WhiteFan", + "url": "https://www.baifan97.cn", + "description": "一个斜杠青年的碎碎念", + "image": "https://ypy.baifan97.cn/typecho/uploads/img/avatar.png" + }, + { + "name": "北枫", + "url": "https://beifeng.me", + "description": "记录一个人的生活点滴", + "image": "https://q1.qlogo.cn/g?b=qq&nk=41777657&s=640" + }, + { + "name": "团团生活志", + "url": "https://t-t.live", + "description": "爱生活 爱分享", + "image": "https://img.t-t.live/img/tx.png" + }, + { + "name": "臭袜子", + "url": "https://huziyan.com", + "description": "臭袜子", + "image": "https://huziyan.com/usr/themes/icefox/assets/img/tx.jpg" + }, + { + "name": "岳泽以", + "url": "https://www.yuezeyi.com", + "description": "我们历经苦难,我们生生不息", + "image": "https://q2.qlogo.cn/g?b=qq&nk=2780008080&s=100" + }, + { + "name": "三石的记录", + "url": "https://www.fllv.cn", + "description": "记录生活", + "image": "https://cdn.helingqi.com/wavatar/a25a7640aa68e6d26e32afee8120f7e5?s=65&r=G&d=" + }, + { + "name": "竹春廿柒", + "url": "https://mojinxi.cn", + "description": "相视而笑,莫逆于心。", + "image": "https://mojinxi.cn/img/logo.png" + }, + { + "name": "大嘴狗狗", + "url": "https://www.sirit.com.cn", + "description": "真正的幸福在于对平淡生活的热爱", + "image": "https://www.sirit.com.cn/siritlogo.png" + }, + { + "name": "半截の诗", + "url": "https://sweetjing.cc/", + "description": "一切都会好起来,然后走向正轨,保持热爱,奔赴山海。", + "image": "https://q1.qlogo.cn/g?b=qq&nk=1486823198&s=640" + }, + { + "name": "XIAOMING'S BLOG", + "url": "https://vimin.cc", + "description": "记录生活点滴,谱写精彩生活!", + "image": "https://cravatar.cn/avatar/6eb95597489dd26f15a973e667d71aef?s=100&r=G&d=" + }, + { + "name": "网友小宋", + "url": "https://xyzbz.cn", + "description": "一个九三年的老青年", + "image": "https://cravatar.cn/avatar/120340d1df519f4e28613fe5d404b286?s=65&r=G&d=" + }, + { + "name": "微风忆夏", + "url": "https://sumiler.com", + "description": "微风轻轻起 夏日不再来", + "image": "https://cravatar.cn/avatar//a35f5ef7a4bce3b7cfa6f538ffcae7aa" + }, + { + "name": "林林杂语", + "url": "https://www.xiaozonglin.cn", + "description": "一位来自福建泉州的高中生", + "image": "https://cravatar.cn/avatar/04bfc914bd50b42eb507bf66aad0cc31?d=mp" + }, + { + "name": "西瓜撞地球", + "url": "https://www.bio-w.cn", + "description": "我在人间贩卖彩虹", + "image": "https://bu.dusays.com/2022/10/17/634d313602a1e.png" + }, + { + "name": "Kris Yan", + "url": "https://blog.krisyan.dev", + "description": "無限進步 @Schale夏萊", + "image": "https://cravatar.cn/avatar/05729ab2ca3b99203a674859f77d592b?&d=mp" + }, + { + "name": "Echo 的小窝", + "url": "https://www.liveout.cn/", + "description": "漂泊于互联网中的小窝", + "image": "https://cravatar.cn/avatar/d9d83d9ebd03447fff3b094e140a0cbe" + }, + { + "name": "未来往事", + "url": "https://felixway.cn", + "description": "漂泊于互联网中的小窝", + "image": "https://cravatar.cn/avatar/2ee35215cbea58cd8b2605e7f517d689?&d=mp" + }, + { + "name": "小稚生活志", + "url": "https://ihello.cc", + "description": "心存阳光,必有诗和远方", + "image": "https://bu.dusays.com/2024/02/02/65bbce27b129a.jpg" + }, + { + "name": "风呼呼", + "url": "https://whooc.com", + "description": "寒冻三尺非此界,烈日焦灼正午时!", + "image": "https://whooc.com/logo" + }, + { + "name": "柳橙睿", + "url": "https://www.liuchengrui.cn", + "description": "其实我是一个假柳橙", + "image": "https://www.liuchengrui.cn/myphoto.png" + }, + { + "name": "永恒末匕", + "url": "https://likaijian.com", + "description": "留下瞬间,记录永恒", + "image": "https://q.qlogo.cn/headimg_dl?dst_uin=1527335101&spec=640" + }, + { + "name": "刘郎阁", + "url": "https://vjo.cc", + "description": "一个积极向上的生活探索者!", + "image": "https://yjvc.cn/usr/uploads/logo.jpeg" + }, + { + "name": "浮沉流年", + "url": "https://www.fcln.net", + "description": "借一点落日的光与来日方长", + "image": "https://cravatar.cn/avatar//4355eea603309a0c159ca27f54e27c89?s=65&r=G&d=" + }, + { + "name": "二十六", + "url": "https://www.ershiliu.com/", + "description": "人生如逆旅,我亦是行人。", + "image": "https://www.ershiliu.com/favicon.ico" + }, + { + "name": "阿呆日记", + "url": "https://dai.ge", + "description": "肥宅的摆烂生活", + "image": "https://dai.ge/img/logo.png" + }, + { + "name": "邹江博客", + "url": "https://www.zoujiang.com", + "description": "热爱你来过的每度温暖", + "image": "https://www.zoujiang.com/logo.jpg" + }, + { + "name": "清梦云熙", + "url": "https://skylan.cc", + "description": "相逢何必曾相识", + "image": "https://cravatar.cn/avatar/853572275a978dbccefbe2242ba1c415" + }, + { + "name": "如约而至", + "url": "https://aloner.ink", + "description": "随便写写", + "image": "https://cravatar.cn/avatar/af1874b7856255302fc3df5102a6ce6d" + } +] \ No newline at end of file