归档 artalk-cf 评论后端 + rss-robot 到 blog-admin(含技术选型/模块分布 README)
Deploy to Production / pre-check (push) Successful in 58s
Deploy to Production / build (push) Successful in 4m3s
Deploy to Production / deploy-edgeone (push) Successful in 3m48s
Deploy to Production / finalize (push) Successful in 26s
Deploy to Production / notify-failure (push) Skipped

This commit is contained in:
zqlit committed 2026-10-04 08:45:40 +08:00
1 parent 855a001046
commit a74b3c7127
98 files changed
+15657

No files matched your search

+66
View File
@@ -0,0 +1,66 @@
// 验证卡片评论的自愈逻辑:即使 Artalk 重渲染把 .atk-main 的内联 display 清掉,
// 只靠 class + !important 的 CSS 规则也仍然隐藏(不再把卡片挤下去)
import { spawn } from 'node:child_process';
import { mkdtempSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
const CHROME = 'C:/Users/QunLIn/.agent-browser/browsers/chrome-154.0.8037.92/chrome.exe';
const PORT = 9377;
const URL_ = process.argv[2] || 'http://127.0.0.1:1313/comment.html';
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
const userDir = mkdtempSync(join(tmpdir(), 'cdp-selfheal-'));
const chrome = spawn(CHROME, ['--headless=new', `--remote-debugging-port=${PORT}`, `--user-data-dir=${userDir}`,
'--no-first-run', '--disable-gpu', `--window-size=${process.argv[4] || '1280,900'}`, 'about:blank'], { stdio: 'ignore' });
const PROBE = `(() => {
const holder = document.querySelector('.atk-fl-card-wrap');
const card = document.querySelector('.atk-fl-card');
const wrap = document.querySelector('.atk-comment-wrap.atk-fl-apply');
const firstCmt = document.querySelector('.atk-list-comments-wrap > .atk-comment-wrap');
const r = (el) => { if (!el) return null; const b = el.getBoundingClientRect(); return { x: Math.round(b.x), y: Math.round(b.y), w: Math.round(b.width), h: Math.round(b.height) }; };
return JSON.stringify({
holderParent: holder && holder.parentNode ? holder.parentNode.className : null,
holderIsFirstChild: holder && holder.parentNode ? holder.parentNode.firstElementChild === holder : null,
card: r(card),
cardDisplay: card ? getComputedStyle(card).display : null,
cardCount: document.querySelectorAll('.atk-fl-card').length,
cardBorderRadiusBtn: card ? getComputedStyle(card.querySelector('.atk-fl-btn')).borderRadius : null,
transformedWrapDisplay: wrap ? getComputedStyle(wrap).display : 'no transformed comment',
firstComment: r(firstCmt),
avatarDisplay: (() => { const a = card ? card.querySelector('.atk-fl-avatar') : null; return a ? getComputedStyle(a).display : null; })(),
cardFontSize: card ? getComputedStyle(card.querySelector('.atk-fl-desc')).fontSize : null,
cardLetterSpacing: card ? getComputedStyle(card.querySelector('.atk-fl-desc')).letterSpacing : null,
cardPadding: card ? getComputedStyle(card).padding : null,
cardGap: card ? getComputedStyle(card).gap : null,
cardBadge: (() => { const b = card ? card.querySelector('.atk-badge') : null; if (!b) return null; const cs = getComputedStyle(b); return { text: b.textContent.trim(), color: cs.color, background: cs.backgroundColor, h: b.offsetHeight, padding: cs.padding, display: cs.display, fontWeight: cs.fontWeight }; })(),
}, null, 1);
})()`;
try {
for (let i = 0; i < 40; i++) { try { const r = await fetch(`http://127.0.0.1:${PORT}/json/version`); if (r.ok) break; } catch {} await sleep(300); }
const tab = await (await fetch(`http://127.0.0.1:${PORT}/json/new?${encodeURIComponent(URL_)}`, { method: 'PUT' })).json();
const ws = new WebSocket(tab.webSocketDebuggerUrl);
let id = 0; const pending = new Map();
ws.addEventListener('message', (ev) => { const m = JSON.parse(ev.data); if (m.id && pending.has(m.id)) { pending.get(m.id)(m); pending.delete(m.id); } });
await new Promise((res) => ws.addEventListener('open', res));
const send = (method, params = {}) => new Promise((res) => { const mid = ++id; pending.set(mid, res); ws.send(JSON.stringify({ id: mid, method, params })); });
await send('Runtime.enable');
await send('Page.enable');
await sleep(5000);
// 移动端评论可能是进入视口才加载,先滚到评论区
await send('Runtime.evaluate', { expression: `(() => { const el = document.querySelector('#Comments'); if (el) el.scrollIntoView({ block: 'center' }); return 'ok'; })()`, returnByValue: true });
await sleep(3000);
const r = await send('Runtime.evaluate', { expression: PROBE, returnByValue: true });
console.log(r.result.result.value);
if (process.argv[3]) {
const shot = await send('Page.captureScreenshot', { format: 'png' });
const { writeFileSync } = await import('node:fs');
writeFileSync(process.argv[3], Buffer.from(shot.result.data, 'base64'));
}
ws.close();
} catch (e) {
console.error('ERROR', e.message);
} finally {
chrome.kill();
}