added 文章一篇
This commit is contained in:
1 parent
834a40819c
commit
7d7e5f12fe
9 files changed
+478
-151
No files matched your search
+59
-139
@@ -1,156 +1,76 @@
|
||||
const fs = require('fs');
|
||||
const yaml = require('js-yaml');
|
||||
const axios = require('axios');
|
||||
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
|
||||
const linksPath = path.join(__dirname, '../themes/Ying/data/links.yaml');
|
||||
const outputPath = path.join(__dirname, '../static/link_status.json');
|
||||
|
||||
async function checkLink(link) {
|
||||
const controller = new AbortController();
|
||||
const timeoutId = setTimeout(() => controller.abort(), TIMEOUT);
|
||||
|
||||
// Ensure static dir exists
|
||||
if (!fs.existsSync(path.dirname(outputPath))) {
|
||||
fs.mkdirSync(path.dirname(outputPath), { recursive: true });
|
||||
}
|
||||
|
||||
async function checkLinks() {
|
||||
console.log('Starting link check...');
|
||||
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'
|
||||
}
|
||||
});
|
||||
const fileContents = fs.readFileSync(linksPath, 'utf8');
|
||||
const data = yaml.load(fileContents);
|
||||
|
||||
clearTimeout(timeoutId);
|
||||
if (!data || !data.blogs) {
|
||||
console.log('No blogs found in links.yaml');
|
||||
return;
|
||||
}
|
||||
|
||||
const results = {};
|
||||
// Process in chunks to avoid too many concurrent connections
|
||||
const batchSize = 10;
|
||||
const blogs = data.blogs;
|
||||
|
||||
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'
|
||||
for (let i = 0; i < blogs.length; i += batchSize) {
|
||||
const batch = blogs.slice(i, i + batchSize);
|
||||
await Promise.all(batch.map(async (blog) => {
|
||||
if (!blog.url) return;
|
||||
try {
|
||||
const response = await axios.head(blog.url, {
|
||||
timeout: 10000,
|
||||
maxRedirects: 5,
|
||||
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
|
||||
});
|
||||
results[blog.url] = true;
|
||||
console.log(`[OK] ${blog.name}`);
|
||||
} catch (error) {
|
||||
// Retry with GET if HEAD fails (some servers block HEAD)
|
||||
try {
|
||||
console.log(`[RETRY] ${blog.name} (HEAD failed, trying GET)...`);
|
||||
await axios.get(blog.url, {
|
||||
timeout: 10000,
|
||||
maxRedirects: 5,
|
||||
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
|
||||
});
|
||||
results[blog.url] = true;
|
||||
console.log(`[OK] ${blog.name} (GET success)`);
|
||||
} catch (err2) {
|
||||
results[blog.url] = false;
|
||||
console.log(`[FAIL] ${blog.name} - ${error.message}`);
|
||||
}
|
||||
});
|
||||
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}`);
|
||||
fs.writeFileSync(outputPath, JSON.stringify(results, null, 2));
|
||||
console.log(`Link status saved to ${outputPath}`);
|
||||
|
||||
if (!fs.existsSync(LINKS_FILE)) {
|
||||
console.error('Error: Link file not found!');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
let linksData;
|
||||
let links;
|
||||
try {
|
||||
const fileContent = fs.readFileSync(LINKS_FILE, 'utf8');
|
||||
linksData = JSON.parse(fileContent);
|
||||
// Adapt to both array (old) and object with friends key (new)
|
||||
if (Array.isArray(linksData)) {
|
||||
links = linksData;
|
||||
} else if (linksData.friends && Array.isArray(linksData.friends)) {
|
||||
// The new format has [name, url, avatar] arrays inside friends array
|
||||
// We need to convert them to objects {name, url} for the checker
|
||||
links = linksData.friends.map(item => {
|
||||
if (Array.isArray(item)) {
|
||||
return { name: item[0], url: item[1] };
|
||||
}
|
||||
return item;
|
||||
});
|
||||
} else {
|
||||
console.error('Error: Unknown JSON format');
|
||||
process.exit(1);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Error parsing JSON:', e.message);
|
||||
console.error(e);
|
||||
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();
|
||||
checkLinks();
|
||||
Reference in new issue
Block a user