feat: implement 13 features — auto-reconnect, notifications, bulk actions, latency, traffic, theme, export/import, scheduled backups, peer lookup, SSH auto-reconnect, log filters, keyboard shortcut, and readyTimeout default
CI / Build & Test (push) Successful in 2m44s
CI / Build & Test (push) Successful in 2m44s
- Change readyTimeoutMs default from 0 to 30000 in both state files - Register Alt+Shift+H keyboard shortcut via manifest _execute_action command - Add severity filter buttons (All/Info/Warn/Error) and Download .txt to Logs page; background logs.js now tags entries with proper level field - Fire browser notifications on tunnelError events (notifyOnTunnelError setting) - Add exponential-backoff auto-reconnect for virtual hosts and service tunnels (tunnelAutoReconnect setting, 5s–120s backoff) - Add backupIntervalHours setting and scheduled auto-backup timer in message-router.js - Add TCP connect latency badges to Virtual Hosts and Service Tunnels tables via new pingTunnel native message - Add bytesIn/bytesOut/requests counters to https-proxy.js; expose in Overview status bar via getState - Add Export/Import connection configs (JSON, no CA key) in Settings - Add autoReconnect flag and exponential-backoff reconnect to SSH connection cards - Add light theme CSS variables and theme toggle in Settings (persisted to localStorage) - Add checkbox column and bulk Stop/Remove actions to Virtual Hosts, Service Tunnels, and Servers tables - Add Peer Lookup UI card on Overview page using existing lookup message handler
This commit is contained in:
@@ -35,6 +35,11 @@ let proxyServer = null;
|
||||
let proxyPort = null;
|
||||
let proxyCertsDirOrCA = null;
|
||||
|
||||
const trafficStats = { bytesIn: 0, bytesOut: 0, requests: 0 };
|
||||
|
||||
function getTrafficStats() { return { ...trafficStats }; }
|
||||
function resetTrafficStats() { trafficStats.bytesIn = 0; trafficStats.bytesOut = 0; trafficStats.requests = 0; }
|
||||
|
||||
/** Resolver: hostname -> { host, port } | port | null */
|
||||
let getBackendForHostname = null;
|
||||
|
||||
@@ -313,7 +318,7 @@ h1{color:#c0392b}code{background:#f4f4f4;padding:2px 6px;border-radius:3px;font-
|
||||
try { res.setHeader(k, proxyRes.headers[k]); } catch (_) {}
|
||||
}
|
||||
}
|
||||
proxyRes.on('data', (chunk) => !timedOut && res.write(chunk));
|
||||
proxyRes.on('data', (chunk) => { if (!timedOut) { trafficStats.bytesOut += chunk.length; res.write(chunk); } });
|
||||
proxyRes.on('end', () => !timedOut && !res.writableEnded && res.end());
|
||||
// Destroy the client socket on a mid-stream backend error so the browser
|
||||
// receives a connection reset rather than a silently truncated 200 body.
|
||||
@@ -327,7 +332,8 @@ h1{color:#c0392b}code{background:#f4f4f4;padding:2px 6px;border-radius:3px;font-
|
||||
res.end('Proxy error: ' + (timedOut ? 'timeout' : err.message));
|
||||
}
|
||||
});
|
||||
req.on('data', (chunk) => proxyReq.write(chunk));
|
||||
trafficStats.requests++;
|
||||
req.on('data', (chunk) => { trafficStats.bytesIn += chunk.length; proxyReq.write(chunk); });
|
||||
req.on('end', () => proxyReq.end());
|
||||
}
|
||||
|
||||
@@ -367,6 +373,8 @@ function onUpgrade (req, socket, head) {
|
||||
const requestLine = (req.method || 'GET') + ' ' + (req.url || '/') + ' HTTP/1.1\r\n';
|
||||
upstream.write(requestLine + headers + '\r\n\r\n');
|
||||
if (head && head.length > 0) upstream.write(head);
|
||||
socket.on('data', (c) => { trafficStats.bytesIn += c.length; });
|
||||
upstream.on('data', (c) => { trafficStats.bytesOut += c.length; });
|
||||
socket.pipe(upstream);
|
||||
upstream.pipe(socket);
|
||||
});
|
||||
@@ -595,5 +603,7 @@ module.exports = {
|
||||
start,
|
||||
stop,
|
||||
restart,
|
||||
getPort
|
||||
getPort,
|
||||
getTrafficStats,
|
||||
resetTrafficStats
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user