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