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; }