This commit is contained in:
Mateusz Gruszczyński
2026-05-08 23:34:22 +02:00
parent c331dcf41f
commit da17facf39
3 changed files with 53 additions and 19 deletions

View File

@@ -862,14 +862,13 @@
return `<details class="automation-history-details"><summary>${summary||'No actions'}</summary><pre>${details}</pre></details>`;
}
function renderAutomationHistory(hist=[], smartStats=automationSmartQueueStats){
function renderAutomationHistory(hist=[]){
if(!$('automationHistory')) return;
const stats=renderSmartQueueNerdStats(smartStats);
const toolbar='<div class="automation-history-toolbar"><button id="automationClearHistoryBtn" class="btn btn-xs btn-outline-danger" type="button"><i class="fa-solid fa-trash"></i> Clear history</button></div>';
const rows=hist.map(h=>[humanDateCell(h.created_at),esc(h.rule_name||''),esc(h.torrent_name||h.torrent_hash||''),automationHistoryActions(h.actions_json||'')]);
// Note: Automation history uses the shared responsive table wrapper so it stays inside narrow mobile modals.
const body=hist.length?responsiveTable(['Time','Rule','Torrent / batch','Actions'],rows,'automation-history-table'):'<div class="empty-mini">No automation history yet.</div>';
$('automationHistory').innerHTML=stats+toolbar+body;
$('automationHistory').innerHTML=toolbar+body;
}
async function clearAutomationHistory(){
@@ -893,13 +892,8 @@
}
async function loadAutomations(){
const [j,smart]=await Promise.all([
fetch('/api/automations').then(r=>r.json()),
fetch('/api/smart-queue?history_limit=100').then(r=>r.json()).catch(()=>({}))
]);
const j=await fetch('/api/automations').then(r=>r.json());
const rules=j.rules||[], hist=j.history||[];
// Note: Automations only display Smart Queue diagnostics here; saving/checking rules remains unchanged.
automationSmartQueueStats=smart?.ok?buildSmartQueueNerdStats(smart.history||[], Number(smart.history_total||0)):null;
automationRulesCache=rules;
if($('automationManager')) $('automationManager').innerHTML=rules.length?rules.map(r=>{
const enabled=!!r.enabled;
@@ -908,7 +902,7 @@
const toggleClass=enabled?'btn-outline-warning':'btn-outline-success';
return `<div class="automation-row"><div class="automation-row-main"><div><b>${esc(r.name)}</b> ${enabled?'<span class="badge text-bg-success">on</span>':'<span class="badge text-bg-secondary">off</span>'}</div><div class="small text-muted automation-rule-summary">${esc(ruleSummary(r))} · cooldown ${esc(r.cooldown_minutes||0)} min</div></div><div class="automation-row-actions"><button class="btn btn-xs ${toggleClass} automation-toggle" data-id="${esc(r.id)}" type="button" title="${toggleTitle}"><i class="fa-solid ${toggleIcon}"></i></button><button class="btn btn-xs btn-outline-secondary automation-edit" data-id="${esc(r.id)}" type="button" title="Edit automation"><i class="fa-solid fa-pen"></i></button><button class="btn btn-xs btn-outline-danger automation-delete" data-id="${esc(r.id)}" type="button" title="Delete automation"><i class="fa-solid fa-trash"></i></button></div></div>`;
}).join(''):'<div class="empty-mini">No automation rules.</div>';
renderAutomationHistory(hist, automationSmartQueueStats);
renderAutomationHistory(hist);
}
async function toggleAutomationRule(rule){
@@ -1082,10 +1076,14 @@
const box=$('appStatusManager'); if(!box) return;
box.innerHTML='<span class="spinner-border spinner-border-sm"></span> Loading diagnostics...';
try{
const j=await (await fetch('/api/app/status')).json();
const [j,smart]=await Promise.all([
fetch('/api/app/status').then(r=>r.json()),
fetch('/api/smart-queue?history_limit=100').then(r=>r.json()).catch(()=>({ok:false}))
]);
if(!j.ok) throw new Error(j.error||'Failed to load diagnostics');
const st=j.status||{}, py=st.pytorrent||{}, scgi=st.scgi||{}, profile=st.profile||{}, pc=st.port_check||{}, cleanup=st.cleanup||{}, db=cleanup.database||{};
const peaks=st.speed_peaks||{}, peakSession=peaks.session||{}, peakAllTime=peaks.all_time||{};
const smartStats=smart?.ok?buildSmartQueueNerdStats(smart.history||[], Number(smart.history_total||0)):null;
const cards=[
diagCard('pyTorrent PID', py.pid), diagCard('pyTorrent uptime', `${py.uptime_seconds||0}s`), diagCard('Memory RSS', py.memory_rss_h||py.memory_rss),
diagCard('Threads', py.threads), diagCard('CPU', `${py.cpu_percent ?? '-'}%`), diagCard('Jobs total', py.jobs_total),
@@ -1098,8 +1096,9 @@
diagCard('SCGI first byte', scgi.first_byte_ms!=null?`${scgi.first_byte_ms} ms`:'-'), diagCard('SCGI total', scgi.total_ms!=null?`${scgi.total_ms} ms`:'-'),
diagCard('Request bytes', scgi.request_bytes), diagCard('Response bytes', scgi.response_bytes), diagCard('XML bytes', scgi.xml_bytes), diagCard('rTorrent version', scgi.client_version||'-')
];
box.innerHTML=`<div class="diag-grid">${cards.join('')}</div>${scgi.error?`<div class="alert alert-danger mt-3 mb-0">${esc(scgi.error)}</div>`:''}`;
}catch(e){ box.innerHTML=`<div class="text-danger">${esc(e.message)}</div>`; }
const smartBlock=`<div class="section-title mt-3"><i class="fa-solid fa-list-check"></i> Smart Queue statistics</div>${renderSmartQueueNerdStats(smartStats)}`;
box.innerHTML=`<div class="diag-grid">${cards.join('')}</div>${smartBlock}${scgi.error?`<div class="alert alert-danger mt-3 mb-0">${esc(scgi.error)}</div>`:''}`;
}catch(e){ box.innerHTML=`<div class="alert alert-danger mb-0">${esc(e.message)}</div>`; }
}
function torrentStatsCard(label, value, note=''){