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

@@ -4,8 +4,17 @@ import { Nupst } from '../nupst.ts';
import { logger, type ITableColumn } from '../logger.ts';
import { theme } from '../colors.ts';
import * as helpers from '../helpers/index.ts';
import type { TUpsModel } from '../snmp/types.ts';
import type { INupstConfig } from '../daemon.ts';
import type { ISnmpConfig, TUpsModel, IUpsStatus as ISnmpUpsStatus } from '../snmp/types.ts';
import type { INupstConfig, IUpsConfig, IUpsStatus } from '../daemon.ts';
import type { IActionConfig } from '../actions/base-action.ts';
/**
* Thresholds configuration for CLI display
*/
interface IThresholds {
battery: number;
runtime: number;
}
/**
* Class for handling UPS-related CLI commands
@@ -27,29 +36,9 @@ export class UpsHandler {
*/
public async add(): Promise<void> {
try {
// Import readline module for user input
const readline = await import('node:readline');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
// Helper function to prompt for input
const prompt = (question: string): Promise<string> => {
return new Promise((resolve) => {
rl.question(question, (answer: string) => {
resolve(answer);
});
});
};
try {
await helpers.withPrompt(async (prompt) => {
await this.runAddProcess(prompt);
} finally {
rl.close();
process.stdin.destroy();
}
});
} catch (error) {
logger.error(`Add UPS error: ${error instanceof Error ? error.message : String(error)}`);
}
@@ -160,29 +149,9 @@ export class UpsHandler {
*/
public async edit(upsId?: string): Promise<void> {
try {
// Import readline module for user input
const readline = await import('node:readline');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
// Helper function to prompt for input
const prompt = (question: string): Promise<string> => {
return new Promise((resolve) => {
rl.question(question, (answer: string) => {
resolve(answer);
});
});
};
try {
await helpers.withPrompt(async (prompt) => {
await this.runEditProcess(upsId, prompt);
} finally {
rl.close();
process.stdin.destroy();
}
});
} catch (error) {
logger.error(`Edit UPS error: ${error instanceof Error ? error.message : String(error)}`);
}
@@ -337,23 +306,11 @@ export class UpsHandler {
const upsToDelete = config.upsDevices[upsIndex];
// Get confirmation before deleting
const readline = await import('node:readline');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
const confirm = await new Promise<string>((resolve) => {
rl.question(
`Are you sure you want to delete UPS "${upsToDelete.name}" (${upsId})? [y/N]: `,
(answer) => {
resolve(answer.toLowerCase());
},
);
});
rl.close();
process.stdin.destroy();
const { prompt, close } = await helpers.createPrompt();
const confirm = (await prompt(
`Are you sure you want to delete UPS "${upsToDelete.name}" (${upsId})? [y/N]: `,
)).toLowerCase();
close();
if (confirm !== 'y' && confirm !== 'yes') {
logger.log('Deletion cancelled.');
@@ -509,7 +466,7 @@ export class UpsHandler {
* Display the configuration for testing
* @param config Current configuration or individual UPS configuration
*/
private displayTestConfig(config: any): void {
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 || {};
@@ -571,7 +528,7 @@ export class UpsHandler {
* Test connection to the UPS
* @param config Current UPS configuration or legacy config
*/
private async testConnection(config: any): Promise<void> {
private async testConnection(config: IUpsConfig | INupstConfig): Promise<void> {
const upsId = config.id || 'default';
const upsName = config.name || 'Default UPS';
logger.log(`\nTesting connection to UPS: ${upsName} (${upsId})...`);
@@ -610,7 +567,7 @@ export class UpsHandler {
* @param status UPS status
* @param thresholds Threshold configuration
*/
private analyzeThresholds(status: any, thresholds: any): void {
private analyzeThresholds(status: ISnmpUpsStatus, thresholds: IThresholds): void {
const boxWidth = 45;
logger.logBoxTitle('Threshold Analysis', boxWidth);
@@ -649,7 +606,7 @@ export class UpsHandler {
* @param prompt Function to prompt for user input
*/
private async gatherSnmpSettings(
snmpConfig: any,
snmpConfig: Partial<ISnmpConfig>,
prompt: (question: string) => Promise<string>,
): Promise<void> {
// SNMP IP Address
@@ -693,7 +650,7 @@ export class UpsHandler {
* @param prompt Function to prompt for user input
*/
private async gatherSnmpV3Settings(
snmpConfig: any,
snmpConfig: Partial<ISnmpConfig>,
prompt: (question: string) => Promise<string>,
): Promise<void> {
logger.log('');
@@ -771,7 +728,7 @@ export class UpsHandler {
* @param prompt Function to prompt for user input
*/
private async gatherAuthenticationSettings(
snmpConfig: any,
snmpConfig: Partial<ISnmpConfig>,
prompt: (question: string) => Promise<string>,
): Promise<void> {
// Authentication protocol
@@ -798,7 +755,7 @@ export class UpsHandler {
* @param prompt Function to prompt for user input
*/
private async gatherPrivacySettings(
snmpConfig: any,
snmpConfig: Partial<ISnmpConfig>,
prompt: (question: string) => Promise<string>,
): Promise<void> {
// Privacy protocol
@@ -823,7 +780,7 @@ export class UpsHandler {
* @param prompt Function to prompt for user input
*/
private async gatherUpsModelSettings(
snmpConfig: any,
snmpConfig: Partial<ISnmpConfig>,
prompt: (question: string) => Promise<string>,
): Promise<void> {
logger.log('');
@@ -888,7 +845,7 @@ export class UpsHandler {
* @param prompt Function to prompt for user input
*/
private async gatherActionSettings(
actions: any[],
actions: IActionConfig[],
prompt: (question: string) => Promise<string>,
): Promise<void> {
logger.log('');
@@ -915,7 +872,7 @@ export class UpsHandler {
const typeInput = await prompt('Select action type [1]: ');
const typeValue = parseInt(typeInput, 10) || 1;
const action: any = {};
const action: Partial<IActionConfig> = {};
if (typeValue === 1) {
// Shutdown action
@@ -1014,8 +971,8 @@ export class UpsHandler {
};
}
actions.push(action);
logger.success(`${action.type.charAt(0).toUpperCase() + action.type.slice(1)} action added (mode: ${action.triggerMode || 'powerChangesAndThresholds'})`);
actions.push(action as IActionConfig);
logger.success(`${action.type!.charAt(0).toUpperCase() + action.type!.slice(1)} action added (mode: ${action.triggerMode || 'powerChangesAndThresholds'})`);
const more = await prompt('Add another action? (y/N): ');
addMore = more.toLowerCase() === 'y';
@@ -1031,7 +988,7 @@ export class UpsHandler {
* Display UPS configuration summary
* @param ups UPS configuration
*/
private displayUpsConfigSummary(ups: any): void {
private displayUpsConfigSummary(ups: IUpsConfig): void {
const boxWidth = 45;
logger.log('');
logger.logBoxTitle(`UPS Configuration: ${ups.name}`, boxWidth);
@@ -1055,7 +1012,7 @@ export class UpsHandler {
* @param prompt Function to prompt for user input
*/
private async optionallyTestConnection(
snmpConfig: any,
snmpConfig: ISnmpConfig,
prompt: (question: string) => Promise<string>,
): Promise<void> {
const testConnection = await prompt(