fix(cli(ups-handler), systemd): add type guards and null checks for UPS configs; improve SNMP handling and prompts; guard version display

This commit is contained in:
2026-01-29 17:07:57 +00:00
parent 91fe5f7ae6
commit c7786e9626
4 changed files with 67 additions and 29 deletions

View File

@@ -3,6 +3,6 @@
*/
export const commitinfo = {
name: '@serve.zone/nupst',
version: '5.2.0',
version: '5.2.1',
description: 'Network UPS Shutdown Tool - Monitor SNMP-enabled UPS devices and orchestrate graceful system shutdowns during power emergencies'
}

View File

@@ -467,18 +467,27 @@ export class UpsHandler {
* @param config Current configuration or individual UPS configuration
*/
private displayTestConfig(config: IUpsConfig | INupstConfig): void {
// Check if this is a UPS device or full configuration
const isUpsConfig = config.snmp;
const snmpConfig = isUpsConfig ? config.snmp : config.snmp || {};
const checkInterval = config.checkInterval || 30000;
// Type guard: IUpsConfig has 'id' and 'name' at root level, INupstConfig doesn't
const isUpsConfig = 'id' in config && 'name' in config;
// Get UPS name and ID if available
const upsName = config.name ? config.name : 'Default UPS';
const upsId = config.id ? config.id : 'default';
// Get SNMP config and other values based on config type
const snmpConfig: ISnmpConfig | undefined = isUpsConfig
? (config as IUpsConfig).snmp
: (config as INupstConfig).snmp;
const checkInterval = isUpsConfig ? 30000 : (config as INupstConfig).checkInterval || 30000;
const upsName = isUpsConfig ? (config as IUpsConfig).name : 'Default UPS';
const upsId = isUpsConfig ? (config as IUpsConfig).id : 'default';
const boxWidth = 45;
logger.logBoxTitle(`Testing Configuration: ${upsName}`, boxWidth);
logger.logBoxLine(`UPS ID: ${upsId}`);
if (!snmpConfig) {
logger.logBoxLine('SNMP Settings: Not configured');
logger.logBoxEnd();
return;
}
logger.logBoxLine('SNMP Settings:');
logger.logBoxLine(` Host: ${snmpConfig.host}`);
logger.logBoxLine(` Port: ${snmpConfig.port}`);
@@ -514,9 +523,10 @@ export class UpsHandler {
logger.logBoxLine(` Battery Runtime: ${snmpConfig.customOIDs.BATTERY_RUNTIME || 'Not set'}`);
}
// Show group assignments if this is a UPS config
if (config.groups && Array.isArray(config.groups)) {
if (isUpsConfig) {
const groups = (config as IUpsConfig).groups;
logger.logBoxLine(
`Group Assignments: ${config.groups.length === 0 ? 'None' : config.groups.join(', ')}`,
`Group Assignments: ${groups.length === 0 ? 'None' : groups.join(', ')}`,
);
}
@@ -529,15 +539,23 @@ export class UpsHandler {
* @param config Current UPS configuration or legacy config
*/
private async testConnection(config: IUpsConfig | INupstConfig): Promise<void> {
const upsId = config.id || 'default';
const upsName = config.name || 'Default UPS';
// Type guard: IUpsConfig has 'id' and 'name' at root level
const isUpsConfig = 'id' in config && 'name' in config;
const upsId = isUpsConfig ? (config as IUpsConfig).id : 'default';
const upsName = isUpsConfig ? (config as IUpsConfig).name : 'Default UPS';
logger.log(`\nTesting connection to UPS: ${upsName} (${upsId})...`);
try {
// Create a test config with a short timeout
const snmpConfig = config.snmp ? config.snmp : config.snmp;
// Get SNMP config based on config type
const snmpConfig: ISnmpConfig | undefined = isUpsConfig
? (config as IUpsConfig).snmp
: (config as INupstConfig).snmp;
const testConfig = {
if (!snmpConfig) {
throw new Error('SNMP configuration not found');
}
const testConfig: ISnmpConfig = {
...snmpConfig,
timeout: Math.min(snmpConfig.timeout, 10000), // Use at most 10 seconds for testing
};
@@ -675,17 +693,17 @@ export class UpsHandler {
if (secLevel === 1) {
snmpConfig.securityLevel = 'noAuthNoPriv';
// No auth, no priv - clear out authentication and privacy settings
snmpConfig.authProtocol = '';
snmpConfig.authKey = '';
snmpConfig.privProtocol = '';
snmpConfig.privKey = '';
snmpConfig.authProtocol = undefined;
snmpConfig.authKey = undefined;
snmpConfig.privProtocol = undefined;
snmpConfig.privKey = undefined;
// Set appropriate timeout for security level
snmpConfig.timeout = 5000; // 5 seconds for basic security
} else if (secLevel === 2) {
snmpConfig.securityLevel = 'authNoPriv';
// Auth, no priv - clear out privacy settings
snmpConfig.privProtocol = '';
snmpConfig.privKey = '';
snmpConfig.privProtocol = undefined;
snmpConfig.privKey = undefined;
// Set appropriate timeout for security level
snmpConfig.timeout = 10000; // 10 seconds for authentication
} else {
@@ -825,16 +843,21 @@ export class UpsHandler {
logger.info('Enter custom OIDs for your UPS:');
logger.dim('(Leave blank to use standard RFC 1628 OIDs as fallback)');
// Custom OIDs
// Custom OIDs - prompt for essential OIDs
const powerStatusOID = await prompt('Power Status OID: ');
const batteryCapacityOID = await prompt('Battery Capacity OID: ');
const batteryRuntimeOID = await prompt('Battery Runtime OID: ');
// Create custom OIDs object
// Create custom OIDs object with all required fields
// Empty strings will use RFC 1628 fallback for non-essential OIDs
snmpConfig.customOIDs = {
POWER_STATUS: powerStatusOID.trim(),
BATTERY_CAPACITY: batteryCapacityOID.trim(),
BATTERY_RUNTIME: batteryRuntimeOID.trim(),
OUTPUT_LOAD: '',
OUTPUT_POWER: '',
OUTPUT_VOLTAGE: '',
OUTPUT_CURRENT: '',
};
}
}

View File

@@ -142,11 +142,14 @@ WantedBy=multi-user.target
private async displayVersionInfo(): Promise<void> {
try {
const nupst = this.daemon.getNupstSnmp().getNupst();
if (!nupst) {
return;
}
const version = nupst.getVersion();
// Check for updates
const updateAvailable = await nupst.checkForUpdates();
// Display version info
if (updateAvailable) {
const updateStatus = nupst.getUpdateStatus();
@@ -161,13 +164,15 @@ WantedBy=multi-user.target
`${theme.dim('NUPST')} ${theme.dim('v' + version)} ${symbols.success} ${theme.success('Up to date')}`,
);
}
} catch (error) {
} catch (_error) {
// If version check fails, show at least the current version
try {
const nupst = this.daemon.getNupstSnmp().getNupst();
const version = nupst.getVersion();
logger.log('');
logger.log(`${theme.dim('NUPST')} ${theme.dim('v' + version)}`);
if (nupst) {
const version = nupst.getVersion();
logger.log('');
logger.log(`${theme.dim('NUPST')} ${theme.dim('v' + version)}`);
}
} catch (_innerError) {
// Silently fail if we can't even get the version
}