const fs = require('fs'); const yaml = require('js-yaml'); const axios = require('axios'); const path = require('path'); const https = require('https'); const http = require('http'); const linksPath = path.join(__dirname, '../themes/Ying/data/links.yaml'); const outputPath = path.join(__dirname, '../static/link_status.json'); // Ensure static dir exists if (!fs.existsSync(path.dirname(outputPath))) { fs.mkdirSync(path.dirname(outputPath), { recursive: true }); } // Config const CONCURRENCY = 10; const TIMEOUT = 10000; // Optimized Axios Instance with Keep-Alive const axiosInstance = axios.create({ timeout: TIMEOUT, maxRedirects: 5, httpAgent: new http.Agent({ keepAlive: true }), httpsAgent: new https.Agent({ keepAlive: true }), headers: { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36' }, validateStatus: (status) => status >= 200 && status < 400 }); async function checkUrl(url) { try { await axiosInstance.head(url); return true; } catch (error) { try { await axiosInstance.get(url); return true; } catch (err2) { return false; } } } async function checkLinks() { console.log('Starting link check...'); const start = Date.now(); try { const fileContents = fs.readFileSync(linksPath, 'utf8'); const data = yaml.load(fileContents); if (!data || !data.blogs) { console.log('No blogs found in links.yaml'); return; } const blogs = data.blogs.filter(b => b.url); const results = {}; const failedLinks = []; let completed = 0; const total = blogs.length; // Concurrency Pool async function worker() { while (blogs.length > 0) { const blog = blogs.shift(); const status = await checkUrl(blog.url); results[blog.url] = status; completed++; const icon = status ? '🟢' : '🔴'; console.log(`[${completed}/${total}] ${icon} ${blog.name}`); if (!status) { failedLinks.push(`- ${blog.name}: ${blog.url}`); } } } // Start workers const workers = Array(Math.min(blogs.length, CONCURRENCY)) .fill(null) .map(() => worker()); await Promise.all(workers); fs.writeFileSync(outputPath, JSON.stringify(results, null, 2)); const duration = ((Date.now() - start) / 1000).toFixed(2); console.log(`✨ Check finished in ${duration}s. Status saved to ${outputPath}`); // Write failed links to summary file for email notification const summaryPath = path.join(__dirname, '../failed_links_summary.txt'); if (failedLinks.length > 0) { const summaryContent = `\n🚫 监测到 ${failedLinks.length} 个异常友链:\n${failedLinks.join('\n')}\n`; fs.writeFileSync(summaryPath, summaryContent); console.log(`Failed links summary saved to ${summaryPath}`); } else { fs.writeFileSync(summaryPath, '\n✅ 所有友链状态正常\n'); } } catch (e) { console.error(e); process.exit(1); } } checkLinks();