UNPKG

spamir-updater

Version:

Secure automatic update client for Node.js applications with encrypted communication and package verification

151 lines (132 loc) 4.78 kB
const fs = require('fs').promises; const path = require('path'); const os = require('os'); const crypto = require('crypto'); const { v5: uuidv5 } = require('uuid'); /** * Load version from config.json in the current working directory * @returns {Promise<string|null>} Version string or null if not found */ async function loadVersionFromConfig() { const configPath = path.join(process.cwd(), 'config.json'); try { const configData = await fs.readFile(configPath, 'utf8'); const config = JSON.parse(configData); return config.version ? String(config.version) : null; } catch (error) { return null; } } /** * Save version to config.json in the current working directory * @param {string} version - Version to save * @returns {Promise<boolean>} Success status */ async function saveVersionToConfig(version) { const configPath = path.join(process.cwd(), 'config.json'); let config = {}; try { // Try to read existing config const configData = await fs.readFile(configPath, 'utf8'); config = JSON.parse(configData); } catch (error) { // Config doesn't exist or is invalid, we'll create a new one } config.version = version; try { await fs.writeFile(configPath, JSON.stringify(config, null, 2), 'utf8'); return true; } catch (error) { console.error('Failed to save version to config:', error); return false; } } /** * Get system information * @returns {Object} System profile */ function getSystemInfo() { const profile = { diag_version: '1.2', os_platform: process.platform, node_version: process.version, os_architecture: process.arch, runtime_ver: process.version }; try { profile.os_release = os.release(); } catch (error) { profile.os_release = 'unknown'; } return profile; } /** * Get network interfaces to generate a unique system identifier * @returns {string} MAC address or random hex string */ function getSystemComponent() { try { const networkInterfaces = os.networkInterfaces(); const interfaces = []; for (const [name, infos] of Object.entries(networkInterfaces)) { for (const info of infos) { if (info.mac && info.mac !== '00:00:00:00:00:00') { const isLowPriority = name.toLowerCase().startsWith('lo') || name.toLowerCase().startsWith('vmnet') || name.toLowerCase().startsWith('docker'); interfaces.push({ name, mac: info.mac.replace(/:/g, '').toUpperCase(), priority: isLowPriority ? 1 : 0 }); } } } if (interfaces.length === 0) { return crypto.randomBytes(16).toString('hex'); } // Sort by priority and name interfaces.sort((a, b) => { if (a.priority !== b.priority) return a.priority - b.priority; const preferredPrefixes = ['eth', 'en', 'wlan']; const aPrefix = preferredPrefixes.findIndex(p => a.name.toLowerCase().startsWith(p)); const bPrefix = preferredPrefixes.findIndex(p => b.name.toLowerCase().startsWith(p)); if (aPrefix !== -1 && bPrefix !== -1) return aPrefix - bPrefix; if (aPrefix !== -1) return -1; if (bPrefix !== -1) return 1; return a.name.localeCompare(b.name); }); return interfaces[0].mac; } catch (error) { return crypto.randomBytes(16).toString('hex'); } } /** * Generate a unique instance signature * @param {string} authToken - Shared authentication token * @returns {string} UUID v5 based on system component and auth token */ function generateInstanceSignature(authToken) { const systemComponent = getSystemComponent(); const idMaterial = systemComponent + authToken; // Using OID namespace for UUID v5 const OID_NAMESPACE = '6ba7b812-9dad-11d1-80b4-00c04fd430c8'; return uuidv5(idMaterial, OID_NAMESPACE); } /** * Simple logging function (can be enhanced later) * @param {string} message - Message to log * @param {string} level - Log level (INFO, WARNING, ERROR, etc.) */ function logToFile(message, level = 'INFO') { const timestamp = new Date().toISOString(); console.log(`[${timestamp}] [${level}] ${message}`); } module.exports = { loadVersionFromConfig, saveVersionToConfig, getSystemInfo, getSystemComponent, generateInstanceSignature, logToFile };