async function jget(u){const r=await fetch(u);return r.json();} async function post(u){const r=await fetch(u,{method:'POST'});return r.json();} async function refresh(){ try{ const s=await jget('/api/stats'); el('m-total').textContent=s.total.toLocaleString('ru'); el('m-today').textContent=s.today.toLocaleString('ru'); const alive=Object.values(s.engines).filter(e=>e.alive).length; el('m-eng').textContent=alive+' / '+Object.keys(s.engines).length; el('m-ts').textContent=s.ts; // движки const box=el('eng');box.innerHTML=''; for(const [id,e] of Object.entries(s.engines)){ const c=document.createElement('div');c.className='card'; c.innerHTML=`
${id}
${e.desc}${e.auto?' · auto':''}
`; const btn=document.createElement('button'); btn.textContent=e.alive?'стоп':'старт'; if(e.alive)btn.className='stop'; btn.onclick=async()=>{e.alive?await post('/api/engine/'+id+'/stop'):await post('/api/engine/'+id+'/start');refresh();}; c.appendChild(btn);box.appendChild(c); } // статусы const st=el('t-status');st.innerHTML=''; const mx=Math.max(...s.by_status.map(x=>x.n),1); for(const r of s.by_status){ const tr=document.createElement('tr'); tr.innerHTML=`${r.status}${r.n.toLocaleString('ru')}
`; st.appendChild(tr); } // провайдеры const pv=el('t-prov');pv.innerHTML=''; for(const r of s.by_prov){ const tr=document.createElement('tr'); tr.innerHTML=`${r.prov}${r.n.toLocaleString('ru')}`; tr.style.cursor='pointer'; tr.onclick=()=>{el('f-prov').value=r.prov;loadKeys();}; pv.appendChild(tr); } loadKeys(); loadLog(); }catch(e){console.error(e);} } async function loadKeys(){ const st=el('f-status').value,pv=el('f-prov').value,lim=el('f-limit').value||50; const d=await jget(`/api/keys?status=${st}&prov=${pv}&limit=${lim}`); const t=el('t-keys');t.innerHTML=''; for(const r of d.rows){ const tr=document.createElement('tr'); tr.innerHTML=`${r.status}${r.prov} ${r.val}${r.repo||''}${r.found||''}`; t.appendChild(tr); } el('k-count').textContent=d.rows.length+' показано'; } async function loadLog(){ const r=await fetch('/api/log?engine='+el('f-log').value); el('log').textContent=await r.text(); el('log').scrollTop=el('log').scrollHeight; } function el(id){return document.getElementById(id);} function exportKeys(){const st=el('f-status').value,pv=el('f-prov').value; window.open(`/api/export?status=${st}&prov=${pv}`,'_blank');} refresh(); setInterval(refresh,5000);