spamir-updater
Version:
Secure automatic update client for Node.js applications with encrypted communication and package verification
151 lines (132 loc) • 4.78 kB
JavaScript
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
};