feat(core): Add update checking and version logging across startup components
This commit is contained in:
parent
32f85aa46f
commit
8ee21ea92b
@ -1,5 +1,13 @@
|
||||
# Changelog
|
||||
|
||||
## 2025-03-25 - 1.10.0 - feat(core)
|
||||
Add update checking and version logging across startup components
|
||||
|
||||
- In daemon.ts, log version info on startup and check for updates in the background using npm registry response
|
||||
- In nupst.ts, implement getVersion, checkForUpdates, getUpdateStatus, and compareVersions functions with update notifications
|
||||
- Establish bidirectional reference between Nupst and NupstSnmp to support version logging
|
||||
- Update systemd service status output to include version information
|
||||
|
||||
## 2025-03-25 - 1.9.0 - feat(cli)
|
||||
Add update command to CLI to update NUPST from repository and refresh the systemd service
|
||||
|
||||
|
@ -3,6 +3,6 @@
|
||||
*/
|
||||
export const commitinfo = {
|
||||
name: '@serve.zone/nupst',
|
||||
version: '1.9.0',
|
||||
version: '1.10.0',
|
||||
description: 'Node.js UPS Shutdown Tool for SNMP-enabled UPS devices'
|
||||
}
|
||||
|
15
ts/daemon.ts
15
ts/daemon.ts
@ -152,6 +152,21 @@ export class NupstDaemon {
|
||||
await this.loadConfig();
|
||||
this.logConfigLoaded();
|
||||
|
||||
// Log version information
|
||||
this.snmp.getNupst().logVersionInfo(false); // Don't check for updates immediately on startup
|
||||
|
||||
// Check for updates in the background
|
||||
this.snmp.getNupst().checkForUpdates().then(updateAvailable => {
|
||||
if (updateAvailable) {
|
||||
const updateStatus = this.snmp.getNupst().getUpdateStatus();
|
||||
console.log('┌─ Update Available ───────────────────────┐');
|
||||
console.log(`│ Current Version: ${updateStatus.currentVersion}`);
|
||||
console.log(`│ Latest Version: ${updateStatus.latestVersion}`);
|
||||
console.log('│ Run "sudo nupst update" to update');
|
||||
console.log('└──────────────────────────────────────────┘');
|
||||
}
|
||||
}).catch(() => {}); // Ignore errors checking for updates
|
||||
|
||||
// Start UPS monitoring
|
||||
this.isRunning = true;
|
||||
await this.monitor();
|
||||
|
146
ts/nupst.ts
146
ts/nupst.ts
@ -1,6 +1,9 @@
|
||||
import { NupstSnmp } from './snmp.js';
|
||||
import { NupstDaemon } from './daemon.js';
|
||||
import { NupstSystemd } from './systemd.js';
|
||||
import { commitinfo } from './00_commitinfo_data.js';
|
||||
import { spawn } from 'child_process';
|
||||
import * as https from 'https';
|
||||
|
||||
/**
|
||||
* Main Nupst class that coordinates all components
|
||||
@ -10,12 +13,15 @@ export class Nupst {
|
||||
private readonly snmp: NupstSnmp;
|
||||
private readonly daemon: NupstDaemon;
|
||||
private readonly systemd: NupstSystemd;
|
||||
private updateAvailable: boolean = false;
|
||||
private latestVersion: string = '';
|
||||
|
||||
/**
|
||||
* Create a new Nupst instance with all necessary components
|
||||
*/
|
||||
constructor() {
|
||||
this.snmp = new NupstSnmp();
|
||||
this.snmp.setNupst(this); // Set up bidirectional reference
|
||||
this.daemon = new NupstDaemon(this.snmp);
|
||||
this.systemd = new NupstSystemd(this.daemon);
|
||||
}
|
||||
@ -40,4 +46,144 @@ export class Nupst {
|
||||
public getSystemd(): NupstSystemd {
|
||||
return this.systemd;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current version of NUPST
|
||||
* @returns The current version string
|
||||
*/
|
||||
public getVersion(): string {
|
||||
return commitinfo.version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if an update is available
|
||||
* @returns Promise resolving to true if an update is available
|
||||
*/
|
||||
public async checkForUpdates(): Promise<boolean> {
|
||||
try {
|
||||
const latestVersion = await this.getLatestVersion();
|
||||
const currentVersion = this.getVersion();
|
||||
|
||||
// Compare versions
|
||||
this.updateAvailable = this.compareVersions(latestVersion, currentVersion) > 0;
|
||||
this.latestVersion = latestVersion;
|
||||
|
||||
return this.updateAvailable;
|
||||
} catch (error) {
|
||||
console.error(`Error checking for updates: ${error.message}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get update status information
|
||||
* @returns Object with update status information
|
||||
*/
|
||||
public getUpdateStatus(): {
|
||||
currentVersion: string,
|
||||
latestVersion: string,
|
||||
updateAvailable: boolean
|
||||
} {
|
||||
return {
|
||||
currentVersion: this.getVersion(),
|
||||
latestVersion: this.latestVersion || this.getVersion(),
|
||||
updateAvailable: this.updateAvailable
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the latest version from npm registry
|
||||
* @returns Promise resolving to the latest version string
|
||||
*/
|
||||
private async getLatestVersion(): Promise<string> {
|
||||
return new Promise<string>((resolve, reject) => {
|
||||
const options = {
|
||||
hostname: 'registry.npmjs.org',
|
||||
path: '/@serve.zone/nupst',
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'User-Agent': `nupst/${this.getVersion()}`
|
||||
}
|
||||
};
|
||||
|
||||
const req = https.request(options, (res) => {
|
||||
let data = '';
|
||||
|
||||
res.on('data', (chunk) => {
|
||||
data += chunk;
|
||||
});
|
||||
|
||||
res.on('end', () => {
|
||||
try {
|
||||
const response = JSON.parse(data);
|
||||
if (response['dist-tags'] && response['dist-tags'].latest) {
|
||||
resolve(response['dist-tags'].latest);
|
||||
} else {
|
||||
reject(new Error('Failed to parse version from npm registry response'));
|
||||
}
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
req.on('error', (error) => {
|
||||
reject(error);
|
||||
});
|
||||
|
||||
req.end();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare two semantic version strings
|
||||
* @param versionA First version
|
||||
* @param versionB Second version
|
||||
* @returns -1 if versionA < versionB, 0 if equal, 1 if versionA > versionB
|
||||
*/
|
||||
private compareVersions(versionA: string, versionB: string): number {
|
||||
const partsA = versionA.split('.').map(part => parseInt(part, 10));
|
||||
const partsB = versionB.split('.').map(part => parseInt(part, 10));
|
||||
|
||||
for (let i = 0; i < Math.max(partsA.length, partsB.length); i++) {
|
||||
const partA = i < partsA.length ? partsA[i] : 0;
|
||||
const partB = i < partsB.length ? partsB[i] : 0;
|
||||
|
||||
if (partA > partB) return 1;
|
||||
if (partA < partB) return -1;
|
||||
}
|
||||
|
||||
return 0; // Versions are equal
|
||||
}
|
||||
|
||||
/**
|
||||
* Log the current version and update status
|
||||
*/
|
||||
public logVersionInfo(checkForUpdates: boolean = true): void {
|
||||
const version = this.getVersion();
|
||||
console.log('┌─ NUPST Version ────────────────────────┐');
|
||||
console.log(`│ Current Version: ${version}`);
|
||||
|
||||
if (this.updateAvailable && this.latestVersion) {
|
||||
console.log(`│ Update Available: ${this.latestVersion}`);
|
||||
console.log('│ Run "sudo nupst update" to update');
|
||||
} else if (checkForUpdates) {
|
||||
console.log('│ Checking for updates...');
|
||||
this.checkForUpdates().then(updateAvailable => {
|
||||
if (updateAvailable) {
|
||||
console.log(`│ Update Available: ${this.latestVersion}`);
|
||||
console.log('│ Run "sudo nupst update" to update');
|
||||
} else {
|
||||
console.log('│ You are running the latest version');
|
||||
}
|
||||
console.log('└──────────────────────────────────────────┘');
|
||||
}).catch(() => {
|
||||
console.log('│ Could not check for updates');
|
||||
console.log('└──────────────────────────────────────────┘');
|
||||
});
|
||||
} else {
|
||||
console.log('└──────────────────────────────────────────┘');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -15,6 +15,8 @@ const execAsync = promisify(exec);
|
||||
export class NupstSnmp {
|
||||
// Active OID set
|
||||
private activeOIDs: OIDSet;
|
||||
// Reference to the parent Nupst instance
|
||||
private nupst: any; // Type 'any' to avoid circular dependency
|
||||
|
||||
// Default SNMP configuration
|
||||
private readonly DEFAULT_CONFIG: SnmpConfig = {
|
||||
@ -43,6 +45,21 @@ export class NupstSnmp {
|
||||
this.activeOIDs = UpsOidSets.getOidSet('cyberpower');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set reference to the main Nupst instance
|
||||
* @param nupst Reference to the main Nupst instance
|
||||
*/
|
||||
public setNupst(nupst: any): void {
|
||||
this.nupst = nupst;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get reference to the main Nupst instance
|
||||
*/
|
||||
public getNupst(): any {
|
||||
return this.nupst;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set active OID set based on UPS model
|
||||
* @param config SNMP configuration
|
||||
|
@ -129,6 +129,9 @@ WantedBy=multi-user.target
|
||||
*/
|
||||
public async getStatus(): Promise<void> {
|
||||
try {
|
||||
// Display version information
|
||||
this.daemon.getNupstSnmp().getNupst().logVersionInfo();
|
||||
|
||||
// Check if config exists first
|
||||
try {
|
||||
await this.checkConfigExists();
|
||||
|
Loading…
x
Reference in New Issue
Block a user