feat(core): Centralize timeouts/constants, add CLI prompt helpers, and introduce webhook/script actions with safety and SNMP refactors

This commit is contained in:
2026-01-29 17:04:12 +00:00
parent d0e3a4ae74
commit 07648b4880
24 changed files with 1019 additions and 590 deletions

View File

@@ -11,6 +11,7 @@ import { theme, symbols, getBatteryColor, getRuntimeColor, formatPowerStatus } f
import type { IActionConfig } from './actions/base-action.ts';
import { ActionManager, type IActionContext, type TPowerStatus } from './actions/index.ts';
import { NupstHttpServer } from './http-server.ts';
import { TIMING, THRESHOLDS, UI } from './constants.ts';
const execAsync = promisify(exec);
const execFileAsync = promisify(execFile);
@@ -144,8 +145,8 @@ export class NupstDaemon {
type: 'shutdown',
triggerMode: 'onlyThresholds',
thresholds: {
battery: 60, // Shutdown when battery below 60%
runtime: 20, // Shutdown when runtime below 20 minutes
battery: THRESHOLDS.DEFAULT_BATTERY_PERCENT, // Shutdown when battery below 60%
runtime: THRESHOLDS.DEFAULT_RUNTIME_MINUTES, // Shutdown when runtime below 20 minutes
},
shutdownDelay: 5,
},
@@ -153,7 +154,7 @@ export class NupstDaemon {
},
],
groups: [],
checkInterval: 30000, // Check every 30 seconds
checkInterval: TIMING.CHECK_INTERVAL_MS, // Check every 30 seconds
}
private config: INupstConfig;
@@ -282,20 +283,23 @@ export class NupstDaemon {
this.logConfigLoaded();
// Log version information
this.snmp.getNupst().logVersionInfo(false); // Don't check for updates immediately on startup
const nupst = this.snmp.getNupst();
if (nupst) {
nupst.logVersionInfo(false); // Don't check for updates immediately on startup
// Check for updates in the background
this.snmp.getNupst().checkForUpdates().then((updateAvailable: boolean) => {
if (updateAvailable) {
const updateStatus = this.snmp.getNupst().getUpdateStatus();
const boxWidth = 45;
logger.logBoxTitle('Update Available', boxWidth);
logger.logBoxLine(`Current Version: ${updateStatus.currentVersion}`);
logger.logBoxLine(`Latest Version: ${updateStatus.latestVersion}`);
logger.logBoxLine('Run "sudo nupst update" to update');
logger.logBoxEnd();
}
}).catch(() => {}); // Ignore errors checking for updates
// Check for updates in the background
nupst.checkForUpdates().then((updateAvailable: boolean) => {
if (updateAvailable) {
const updateStatus = nupst.getUpdateStatus();
const boxWidth = 45;
logger.logBoxTitle('Update Available', boxWidth);
logger.logBoxLine(`Current Version: ${updateStatus.currentVersion}`);
logger.logBoxLine(`Latest Version: ${updateStatus.latestVersion}`);
logger.logBoxLine('Run "sudo nupst update" to update');
logger.logBoxEnd();
}
}).catch(() => {}); // Ignore errors checking for updates
}
// Initialize UPS status tracking
this.initializeUpsStatus();
@@ -441,7 +445,6 @@ export class NupstDaemon {
}
let lastLogTime = 0; // Track when we last logged status
const LOG_INTERVAL = 5 * 60 * 1000; // Log at least every 5 minutes (300000ms)
// Monitor continuously
while (this.isRunning) {
@@ -451,7 +454,7 @@ export class NupstDaemon {
// Log periodic status update
const currentTime = Date.now();
if (currentTime - lastLogTime >= LOG_INTERVAL) {
if (currentTime - lastLogTime >= TIMING.LOG_INTERVAL_MS) {
this.logAllUpsStatus();
lastLogTime = currentTime;
}
@@ -789,21 +792,18 @@ export class NupstDaemon {
* Force immediate shutdown if any UPS gets critically low
*/
private async monitorDuringShutdown(): Promise<void> {
const EMERGENCY_RUNTIME_THRESHOLD = 5; // 5 minutes remaining is critical
const CHECK_INTERVAL = 30000; // Check every 30 seconds during shutdown
const MAX_MONITORING_TIME = 5 * 60 * 1000; // Max 5 minutes of monitoring
const startTime = Date.now();
logger.log('');
logger.logBoxTitle('Shutdown Monitoring Active', 60, 'warning');
logger.logBoxLine(`Emergency threshold: ${EMERGENCY_RUNTIME_THRESHOLD} minutes runtime`);
logger.logBoxLine(`Check interval: ${CHECK_INTERVAL / 1000} seconds`);
logger.logBoxLine(`Max monitoring time: ${MAX_MONITORING_TIME / 1000} seconds`);
logger.logBoxTitle('Shutdown Monitoring Active', UI.WIDE_BOX_WIDTH, 'warning');
logger.logBoxLine(`Emergency threshold: ${THRESHOLDS.EMERGENCY_RUNTIME_MINUTES} minutes runtime`);
logger.logBoxLine(`Check interval: ${TIMING.SHUTDOWN_CHECK_INTERVAL_MS / 1000} seconds`);
logger.logBoxLine(`Max monitoring time: ${TIMING.MAX_SHUTDOWN_MONITORING_MS / 1000} seconds`);
logger.logBoxEnd();
logger.log('');
// Continue monitoring until max monitoring time is reached
while (Date.now() - startTime < MAX_MONITORING_TIME) {
while (Date.now() - startTime < TIMING.MAX_SHUTDOWN_MONITORING_MS) {
try {
logger.info('Checking UPS status during shutdown...');
@@ -827,7 +827,7 @@ export class NupstDaemon {
const batteryColor = getBatteryColor(status.batteryCapacity);
const runtimeColor = getRuntimeColor(status.batteryRuntime);
const isCritical = status.batteryRuntime < EMERGENCY_RUNTIME_THRESHOLD;
const isCritical = status.batteryRuntime < THRESHOLDS.EMERGENCY_RUNTIME_MINUTES;
rows.push({
name: ups.name,
@@ -868,7 +868,7 @@ export class NupstDaemon {
logger.logBoxLine(
`UPS ${emergencyUps.ups.name} runtime critically low: ${emergencyUps.status.batteryRuntime} minutes`,
);
logger.logBoxLine(`Emergency threshold: ${EMERGENCY_RUNTIME_THRESHOLD} minutes`);
logger.logBoxLine(`Emergency threshold: ${THRESHOLDS.EMERGENCY_RUNTIME_MINUTES} minutes`);
logger.logBoxLine('Forcing immediate shutdown!');
logger.logBoxEnd();
logger.log('');
@@ -879,14 +879,14 @@ export class NupstDaemon {
}
// Wait before checking again
await this.sleep(CHECK_INTERVAL);
await this.sleep(TIMING.SHUTDOWN_CHECK_INTERVAL_MS);
} catch (error) {
logger.error(
`Error monitoring UPS during shutdown: ${
error instanceof Error ? error.message : String(error)
}`,
);
await this.sleep(CHECK_INTERVAL);
await this.sleep(TIMING.SHUTDOWN_CHECK_INTERVAL_MS);
}
}
@@ -988,12 +988,10 @@ export class NupstDaemon {
* Watches for config changes and reloads when detected
*/
private async idleMonitoring(): Promise<void> {
const IDLE_CHECK_INTERVAL = 60000; // Check every 60 seconds
let lastConfigCheck = Date.now();
const CONFIG_CHECK_INTERVAL = 60000; // Check config every minute
logger.log('Entering idle monitoring mode...');
logger.log('Daemon will check for config changes every 60 seconds');
logger.log(`Daemon will check for config changes every ${TIMING.IDLE_CHECK_INTERVAL_MS / 1000} seconds`);
// Start file watcher for hot-reload
this.watchConfigFile();
@@ -1003,7 +1001,7 @@ export class NupstDaemon {
const currentTime = Date.now();
// Periodically check if config has been updated
if (currentTime - lastConfigCheck >= CONFIG_CHECK_INTERVAL) {
if (currentTime - lastConfigCheck >= TIMING.CONFIG_CHECK_INTERVAL_MS) {
try {
// Try to load config
const newConfig = await this.loadConfig();
@@ -1023,12 +1021,12 @@ export class NupstDaemon {
lastConfigCheck = currentTime;
}
await this.sleep(IDLE_CHECK_INTERVAL);
await this.sleep(TIMING.IDLE_CHECK_INTERVAL_MS);
} catch (error) {
logger.error(
`Error during idle monitoring: ${error instanceof Error ? error.message : String(error)}`,
);
await this.sleep(IDLE_CHECK_INTERVAL);
await this.sleep(TIMING.IDLE_CHECK_INTERVAL_MS);
}
}