UNPKG

@ufdevsllc/authme2.0

Version:

SDK for license management and remote monitoring with automatic system tracking, license validation, and remote control capabilities

434 lines (394 loc) 14.9 kB
import os from 'os'; import DatabaseManager from './database-manager.js'; class SystemTracker { constructor(errorHandler = null) { this.errorHandler = errorHandler; this.dbManager = new DatabaseManager(this.errorHandler); this.trackingInterval = null; this.trackingIntervalMs = 5 * 60 * 1000; // 5 minutes } /** * Collect comprehensive system information * @returns {Object} System information object */ collectSystemInfo() { const context = { component: 'system-tracker', operation: 'collect-system-info' }; try { const systemInfo = { os: os.type(), platform: os.platform(), arch: os.arch(), release: os.release(), hostname: os.hostname(), memory: { total: os.totalmem(), free: os.freemem(), used: os.totalmem() - os.freemem() }, cpu: { model: os.cpus()[0]?.model || 'Unknown', cores: os.cpus().length, speed: os.cpus()[0]?.speed || 0 }, uptime: os.uptime(), loadAverage: os.loadavg(), networkInterfaces: this._getNetworkInterfaces(), timestamp: new Date() }; return systemInfo; } catch (error) { if (this.errorHandler) { this.errorHandler.handleError(error, context); } else { console.error('Error collecting system info:', error); } return { error: 'Failed to collect system information', errorMessage: error.message, timestamp: new Date() }; } } /** * Collect deployment information including IP and location * @returns {Object} Deployment information object */ collectDeploymentInfo() { try { const deploymentInfo = { nodeVersion: process.version, environment: process.env.NODE_ENV || 'development', processId: process.pid, workingDirectory: process.cwd(), execPath: process.execPath, argv: process.argv, timestamp: new Date(), startTime: new Date(Date.now() - process.uptime() * 1000), ipAddress: this._getLocalIPAddress(), serverLocation: process.env.SERVER_LOCATION || 'Unknown' }; return deploymentInfo; } catch (error) { console.error('Error collecting deployment info:', error); return { error: 'Failed to collect deployment information', timestamp: new Date() }; } } /** * Collect CORS settings from common Express configurations * @returns {Object} CORS information object */ collectCORSInfo() { try { // This is a basic implementation that checks common CORS environment variables // In a real application, this might need to be more sophisticated const corsInfo = { allowedOrigins: this._parseEnvArray(process.env.CORS_ORIGINS) || ['*'], allowedMethods: this._parseEnvArray(process.env.CORS_METHODS) || ['GET', 'POST', 'PUT', 'DELETE'], allowedHeaders: this._parseEnvArray(process.env.CORS_HEADERS) || ['Content-Type', 'Authorization'], credentials: process.env.CORS_CREDENTIALS === 'true', maxAge: parseInt(process.env.CORS_MAX_AGE) || 86400, timestamp: new Date() }; return corsInfo; } catch (error) { console.error('Error collecting CORS info:', error); return { error: 'Failed to collect CORS information', timestamp: new Date() }; } } /** * Collect filtered environment variables (excluding sensitive data) * @returns {Object} Filtered environment variables */ collectEnvironmentVariables() { try { const sensitiveKeys = [ 'password', 'secret', 'key', 'token', 'auth', 'credential', 'private', 'api_key', 'db_password', 'mongodb_password' ]; const filteredEnv = {}; Object.keys(process.env).forEach(key => { const lowerKey = key.toLowerCase(); const isSensitive = sensitiveKeys.some(sensitive => lowerKey.includes(sensitive) ); if (!isSensitive) { filteredEnv[key] = process.env[key]; } else { filteredEnv[key] = '[FILTERED]'; } }); return { variables: filteredEnv, count: Object.keys(process.env).length, filteredCount: Object.keys(filteredEnv).filter(k => filteredEnv[k] === '[FILTERED]').length, timestamp: new Date() }; } catch (error) { console.error('Error collecting environment variables:', error); return { error: 'Failed to collect environment variables', timestamp: new Date() }; } } /** * Collect all system tracking data * @param {string} licenseKey - License key for tracking * @returns {Object} Complete tracking data */ async collectAllTrackingData(licenseKey) { try { const trackingData = { licenseKey, systemInfo: this.collectSystemInfo(), deploymentInfo: this.collectDeploymentInfo(), corsSettings: this.collectCORSInfo(), environmentVariables: this.collectEnvironmentVariables(), collectionTimestamp: new Date(), isActive: true }; return trackingData; } catch (error) { if (this.errorHandler) { await this.errorHandler.handleError(error, { component: 'system-tracker', operation: 'collect-all-tracking-data', licenseKey }); } else { console.error('Error collecting tracking data:', error); } throw new Error(`Failed to collect tracking data: ${error.message}`); } } /** * Save collected data to monitoring database * @param {Object} trackingData - Data to save * @returns {Promise<Object>} Save result */ async saveToMonitoring(trackingData) { const context = { component: 'system-tracker', operation: 'save-to-monitoring', licenseKey: trackingData?.licenseKey }; try { const saveOperation = async () => { const db = this.dbManager.getMonitoringDB(); const collection = db.collection('systemTracking'); // Update existing record or insert new one const result = await collection.updateOne( { licenseKey: trackingData.licenseKey }, { $set: { ...trackingData, lastSeen: new Date() } }, { upsert: true } ); return { success: true, operation: result.upsertedId ? 'inserted' : 'updated', id: result.upsertedId || trackingData.licenseKey, timestamp: new Date() }; }; let result; if (this.errorHandler) { result = await this.errorHandler.executeWithRetry(saveOperation, context, { maxRetries: 3 }); await this.errorHandler.logInfo(`System tracking data saved successfully`, context); } else { result = await saveOperation(); console.log('System tracking data saved successfully'); } return result; } catch (error) { if (this.errorHandler) { await this.errorHandler.handleError(error, context); } else { console.error('Error saving to monitoring database:', error); } throw new Error(`Failed to save tracking data: ${error.message}`); } } /** * Start periodic tracking * @param {string} licenseKey - License key for tracking * @param {number} intervalMs - Tracking interval in milliseconds (optional) */ async startPeriodicTracking(licenseKey, intervalMs = this.trackingIntervalMs) { // Validate license key if (this.errorHandler) { const validation = this.errorHandler.validateInput(licenseKey, { type: 'string', required: true, minLength: 8 }, { field: 'licenseKey' }); if (!validation.isValid) { const error = new Error(`Invalid license key for tracking: ${validation.errors.join(', ')}`); await this.errorHandler.handleError(error, { component: 'system-tracker', operation: 'start-periodic-tracking', validationErrors: validation.errors }); throw error; } } else { // Basic validation without error handler if (!licenseKey || typeof licenseKey !== 'string' || licenseKey.length < 8) { throw new Error('Invalid license key for tracking'); } } const context = { component: 'system-tracker', operation: 'start-periodic-tracking', licenseKey, intervalMs }; try { if (this.errorHandler) { await this.errorHandler.initialize(); } // Clear existing interval if any this.stopPeriodicTracking(); // Initial tracking await this.trackAndSave(licenseKey); // Set up periodic tracking this.trackingInterval = setInterval(async () => { try { await this.trackAndSave(licenseKey); } catch (error) { if (this.errorHandler) { await this.errorHandler.handleError(error, { component: 'system-tracker', operation: 'periodic-tracking-iteration', licenseKey }); } else { console.error('Error in periodic tracking:', error); } } }, intervalMs); if (this.errorHandler) { await this.errorHandler.logInfo(`Started periodic tracking every ${intervalMs}ms`, context); } else { console.log(`Started periodic tracking for license ${licenseKey} every ${intervalMs}ms`); } } catch (error) { if (this.errorHandler) { await this.errorHandler.handleError(error, context); } else { console.error('Error starting periodic tracking:', error); } throw new Error(`Failed to start periodic tracking: ${error.message}`); } } /** * Stop periodic tracking */ stopPeriodicTracking() { if (this.trackingInterval) { clearInterval(this.trackingInterval); this.trackingInterval = null; if (this.errorHandler) { this.errorHandler.logInfo('Stopped periodic tracking', { component: 'system-tracker', operation: 'stop-periodic-tracking' }); } else { console.log('Stopped periodic tracking'); } } } /** * Collect and save tracking data * @param {string} licenseKey - License key for tracking * @returns {Promise<Object>} Save result */ async trackAndSave(licenseKey) { try { const trackingData = await this.collectAllTrackingData(licenseKey); const result = await this.saveToMonitoring(trackingData); return result; } catch (error) { if (this.errorHandler) { await this.errorHandler.handleError(error, { component: 'system-tracker', operation: 'track-and-save', licenseKey }); } else { console.error('Error in trackAndSave:', error); } throw error; } } /** * Get network interfaces information * @private * @returns {Object} Network interfaces */ _getNetworkInterfaces() { try { const interfaces = os.networkInterfaces(); const result = {}; Object.keys(interfaces).forEach(name => { result[name] = interfaces[name].map(iface => ({ address: iface.address, netmask: iface.netmask, family: iface.family, mac: iface.mac, internal: iface.internal })); }); return result; } catch (error) { return { error: 'Failed to get network interfaces' }; } } /** * Get local IP address * @private * @returns {string} Local IP address */ _getLocalIPAddress() { try { const interfaces = os.networkInterfaces(); for (const name of Object.keys(interfaces)) { for (const iface of interfaces[name]) { if (iface.family === 'IPv4' && !iface.internal) { return iface.address; } } } return '127.0.0.1'; } catch (error) { return 'Unknown'; } } /** * Parse environment array values * @private * @param {string} envValue - Environment variable value * @returns {Array|null} Parsed array or null */ _parseEnvArray(envValue) { if (!envValue) return null; try { return envValue.split(',').map(item => item.trim()).filter(item => item.length > 0); } catch (error) { return null; } } } export { SystemTracker };