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