UNPKG

network-performance-monitor

Version:

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

187 lines 7.72 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; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.SpeedTest = void 0; const child_process_1 = require("child_process"); const util_1 = require("util"); const logger_1 = require("./logger"); const fs = __importStar(require("fs")); const execAsync = (0, util_1.promisify)(child_process_1.exec); // Determine speedtest-cli path based on OS function getSpeedtestPath() { const paths = [ '/opt/homebrew/bin/speedtest-cli', // macOS with Homebrew '/usr/local/bin/speedtest-cli', // Common Unix path '/usr/bin/speedtest-cli', // System path 'speedtest-cli' // Fallback to PATH ]; for (const path of paths) { if (path === 'speedtest-cli' || fs.existsSync(path)) { return path; } } return 'speedtest-cli'; // Default fallback } const SPEEDTEST_PATH = getSpeedtestPath(); class SpeedTest { db; constructor(db) { this.db = db; } async runSpeedTestWithSpawn() { return new Promise((resolve, reject) => { const child = (0, child_process_1.spawn)(SPEEDTEST_PATH, ['--json', '--no-pre-allocate'], { env: { ...process.env, PYTHONUNBUFFERED: '1', NO_COLOR: '1', TERM: 'dumb', FORCE_COLOR: '0', PYTHONWARNINGS: 'ignore::DeprecationWarning' } }); let stdout = ''; let stderr = ''; const timeout = setTimeout(() => { child.kill(); reject(new Error('Speedtest timed out after 2 minutes')); }, 120000); child.stdout.on('data', (data) => { stdout += data.toString(); }); child.stderr.on('data', (data) => { stderr += data.toString(); }); child.on('close', (code) => { clearTimeout(timeout); if (code !== 0) { logger_1.logger.debug('Speedtest-cli exited with code:', code); logger_1.logger.debug('Stderr (ignored):', stderr); } // Always return stdout, even if exit code is non-zero resolve(stdout); }); child.on('error', (err) => { clearTimeout(timeout); reject(err); }); }); } async runTest(triggerReason, networkName) { logger_1.logger.debug(`Running speedtest with path: ${SPEEDTEST_PATH}`); const result = { downloadSpeed: null, uploadSpeed: null, ping: null, server: null, error: null, success: false, triggerReason }; try { // Always use spawn method for consistency - it gives us better control over stdout/stderr logger_1.logger.debug('Running speedtest with spawn method'); const stdout = await this.runSpeedTestWithSpawn(); // Log raw output for debugging if (!stdout || stdout.trim().length === 0) { logger_1.logger.error('Speedtest produced no stdout', { triggerReason, speedtestPath: SPEEDTEST_PATH, stdoutLength: stdout ? stdout.length : 0 }); throw new Error('No output from speedtest-cli'); } logger_1.logger.debug('Speedtest stdout received', { triggerReason, stdoutLength: stdout.length, firstChars: stdout.substring(0, 100) }); // Strip any ANSI escape codes and filter out any non-JSON content const lines = stdout .replace(/\u001b\[[0-9;]*m/g, '') // Remove ANSI codes .split('\n') .map(line => line.trim()) .filter(line => line.length > 0); // Find the JSON line (should start with {) const jsonLine = lines.find(line => line.startsWith('{') && line.endsWith('}')); if (!jsonLine) { logger_1.logger.debug('No JSON found in speedtest output:', { lineCount: lines.length, firstFewLines: lines.slice(0, 5), lastFewLines: lines.slice(-5) }); throw new Error('No valid JSON output from speedtest-cli'); } try { const data = JSON.parse(jsonLine); // Convert from bits/s to Mbps result.downloadSpeed = data.download / 1000000; result.uploadSpeed = data.upload / 1000000; result.ping = data.ping; result.server = `${data.server.name} (${data.server.sponsor})`; result.success = true; (0, logger_1.logTestResult)('SPEEDTEST', 'speedtest-cli', true, result.ping || undefined, undefined); } catch (parseError) { logger_1.logger.error('Failed to parse speedtest JSON:', { error: parseError instanceof Error ? parseError.message : 'Unknown error', jsonLine: jsonLine.substring(0, 200) // First 200 chars for debugging }); throw new Error('Failed to parse speedtest-cli output'); } } catch (error) { result.error = error instanceof Error ? error.message : 'Unknown error'; (0, logger_1.logTestResult)('SPEEDTEST', 'speedtest-cli', false, undefined, result.error); } // Store in database await this.db.insertSpeedTest(result.downloadSpeed, result.uploadSpeed, result.ping, result.server, result.error, result.triggerReason, networkName); return result; } formatResult(result) { if (result.success) { return `Speed Test Results (${result.triggerReason}): Download: ${result.downloadSpeed?.toFixed(2)} Mbps Upload: ${result.uploadSpeed?.toFixed(2)} Mbps Ping: ${result.ping} ms Server: ${result.server}`; } else { return `Speed Test Failed (${result.triggerReason}): ${result.error}`; } } } exports.SpeedTest = SpeedTest; //# sourceMappingURL=speedTest.js.map