Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| fda072d15e | |||
| c7786e9626 |
10
changelog.md
10
changelog.md
@@ -1,5 +1,15 @@
|
||||
# Changelog
|
||||
|
||||
## 2026-01-29 - 5.2.1 - fix(cli(ups-handler), systemd)
|
||||
add type guards and null checks for UPS configs; improve SNMP handling and prompts; guard version display
|
||||
|
||||
- Introduce a type guard ('id' in config && 'name' in config) to distinguish IUpsConfig from legacy INupstConfig and route fields (snmp, checkInterval, name, id) accordingly.
|
||||
- displayTestConfig now handles missing SNMP by logging 'Not configured' and returning, computes checkInterval/upsName/upsId correctly, and uses groups only for true UPS configs.
|
||||
- testConnection now safely derives snmpConfig for both config types, throws if SNMP is missing, and caps test timeout to 10s for probes.
|
||||
- Clear auth/priv credentials by setting undefined (instead of empty strings) when disabling security levels to avoid invalid/empty string values.
|
||||
- Expanded customOIDs to include OUTPUT_LOAD, OUTPUT_POWER, OUTPUT_VOLTAGE, OUTPUT_CURRENT with defaults; trim prompt input and document RFC 1628 fallbacks.
|
||||
- systemd.displayVersionInfo: guard against missing nupst (silent return) and avoid errors when printing version info; use ignored catch variables for clarity.
|
||||
|
||||
## 2026-01-29 - 5.2.0 - feat(core)
|
||||
Centralize timeouts/constants, add CLI prompt helpers, and introduce webhook/script actions with safety and SNMP refactors
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@serve.zone/nupst",
|
||||
"version": "5.2.0",
|
||||
"version": "5.2.1",
|
||||
"exports": "./mod.ts",
|
||||
"nodeModulesDir": "auto",
|
||||
"tasks": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"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",
|
||||
"keywords": [
|
||||
"ups",
|
||||
|
||||
@@ -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'
|
||||
}
|
||||
|
||||
@@ -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: '',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user