diff --git a/static/dashboard.html b/static/dashboard.html index dd7a3a7..6eda1ac 100644 --- a/static/dashboard.html +++ b/static/dashboard.html @@ -33,57 +33,45 @@
-
Process:-
-
Scrape:--
-
Proxy:--
- -
+ +
Working Proxies
-
of - in database
-
Tests (Cumulative)
-
-
-
- passed / - failed
+
Active Workers
+
-
+
distributed nodes
-
Success Rate
-
-
-
-
-
-
Test Rate
-
-
-
tests/sec (60s)
-
-
-
Uptime
-
-
-
session duration
+
Combined Rate
+
-
+
tests/sec (all workers)
- +
-
Worker Pool
+
Worker Testing
-
Active Threads-
-
-
Job Queue-
-
Queue ETA-
+
Tests (Session)-
+
Working Found-
+
Success Rate-
+
Uptime-
-
Tor Pool
+
Scraper Network
-
Total Requests-
-
Success Rate-
-
Healthy Nodes-
-
Avg Latency-
+
Tor Requests-
+
Tor Success-
+
Engines Active-
+
Proxies Added-
@@ -91,17 +79,69 @@
- + + - +
+ +
+
+
Distributed Worker Nodes
+
+
+
Active
+
-
+
+
+
Total Tested
+
-
+
+
+
Working Found
+
-
+
+
+
Success Rate
+
-
+
+
+
+
+
Worker Status
+
+
+
No workers connected
+
Add workers with: python ppf.py --worker --server URL
+
+
+
+
+
Queue Status
+
+
+
Pending Tests
+
-
+
+
+
Claimed by Workers
+
-
+
+
+
Due for Retest
+
-
+
+
+
+
+ -
+
@@ -230,74 +270,9 @@
- -
- -
-
-
Judge Services
-
-
Available-
-
In Cooldown-
-
-
Top Performers
-
-
-
-
-
-
Anonymity Levels
-
-
-
-
Elite = no headers, Anonymous = adds headers, Transparent = reveals IP
-
-
- - -
-
Tor Exit Nodes
-
-
- - -
-
Network Usage
-
-
-
Total RX
-
-
-
-
-
Total TX
-
-
-
-
-
Total
-
-
-
-
-
-
-
By Category
-
-
Proxy Testing-
-
Scraping-
-
-
-
-
Rates (avg)
-
-
RX Rate-
-
TX Rate-
-
-
-
-
Per Tor Node
-
-
- - + +
+
Search Engines
@@ -312,7 +287,31 @@
-
SSL/TLS Security
+
Tor Network (Scraping)
+
+
Healthy Nodes-
+
Avg Latency-
+
+
Tor Exit Nodes
+
+
+
+ + +
+
+
Judge Services
+
+
Available-
+
In Cooldown-
+
+
Top Performers
+
+
+
+
+
+
SSL/TLS Testing
SSL Tests-
Passed-
diff --git a/static/dashboard.js b/static/dashboard.js index fbc8d8b..0715072 100644 --- a/static/dashboard.js +++ b/static/dashboard.js @@ -9,10 +9,6 @@ var uplotCharts = {}; // Chart.js instances (persistent) var chartJsInstances = {}; -// Network rate tracking (for real-time speed calculation) -var prevNet = null; -var prevNetTime = null; - // Tab switching function initTabs() { $$('.tab-btn').forEach(function(btn) { @@ -86,13 +82,6 @@ function fmtBytes(b) { return b.toFixed(i > 0 ? 1 : 0) + ' ' + units[i]; } -function fmtRate(bps) { - if (bps < 1) return '0'; - if (bps < 1024) return bps.toFixed(0) + 'B'; - if (bps < 1024 * 1024) return (bps / 1024).toFixed(1) + 'K'; - return (bps / (1024 * 1024)).toFixed(1) + 'M'; -} - function fmtTime(s) { if (!s) return '-'; var d = Math.floor(s / 86400), h = Math.floor((s % 86400) / 3600), m = Math.floor((s % 3600) / 60); @@ -301,46 +290,32 @@ function update(d) { var memGrowthStr = memGrowth > 0 ? ' (+' + fmtBytes(memGrowth) + ')' : ''; $('sysProcMem').textContent = fmtBytes(sys.proc_rss) + memGrowthStr; - // Network speed (current rate, not average) - var net = d.network || {}; - var now = Date.now(); - if (prevNet && prevNetTime) { - var dt = (now - prevNetTime) / 1000; - if (dt > 0) { - var s = net.scraper || {}, ps = prevNet.scraper || {}; - var p = net.proxy || {}, pp = prevNet.proxy || {}; - $('netScrapeTx').textContent = fmtRate((s.bytes_tx - (ps.bytes_tx || 0)) / dt); - $('netScrapeRx').textContent = fmtRate((s.bytes_rx - (ps.bytes_rx || 0)) / dt); - $('netProxyTx').textContent = fmtRate((p.bytes_tx - (pp.bytes_tx || 0)) / dt); - $('netProxyRx').textContent = fmtRate((p.bytes_rx - (pp.bytes_rx || 0)) / dt); - } - } - prevNet = net; - prevNetTime = now; - // Main stats - db stats are nested under d.db var db = d.db || {}; $('working').textContent = fmt(db.working); $('total').textContent = fmt(db.total); - $('tested').textContent = fmt(d.tested); - $('passed').textContent = fmt(d.passed); - $('failed').textContent = fmt(d.failed); - // Success rate + // Success rate (displayed in Worker Testing section) var sr = d.success_rate || 0; $('successRate').textContent = fmtDec(sr, 1) + '%'; - $('successRate').className = 'val-md ' + (sr < 20 ? 'red' : sr < 50 ? 'yel' : 'grn'); - setBar('srBar', sr, 100, sr < 20 ? 'red' : sr < 50 ? 'yel' : 'grn'); + $('successRate').className = 'stat-val ' + (sr < 20 ? 'red' : sr < 50 ? 'yel' : 'grn'); var rsr = d.recent_success_rate || 0; - $('recentSuccessRate').textContent = fmtDec(rsr, 1) + '%'; - $('recentSuccessRate').className = 'stat-val ' + (rsr < 20 ? 'red' : rsr < 50 ? 'yel' : 'grn'); + if ($('recentSuccessRate')) { + $('recentSuccessRate').textContent = fmtDec(rsr, 1) + '%'; + $('recentSuccessRate').className = 'stat-val ' + (rsr < 20 ? 'red' : rsr < 50 ? 'yel' : 'grn'); + } - // Rates - $('rate').textContent = fmtDec(d.recent_rate, 2); - $('recentRate').textContent = fmtDec(d.recent_rate, 2) + '/s'; - $('peakRate').textContent = fmtDec(d.peak_rate, 2) + '/s'; - $('passRate').textContent = fmtDec(d.pass_rate, 3); + // Rates (Performance tab) + if ($('recentRate')) $('recentRate').textContent = fmtDec(d.recent_rate, 2) + '/s'; + if ($('peakRate')) $('peakRate').textContent = fmtDec(d.peak_rate, 2) + '/s'; + if ($('passRate')) $('passRate').textContent = fmtDec(d.pass_rate, 3); + + // Scraper stats + var engAvail = d.engines_available || 0; + var engTotal = d.engines_total || 0; + if ($('engActive')) $('engActive').textContent = engAvail + '/' + engTotal; + if ($('proxiesAdded')) $('proxiesAdded').textContent = fmt(db.added_last_day || d.proxies_added || 0); // Latency var lat = d.avg_latency || 0; @@ -354,18 +329,6 @@ function update(d) { $('p99').textContent = fmtMs(pctl.p99); // System - $('threads').textContent = d.threads + '/' + d.max_threads; - setBar('threadBar', d.threads, d.max_threads, 'blu'); - $('queue').textContent = fmt(d.queue_size); - // Calculate queue ETA: queue_size / tests_per_second - var queueEta = '-'; - if (d.queue_size > 0 && d.recent_rate > 0.01) { - var etaSecs = d.queue_size / d.recent_rate; - queueEta = fmtTime(etaSecs); - } else if (d.queue_size === 0) { - queueEta = 'empty'; - } - $('queueEta').textContent = queueEta; $('uptime').textContent = fmtTime(d.uptime_seconds); // Charts @@ -621,37 +584,6 @@ function update(d) { $('mitmRecent').innerHTML = recentHtml; } - // Network usage stats - if (d.network) { - var net = d.network; - $('netRx').textContent = fmtBytes(net.bytes_rx || 0); - $('netTx').textContent = fmtBytes(net.bytes_tx || 0); - $('netTotal').textContent = fmtBytes(net.bytes_total || 0); - $('netRxRate').textContent = fmtBytes(net.rx_rate || 0) + '/s'; - $('netTxRate').textContent = fmtBytes(net.tx_rate || 0) + '/s'; - if (net.proxy) { - $('netProxy').textContent = fmtBytes(net.proxy.bytes_total || 0); - } - if (net.scraper) { - $('netScraper').textContent = fmtBytes(net.scraper.bytes_total || 0); - } - // Per-tor-node stats - var torContainer = $('netTorNodes'); - if (torContainer && net.tor_nodes) { - var html = ''; - var nodes = Object.keys(net.tor_nodes).sort(); - nodes.forEach(function(node) { - var s = net.tor_nodes[node]; - html += '
'; - html += '
' + node + '
'; - html += '
' + fmtBytes(s.rx + s.tx) + '
'; - html += '
' + fmt(s.requests || 0) + ' req
'; - html += '
'; - }); - torContainer.innerHTML = html; - } - } - $('lastUpdate').textContent = new Date().toLocaleTimeString(); } @@ -660,6 +592,93 @@ function fetchStats() { .then(function(r) { return r.json(); }) .then(update) .catch(function(e) { $('dot').className = 'dot err'; $('statusTxt').textContent = 'Error'; }); + // Also fetch worker stats + fetchWorkers(); +} + +function fetchWorkers() { + fetch('/api/workers') + .then(function(r) { return r.json(); }) + .then(updateWorkers) + .catch(function(e) { console.error('Failed to fetch workers:', e); }); +} + +function updateWorkers(data) { + if (!data) return; + + // Update summary stats (both main panel and Workers tab) + var active = data.active || 0; + var total = data.total || 0; + if ($('wkActive')) $('wkActive').textContent = active + '/' + total; + if ($('dwActive')) $('dwActive').textContent = active + '/' + total; + + if (data.summary) { + if ($('wkTested')) $('wkTested').textContent = fmt(data.summary.total_tested); + if ($('wkWorking')) $('wkWorking').textContent = fmt(data.summary.total_working); + if ($('wkSuccessRate')) $('wkSuccessRate').textContent = data.summary.overall_success_rate.toFixed(1) + '%'; + // Main panel distributed workers + if ($('dwTested')) $('dwTested').textContent = fmt(data.summary.total_tested); + if ($('dwWorking')) $('dwWorking').textContent = fmt(data.summary.total_working); + // Calculate combined rate from worker stats + var combinedRate = 0; + if (data.workers) { + data.workers.forEach(function(w) { + if (w.active && w.test_rate) combinedRate += w.test_rate; + }); + } + if ($('dwRate')) $('dwRate').textContent = combinedRate > 0 ? combinedRate.toFixed(1) + '/s' : '-'; + } + + // Queue status + if (data.queue) { + if ($('queuePending')) $('queuePending').textContent = fmt(data.queue.pending || 0); + if ($('queueClaimed')) $('queueClaimed').textContent = fmt(data.queue.claimed || 0); + if ($('queueDue')) $('queueDue').textContent = fmt(data.queue.due || 0); + } + + // Update worker cards + var container = $('workerCards'); + if (!container) return; + + if (!data.workers || data.workers.length === 0) { + container.innerHTML = '
' + + '
No workers connected
' + + '
Add workers with: python ppf.py --register --server URL
'; + return; + } + + var html = ''; + data.workers.forEach(function(w) { + var statusClass = w.active ? 'grn' : 'red'; + var statusText = w.active ? 'ACTIVE' : 'OFFLINE'; + var successRate = w.success_rate || 0; + var rateClass = successRate >= 50 ? 'grn' : (successRate >= 20 ? 'yel' : 'red'); + var profBadge = w.profiling ? 'PROF' : ''; + + html += '
' + + '
' + + '' + w.name + '' + + '' + statusText + '' + profBadge + '' + + '
' + + '
' + + '
Tested' + fmt(w.proxies_tested) + '
' + + '
Working' + fmt(w.proxies_working) + '
' + + '
Success' + successRate.toFixed(1) + '%
' + + '
Jobs' + fmt(w.jobs_completed) + '
' + + '
' + + '
' + + 'IP: ' + w.ip + ' | Last: ' + formatAge(w.age) + + '
' + + '
'; + }); + container.innerHTML = html; +} + +function formatAge(seconds) { + if (seconds < 60) return seconds + 's ago'; + if (seconds < 3600) return Math.floor(seconds / 60) + 'm ago'; + if (seconds < 86400) return Math.floor(seconds / 3600) + 'h ago'; + return Math.floor(seconds / 86400) + 'd ago'; } // Visibility-aware polling - pause when tab is hidden diff --git a/static/style.css b/static/style.css index 3757419..a8ec106 100644 --- a/static/style.css +++ b/static/style.css @@ -102,9 +102,6 @@ h3 { font-size: 13px; font-weight: 600; color: var(--dim); margin-bottom: 8px; } .sysbar-val { font-weight: 600; font-feature-settings: "tnum"; } .sysbar-bar { width: 50px; height: 4px; background: var(--border); border-radius: 2px; overflow: hidden; } .sysbar-fill { height: 100%; border-radius: 2px; transition: width 0.3s; } -.sysbar-net .net-tx::before { content: '↑'; opacity: 0.5; margin-right: 1px; } -.sysbar-net .net-rx::before { content: '↓'; opacity: 0.5; margin-right: 1px; } -.sysbar-net .sysbar-val { min-width: 42px; } /* Grid */ .g { display: grid; gap: 12px; margin-bottom: 16px; }