-
+
- {{ .name }}{{ - .description }} +
- {{ .name }}{{ .description }} {{ end }}
-
+
{{ range site.Data.links.blogs }}
-
From b6f2b0aa51af8c257c3381122f0cea33175eb56b Mon Sep 17 00:00:00 2001 From: Vaica <94612053+zqlit@users.noreply.github.com> Date: Sun, 18 Jan 2026 23:35:56 +0800 Subject: [PATCH] =?UTF-8?q?added=EF=BC=9A=E5=8F=8B=E8=81=94=E6=9C=AC?= =?UTF-8?q?=E5=9C=B0=E5=92=8C=E8=BF=9C=E7=A8=8B=E9=87=8D=E5=86=99=E9=80=BB?= =?UTF-8?q?=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scripts/check_links.js | 139 ++++++++++ .../Ying/layouts/_default/new-links-lite.html | 42 ++- themes/Ying/layouts/_default/new-links.html | 53 +++- themes/Ying/static/json/link_lite.json | 242 ++++++++++++++++++ 4 files changed, 472 insertions(+), 4 deletions(-) create mode 100644 scripts/check_links.js create mode 100644 themes/Ying/static/json/link_lite.json 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 @@