UNPKG

network-performance-monitor

Version:

A comprehensive network performance monitoring tool that continuously tests and tracks your network's performance over time

345 lines 14.8 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.WebServer = void 0; const express_1 = __importDefault(require("express")); const path = __importStar(require("path")); const logger_1 = require("./logger"); const networkInfo_1 = require("./networkInfo"); const speedTest_1 = require("./speedTest"); class WebServer { app; db; port; server; networkDetector; speedTest; constructor(db, port = 3000) { this.db = db; this.port = port; this.app = (0, express_1.default)(); this.app.use(express_1.default.json()); // Add JSON body parser this.networkDetector = networkInfo_1.NetworkDetector.getInstance(); this.speedTest = new speedTest_1.SpeedTest(db); this.setupRoutes(); } setupRoutes() { // Serve static files this.app.use(express_1.default.static(path.join(__dirname, '..', '..', 'public'))); // API endpoints this.app.get('/api/ping-data', async (req, res) => { try { const { start, end, network } = req.query; const data = await this.getPingData(start ? new Date(start) : undefined, end ? new Date(end) : undefined, network); res.json(data); } catch (error) { logger_1.logger.error('Error fetching ping data:', error); res.status(500).json({ error: 'Failed to fetch ping data' }); } }); this.app.get('/api/speed-data', async (req, res) => { try { const { start, end, network } = req.query; const data = await this.getSpeedData(start ? new Date(start) : undefined, end ? new Date(end) : undefined, network); res.json(data); } catch (error) { logger_1.logger.error('Error fetching speed data:', error); res.status(500).json({ error: 'Failed to fetch speed data' }); } }); this.app.get('/api/website-data', async (req, res) => { try { const { start, end, network } = req.query; const data = await this.getWebsiteData(start ? new Date(start) : undefined, end ? new Date(end) : undefined, network); res.json(data); } catch (error) { logger_1.logger.error('Error fetching website data:', error); res.status(500).json({ error: 'Failed to fetch website data' }); } }); this.app.get('/api/averages', async (req, res) => { try { const { network, start, end } = req.query; const averages = await this.getAverages(network, start ? new Date(start) : undefined, end ? new Date(end) : undefined); res.json(averages); } catch (error) { logger_1.logger.error('Error fetching averages:', error); res.status(500).json({ error: 'Failed to fetch averages' }); } }); this.app.get('/api/data-range', async (req, res) => { try { const range = await this.getDataRange(); res.json(range); } catch (error) { logger_1.logger.error('Error fetching data range:', error); res.status(500).json({ error: 'Failed to fetch data range' }); } }); this.app.get('/api/networks', async (req, res) => { try { const networks = await this.db.getNetworkNames(); res.json(networks); } catch (error) { logger_1.logger.error('Error fetching networks:', error); res.status(500).json({ error: 'Failed to fetch networks' }); } }); this.app.get('/api/current-network', async (req, res) => { try { // Use cached network info for UI to avoid excessive detection const networkInfo = await this.networkDetector.getCurrentNetwork(false); res.json(networkInfo); } catch (error) { logger_1.logger.error('Error fetching current network:', error); res.status(500).json({ error: 'Failed to fetch current network' }); } }); this.app.post('/api/trigger-speed-test', async (req, res) => { try { logger_1.logger.info('Manual speed test triggered via UI'); // Get current network info (use cache for manual tests) const networkInfo = await this.networkDetector.getCurrentNetwork(false); const networkName = networkInfo.name; // Run speed test const result = await this.speedTest.runTest('manual', networkName); // Format the result const formattedResult = { success: !result.error, downloadSpeed: result.downloadSpeed, uploadSpeed: result.uploadSpeed, ping: result.ping, server: result.server, timestamp: new Date().toISOString(), network: networkName, error: result.error }; logger_1.logger.info(this.speedTest.formatResult(result)); res.json(formattedResult); } catch (error) { logger_1.logger.error('Error running manual speed test:', error); res.status(500).json({ error: 'Failed to run speed test', details: error instanceof Error ? error.message : 'Unknown error' }); } }); } async getPingData(start, end, network) { return new Promise((resolve, reject) => { let query = 'SELECT timestamp, host, latency, packet_loss, network_name FROM ping_results WHERE success = 1'; const params = []; if (network) { query += ' AND network_name = ?'; params.push(network); } if (start || end) { // Convert dates to UTC string format that matches database storage query += ' AND datetime(timestamp) BETWEEN datetime(?) AND datetime(?)'; params.push(start ? start.toISOString().replace('T', ' ').replace('Z', '') : '1970-01-01 00:00:00', end ? end.toISOString().replace('T', ' ').replace('Z', '') : new Date().toISOString().replace('T', ' ').replace('Z', '')); } query += ' ORDER BY timestamp ASC'; this.db.db.all(query, params, (err, rows) => { if (err) reject(err); else resolve(rows); }); }); } async getWebsiteData(start, end, network) { return new Promise((resolve, reject) => { let query = 'SELECT timestamp, url, latency, status_code, network_name FROM website_tests WHERE success = 1'; const params = []; if (network) { query += ' AND network_name = ?'; params.push(network); } if (start || end) { // Convert dates to UTC string format that matches database storage query += ' AND datetime(timestamp) BETWEEN datetime(?) AND datetime(?)'; params.push(start ? start.toISOString().replace('T', ' ').replace('Z', '') : '1970-01-01 00:00:00', end ? end.toISOString().replace('T', ' ').replace('Z', '') : new Date().toISOString().replace('T', ' ').replace('Z', '')); } query += ' ORDER BY timestamp ASC'; this.db.db.all(query, params, (err, rows) => { if (err) reject(err); else resolve(rows); }); }); } async getSpeedData(start, end, network) { return new Promise((resolve, reject) => { let query = 'SELECT timestamp, download_speed, upload_speed, ping, server, trigger_reason, network_name FROM speed_tests WHERE success = 1'; const params = []; if (network) { query += ' AND network_name = ?'; params.push(network); } if (start || end) { // Convert dates to UTC string format that matches database storage query += ' AND datetime(timestamp) BETWEEN datetime(?) AND datetime(?)'; params.push(start ? start.toISOString().replace('T', ' ').replace('Z', '') : '1970-01-01 00:00:00', end ? end.toISOString().replace('T', ' ').replace('Z', '') : new Date().toISOString().replace('T', ' ').replace('Z', '')); } query += ' ORDER BY timestamp ASC'; this.db.db.all(query, params, (err, rows) => { if (err) reject(err); else resolve(rows); }); }); } async getAverages(network, start, end) { // Default to last 30 days if no time range specified if (!start && !end) { const thirtyDaysAgo = new Date(); thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30); start = thirtyDaysAgo; end = new Date(); } else { // Ensure both start and end are defined if (!start) { start = new Date('1970-01-01'); } if (!end) { end = new Date(); } } return new Promise((resolve, reject) => { // Build queries with optional network filter and time range let pingQuery = ` SELECT AVG(latency) as avg_latency FROM ping_results WHERE success = 1 AND datetime(timestamp) BETWEEN datetime(?) AND datetime(?) `; let speedQuery = ` SELECT AVG(download_speed) as avg_download, AVG(upload_speed) as avg_upload FROM speed_tests WHERE success = 1 AND datetime(timestamp) BETWEEN datetime(?) AND datetime(?) `; let websiteQuery = ` SELECT AVG(latency) as avg_website_latency FROM website_tests WHERE success = 1 AND datetime(timestamp) BETWEEN datetime(?) AND datetime(?) `; const pingParams = [ start.toISOString().replace('T', ' ').replace('Z', ''), end.toISOString().replace('T', ' ').replace('Z', '') ]; const speedParams = [ start.toISOString().replace('T', ' ').replace('Z', ''), end.toISOString().replace('T', ' ').replace('Z', '') ]; const websiteParams = [ start.toISOString().replace('T', ' ').replace('Z', ''), end.toISOString().replace('T', ' ').replace('Z', '') ]; if (network) { pingQuery += ' AND network_name = ?'; speedQuery += ' AND network_name = ?'; websiteQuery += ' AND network_name = ?'; pingParams.push(network); speedParams.push(network); websiteParams.push(network); } const result = {}; this.db.db.get(pingQuery, pingParams, (err, row) => { if (err) return reject(err); result.avgPingLatency = row?.avg_latency || 0; this.db.db.get(speedQuery, speedParams, (err, row) => { if (err) return reject(err); result.avgDownloadSpeed = row?.avg_download || 0; result.avgUploadSpeed = row?.avg_upload || 0; this.db.db.get(websiteQuery, websiteParams, (err, row) => { if (err) return reject(err); result.avgWebsiteLatency = row?.avg_website_latency || 0; resolve(result); }); }); }); }); } async getDataRange() { return new Promise((resolve, reject) => { const query = ` SELECT MIN(min_ts) as start, MAX(max_ts) as end FROM ( SELECT MIN(timestamp) as min_ts, MAX(timestamp) as max_ts FROM ping_results UNION ALL SELECT MIN(timestamp) as min_ts, MAX(timestamp) as max_ts FROM speed_tests ) `; this.db.db.get(query, (err, row) => { if (err) reject(err); else resolve({ start: row.start, end: row.end }); }); }); } start() { this.server = this.app.listen(this.port, () => { logger_1.logger.info(`Web UI server running at http://localhost:${this.port}`); console.log(`Web UI available at http://localhost:${this.port}`); }); } stop() { if (this.server) { this.server.close(() => { logger_1.logger.info('Web UI server stopped'); }); } } } exports.WebServer = WebServer; //# sourceMappingURL=webServer.js.map