7 lines
3.2 KiB
JavaScript
7 lines
3.2 KiB
JavaScript
const $=s=>document.querySelector(s), state={results:[],filter:'all',domain:'',type:''};
|
|
$('#theme').onclick=()=>{document.body.classList.toggle('light');localStorage.theme=document.body.classList.contains('light')?'light':'dark'};if(localStorage.theme==='light')document.body.classList.add('light');
|
|
$('#form').onsubmit=async e=>{e.preventDefault();$('#error').textContent='';$('#submit').disabled=true;state.results=[];render();try{const res=await fetch('/api/dns/query',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({domain:$('#domain').value,type:$('#type').value})});if(!res.ok)throw new Error(await res.text());const q=await res.json();state.domain=q.domain;state.type=q.type;$('#queryTitle').textContent=`${q.domain} / ${q.type}`;$('#dashboard').hidden=false;$('#empty').hidden=true;const es=new EventSource(`/api/dns/query/${q.queryId}/events`);es.addEventListener('result',x=>{state.results.push(JSON.parse(x.data));render()});es.addEventListener('complete',()=>{es.close();$('#submit').disabled=false;$('#finished').textContent='COMPLETED '+new Date().toLocaleTimeString()})}catch(err){$('#error').textContent=err.message.trim();$('#submit').disabled=false}};
|
|
function render(){const rs=state.results,ok=rs.filter(x=>x.status==='success'),bad=rs.filter(x=>x.status!=='success');$('#progress').textContent=`${rs.length} / 20`;$('#bar').style.width=`${rs.length*5}%`;$('#success').textContent=ok.length;$('#failed').textContent=bad.length;const signatures=new Set(ok.map(x=>x.records.map(r=>r.value).sort().join('|')));$('#consistent').textContent=!ok.length?'分析中':signatures.size===1?'一致':'不一致';$('#consistent').style.color=signatures.size>1?'var(--amber)':'var(--green)';let shown=rs.filter(x=>state.filter==='all'||state.filter==='success'&&x.status==='success'||state.filter==='issue'&&x.status!=='success');$('#rows').innerHTML=shown.map((x,i)=>`<tr><td><b>${esc(x.country)} · ${esc(x.city)}</b><small>${esc(x.region)} / ${esc(x.resolver)}</small></td><td><span class="status ${x.status==='error'?'error-s':x.status+'-s'}">${label(x.status)}</span></td><td>${x.records.length?x.records.map(r=>`<code>${esc(r.value)}</code>`).join('<br>'):'<span style="color:var(--muted)">—</span>'}</td><td>${x.records[0]?.ttl??'—'}</td><td>${x.responseTime?x.responseTime+' ms':'—'}</td><td><button class="copy" data-i="${state.results.indexOf(x)}">⧉</button></td></tr>`).join('');document.querySelectorAll('.copy').forEach(b=>b.onclick=()=>copyResult(state.results[+b.dataset.i]))}
|
|
function label(s){return({success:'成功',timeout:'超时',empty:'无记录',error:'失败'})[s]||s}function esc(s){return String(s).replace(/[&<>"']/g,c=>({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c]))}function copyResult(x){navigator.clipboard.writeText(`${x.resolver}\t${x.status}\t${x.records.map(r=>r.value).join(', ')}`)}
|
|
$('#filters').onclick=e=>{if(!e.target.dataset.filter)return;document.querySelectorAll('#filters button').forEach(x=>x.classList.remove('active'));e.target.classList.add('active');state.filter=e.target.dataset.filter;render()};$('#copyAll').onclick=()=>navigator.clipboard.writeText(state.results.map(x=>`${x.country}/${x.city}\t${x.resolver}\t${label(x.status)}\t${x.records.map(r=>r.value).join(', ')}\t${x.responseTime}ms`).join('\n'));
|