first commit

This commit is contained in:
Longtail committed 2026-10-31 16:15:04 +08:00
commit 1e6504b4a2
41 files changed
+4964

No files matched your search

+79
View File
@@ -0,0 +1,79 @@
'use strict';
const $ = id => document.getElementById(id);
let token = '', running = false, starting = false, loadingChannels = false, loadedKey = '', lastLog = -1;
const labels = {idle:'等待开始',running:'下载中',completed:'已完成',partial:'未达到目标',cancelled:'已停止',error:'任务中断'};
const key = () => $('key').value.trim();
function controls() {
const busy = running || starting;
$('start').disabled = busy || !loadedKey || loadedKey !== key() || !$('channel').value;
$('stop').disabled = !running;
$('channels').disabled = busy || loadingChannels;
for (const id of ['key','channel','target']) $(id).disabled = busy || (id === 'channel' && !loadedKey);
}
async function post(path, body) {
const response = await fetch('/api/' + path, {method:'POST',headers:{'Content-Type':'application/json','X-Session-Token':token},body:JSON.stringify(body)});
const data = await response.json();
if (!response.ok) throw new Error(data.error || '请求失败');
return data;
}
$('key').addEventListener('input', () => {loadedKey=''; $('channel').replaceChildren(new Option('请重新获取频道','')); controls();});
$('channels').addEventListener('click', async () => {
loadingChannels=true; controls(); $('notice').textContent='正在获取频道…';
const submittedKey = key();
try {
const data = await post('channels',{key:submittedKey});
if (key() !== submittedKey) return;
$('channel').replaceChildren(...data.channels.map(c => new Option(c,c)));
loadedKey=submittedKey; $('notice').textContent=`已获取 ${data.channels.length} 个频道。`;
$('debug-channel').value=$('channel').value;
} catch(error) {$('notice').textContent=error.message;}
finally {loadingChannels=false; controls();}
});
$('channel').addEventListener('change', () => {$('debug-channel').value=$('channel').value; controls();});
$('download-form').addEventListener('submit', async event => {
event.preventDefault(); starting=true; controls(); $('notice').textContent='';
try {await post('start',{key:key(),channel:$('channel').value,target:Number($('target').value)}); await refresh();}
catch(error) {$('notice').textContent=error.message;}
finally {starting=false; controls();}
});
$('stop').addEventListener('click', async () => {
$('stop').disabled=true;
try {await post('stop',{}); $('notice').textContent='正在停止,等待当前请求或文章保存结束…';}
catch(error) {$('notice').textContent=error.message;}
});
$('debug-form').addEventListener('submit', async event => {
event.preventDefault(); $('debug-submit').disabled=true; $('debug-output').textContent='正在请求…';
try {
const data=await post('debug',{key:key(),endpoint:$('endpoint').value,channel:$('debug-channel').value,num:Number($('debug-num').value),start:Number($('debug-start').value)});
const output=JSON.stringify(data,null,2);
$('debug-output').textContent=output.slice(0,100000)+(output.length>100000?'\n…输出超过 100000 字符,已截断。':'');
} catch(error) {$('debug-output').textContent=error.message;}
finally {$('debug-submit').disabled=false;}
});
async function refresh() {
const response=await fetch('/api/state');
if (!response.ok) throw new Error('无法读取下载状态');
const data=await response.json();
running=data.status==='running'; controls();
const percent=data.target ? Math.floor(data.saved/data.target*100) : 0;
$('status').textContent=labels[data.status] || data.status;
$('progress-text').textContent=`下载新闻 ${data.saved}/${data.target} 条`;
$('percent').textContent=`已完成 ${percent}%`; $('progress').value=percent;
for (const id of ['saved','skipped','failed','requests','message','output']) $(id).textContent=data[id];
const latest=data.logs.length ? data.logs[data.logs.length-1].id : 0;
if (latest!==lastLog) {
const logs=$('logs'); const bottom=logs.scrollHeight-logs.scrollTop-logs.clientHeight<40;
logs.replaceChildren(...data.logs.map(log => {const row=document.createElement('div'); const stamp=document.createElement('time'); stamp.textContent=log.time; row.append(stamp,document.createTextNode(log.message)); return row;}));
if (bottom) logs.scrollTop=logs.scrollHeight;
lastLog=latest;
}
}
async function poll() {
try {await refresh();}
catch(error) {$('notice').textContent='与本地服务连接中断,请检查启动窗口。';}
setTimeout(poll,700);
}
(async () => {
try {const response=await fetch('/api/session'); token=(await response.json()).token; await poll();}
catch(error) {$('notice').textContent='初始化失败,请刷新页面。';}
})();