feat(cli): modernize all CLI output to use logger tables
- Modernize ups list command with logger.logTable() - Modernize group list command with logger.logTable() - Completely rewrite config show with tables and proper box styling - Add professional column definitions with themed colors - Replace all manual table formatting (padEnd, pipe separators) - Improve visual hierarchy with appropriate box styles (info, warning, success) - Ensure consistent theming across all CLI commands
This commit is contained in:
252
ts/cli.ts
252
ts/cli.ts
@@ -1,6 +1,6 @@
|
||||
import { execSync } from 'node:child_process';
|
||||
import { Nupst } from './nupst.ts';
|
||||
import { logger } from './logger.ts';
|
||||
import { logger, type ITableColumn } from './logger.ts';
|
||||
import { theme, symbols } from './colors.ts';
|
||||
|
||||
/**
|
||||
@@ -303,154 +303,164 @@ export class NupstCli {
|
||||
try {
|
||||
await this.nupst.getDaemon().loadConfig();
|
||||
} catch (_error) {
|
||||
const errorBoxWidth = 45;
|
||||
logger.logBoxTitle('Configuration Error', errorBoxWidth);
|
||||
logger.logBoxLine('No configuration found.');
|
||||
logger.logBoxLine("Please run 'nupst setup' first to create a configuration.");
|
||||
logger.logBoxEnd();
|
||||
logger.logBox('Configuration Error', [
|
||||
'No configuration found.',
|
||||
"Please run 'nupst ups add' first to create a configuration.",
|
||||
], 50, 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
// Get current configuration
|
||||
const config = this.nupst.getDaemon().getConfig();
|
||||
|
||||
const boxWidth = 50;
|
||||
logger.logBoxTitle('NUPST Configuration', boxWidth);
|
||||
|
||||
// Check if multi-UPS config
|
||||
if (config.upsDevices && Array.isArray(config.upsDevices)) {
|
||||
// Multi-UPS configuration
|
||||
logger.logBoxLine(`UPS Devices: ${config.upsDevices.length}`);
|
||||
logger.logBoxLine(`Groups: ${config.groups ? config.groups.length : 0}`);
|
||||
logger.logBoxLine(`Check Interval: ${config.checkInterval / 1000} seconds`);
|
||||
logger.logBoxLine('');
|
||||
logger.logBoxLine('Configuration File Location:');
|
||||
logger.logBoxLine(' /etc/nupst/config.json');
|
||||
logger.logBoxEnd();
|
||||
// === Multi-UPS Configuration ===
|
||||
|
||||
// Overview Box
|
||||
logger.log('');
|
||||
logger.logBox('NUPST Configuration', [
|
||||
`UPS Devices: ${theme.highlight(String(config.upsDevices.length))}`,
|
||||
`Groups: ${theme.highlight(String(config.groups ? config.groups.length : 0))}`,
|
||||
`Check Interval: ${theme.info(String(config.checkInterval / 1000))} seconds`,
|
||||
'',
|
||||
theme.dim('Configuration File:'),
|
||||
` ${theme.path('/etc/nupst/config.json')}`,
|
||||
], 60, 'info');
|
||||
|
||||
// Show UPS devices
|
||||
// UPS Devices Table
|
||||
if (config.upsDevices.length > 0) {
|
||||
logger.logBoxTitle('UPS Devices', boxWidth);
|
||||
for (const ups of config.upsDevices) {
|
||||
logger.logBoxLine(`${ups.name} (${ups.id}):`);
|
||||
logger.logBoxLine(` Host: ${ups.snmp.host}:${ups.snmp.port}`);
|
||||
logger.logBoxLine(` Model: ${ups.snmp.upsModel}`);
|
||||
logger.logBoxLine(
|
||||
` Thresholds: ${ups.thresholds.battery}% battery, ${ups.thresholds.runtime} min runtime`,
|
||||
);
|
||||
logger.logBoxLine(
|
||||
` Groups: ${ups.groups.length > 0 ? ups.groups.join(', ') : 'None'}`,
|
||||
);
|
||||
logger.logBoxLine('');
|
||||
}
|
||||
logger.logBoxEnd();
|
||||
const upsRows = config.upsDevices.map((ups) => ({
|
||||
name: ups.name,
|
||||
id: theme.dim(ups.id),
|
||||
host: `${ups.snmp.host}:${ups.snmp.port}`,
|
||||
model: ups.snmp.upsModel || 'cyberpower',
|
||||
thresholds: `${ups.thresholds.battery}% / ${ups.thresholds.runtime}min`,
|
||||
groups: ups.groups.length > 0 ? ups.groups.join(', ') : theme.dim('None'),
|
||||
}));
|
||||
|
||||
const upsColumns: ITableColumn[] = [
|
||||
{ header: 'Name', key: 'name', align: 'left', color: theme.highlight },
|
||||
{ header: 'ID', key: 'id', align: 'left' },
|
||||
{ header: 'Host:Port', key: 'host', align: 'left', color: theme.info },
|
||||
{ header: 'Model', key: 'model', align: 'left' },
|
||||
{ header: 'Battery/Runtime', key: 'thresholds', align: 'left' },
|
||||
{ header: 'Groups', key: 'groups', align: 'left' },
|
||||
];
|
||||
|
||||
logger.log('');
|
||||
logger.info(`UPS Devices (${config.upsDevices.length}):`);
|
||||
logger.log('');
|
||||
logger.logTable(upsColumns, upsRows);
|
||||
}
|
||||
|
||||
// Show groups
|
||||
// Groups Table
|
||||
if (config.groups && config.groups.length > 0) {
|
||||
logger.logBoxTitle('UPS Groups', boxWidth);
|
||||
for (const group of config.groups) {
|
||||
logger.logBoxLine(`${group.name} (${group.id}):`);
|
||||
logger.logBoxLine(` Mode: ${group.mode}`);
|
||||
if (group.description) {
|
||||
logger.logBoxLine(` Description: ${group.description}`);
|
||||
}
|
||||
|
||||
// List UPS devices in this group
|
||||
const groupRows = config.groups.map((group) => {
|
||||
const upsInGroup = config.upsDevices.filter((ups) =>
|
||||
ups.groups && ups.groups.includes(group.id)
|
||||
);
|
||||
logger.logBoxLine(
|
||||
` UPS Devices: ${
|
||||
upsInGroup.length > 0 ? upsInGroup.map((ups) => ups.name).join(', ') : 'None'
|
||||
}`,
|
||||
);
|
||||
logger.logBoxLine('');
|
||||
}
|
||||
logger.logBoxEnd();
|
||||
return {
|
||||
name: group.name,
|
||||
id: theme.dim(group.id),
|
||||
mode: group.mode,
|
||||
upsCount: String(upsInGroup.length),
|
||||
ups: upsInGroup.length > 0
|
||||
? upsInGroup.map((ups) => ups.name).join(', ')
|
||||
: theme.dim('None'),
|
||||
description: group.description || theme.dim('—'),
|
||||
};
|
||||
});
|
||||
|
||||
const groupColumns: ITableColumn[] = [
|
||||
{ header: 'Name', key: 'name', align: 'left', color: theme.highlight },
|
||||
{ header: 'ID', key: 'id', align: 'left' },
|
||||
{ header: 'Mode', key: 'mode', align: 'left', color: theme.info },
|
||||
{ header: 'UPS', key: 'upsCount', align: 'right' },
|
||||
{ header: 'UPS Devices', key: 'ups', align: 'left' },
|
||||
{ header: 'Description', key: 'description', align: 'left' },
|
||||
];
|
||||
|
||||
logger.log('');
|
||||
logger.info(`UPS Groups (${config.groups.length}):`);
|
||||
logger.log('');
|
||||
logger.logTable(groupColumns, groupRows);
|
||||
}
|
||||
} else {
|
||||
// Legacy single UPS configuration
|
||||
if (!config.snmp) {
|
||||
logger.logBoxLine('Error: Legacy configuration missing SNMP settings');
|
||||
} else {
|
||||
// SNMP Settings
|
||||
logger.logBoxLine('SNMP Settings:');
|
||||
logger.logBoxLine(` Host: ${config.snmp.host}`);
|
||||
logger.logBoxLine(` Port: ${config.snmp.port}`);
|
||||
logger.logBoxLine(` Version: ${config.snmp.version}`);
|
||||
logger.logBoxLine(` UPS Model: ${config.snmp.upsModel || 'cyberpower'}`);
|
||||
|
||||
if (config.snmp.version === 1 || config.snmp.version === 2) {
|
||||
logger.logBoxLine(` Community: ${config.snmp.community}`);
|
||||
} else if (config.snmp.version === 3) {
|
||||
logger.logBoxLine(` Security Level: ${config.snmp.securityLevel}`);
|
||||
logger.logBoxLine(` Username: ${config.snmp.username}`);
|
||||
|
||||
// Show auth and privacy details based on security level
|
||||
if (
|
||||
config.snmp.securityLevel === 'authNoPriv' ||
|
||||
config.snmp.securityLevel === 'authPriv'
|
||||
) {
|
||||
logger.logBoxLine(` Auth Protocol: ${config.snmp.authProtocol || 'None'}`);
|
||||
}
|
||||
|
||||
if (config.snmp.securityLevel === 'authPriv') {
|
||||
logger.logBoxLine(` Privacy Protocol: ${config.snmp.privProtocol || 'None'}`);
|
||||
}
|
||||
|
||||
// Show timeout value
|
||||
logger.logBoxLine(` Timeout: ${config.snmp.timeout / 1000} seconds`);
|
||||
}
|
||||
|
||||
// Show OIDs if custom model is selected
|
||||
if (config.snmp.upsModel === 'custom' && config.snmp.customOIDs) {
|
||||
logger.logBoxLine('Custom OIDs:');
|
||||
logger.logBoxLine(
|
||||
` Power Status: ${config.snmp.customOIDs.POWER_STATUS || 'Not set'}`,
|
||||
);
|
||||
logger.logBoxLine(
|
||||
` Battery Capacity: ${config.snmp.customOIDs.BATTERY_CAPACITY || 'Not set'}`,
|
||||
);
|
||||
logger.logBoxLine(
|
||||
` Battery Runtime: ${config.snmp.customOIDs.BATTERY_RUNTIME || 'Not set'}`,
|
||||
);
|
||||
}
|
||||
// === Legacy Single UPS Configuration ===
|
||||
|
||||
if (!config.snmp || !config.thresholds) {
|
||||
logger.logBox('Configuration Error', [
|
||||
'Error: Legacy configuration missing SNMP or threshold settings',
|
||||
], 60, 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
// Thresholds
|
||||
if (!config.thresholds) {
|
||||
logger.logBoxLine('Error: Legacy configuration missing threshold settings');
|
||||
} else {
|
||||
logger.logBoxLine('Thresholds:');
|
||||
logger.logBoxLine(` Battery: ${config.thresholds.battery}%`);
|
||||
logger.logBoxLine(` Runtime: ${config.thresholds.runtime} minutes`);
|
||||
}
|
||||
logger.logBoxLine(`Check Interval: ${config.checkInterval / 1000} seconds`);
|
||||
|
||||
// Configuration file location
|
||||
logger.logBoxLine('');
|
||||
logger.logBoxLine('Configuration File Location:');
|
||||
logger.logBoxLine(' /etc/nupst/config.json');
|
||||
logger.logBoxLine('');
|
||||
logger.logBoxLine('Note: Using legacy single-UPS configuration format.');
|
||||
logger.logBoxLine('Consider using "nupst add" to migrate to multi-UPS format.');
|
||||
|
||||
logger.logBoxEnd();
|
||||
logger.log('');
|
||||
logger.logBox('NUPST Configuration (Legacy)', [
|
||||
theme.warning('Legacy single-UPS configuration format'),
|
||||
'',
|
||||
theme.dim('SNMP Settings:'),
|
||||
` Host: ${theme.info(config.snmp.host)}`,
|
||||
` Port: ${theme.info(String(config.snmp.port))}`,
|
||||
` Version: ${config.snmp.version}`,
|
||||
` UPS Model: ${config.snmp.upsModel || 'cyberpower'}`,
|
||||
...(config.snmp.version === 1 || config.snmp.version === 2
|
||||
? [` Community: ${config.snmp.community}`]
|
||||
: []
|
||||
),
|
||||
...(config.snmp.version === 3
|
||||
? [
|
||||
` Security Level: ${config.snmp.securityLevel}`,
|
||||
` Username: ${config.snmp.username}`,
|
||||
...(config.snmp.securityLevel === 'authNoPriv' || config.snmp.securityLevel === 'authPriv'
|
||||
? [` Auth Protocol: ${config.snmp.authProtocol || 'None'}`]
|
||||
: []
|
||||
),
|
||||
...(config.snmp.securityLevel === 'authPriv'
|
||||
? [` Privacy Protocol: ${config.snmp.privProtocol || 'None'}`]
|
||||
: []
|
||||
),
|
||||
` Timeout: ${config.snmp.timeout / 1000} seconds`,
|
||||
]
|
||||
: []
|
||||
),
|
||||
...(config.snmp.upsModel === 'custom' && config.snmp.customOIDs
|
||||
? [
|
||||
theme.dim('Custom OIDs:'),
|
||||
` Power Status: ${config.snmp.customOIDs.POWER_STATUS || 'Not set'}`,
|
||||
` Battery Capacity: ${config.snmp.customOIDs.BATTERY_CAPACITY || 'Not set'}`,
|
||||
` Battery Runtime: ${config.snmp.customOIDs.BATTERY_RUNTIME || 'Not set'}`,
|
||||
]
|
||||
: []
|
||||
),
|
||||
'',
|
||||
theme.dim('Thresholds:'),
|
||||
` Battery: ${theme.highlight(String(config.thresholds.battery))}%`,
|
||||
` Runtime: ${theme.highlight(String(config.thresholds.runtime))} minutes`,
|
||||
` Check Interval: ${config.checkInterval / 1000} seconds`,
|
||||
'',
|
||||
theme.dim('Configuration File:'),
|
||||
` ${theme.path('/etc/nupst/config.json')}`,
|
||||
'',
|
||||
theme.warning('Note: Using legacy single-UPS configuration format.'),
|
||||
`Consider using ${theme.command('nupst ups add')} to migrate to multi-UPS format.`,
|
||||
], 70, 'warning');
|
||||
}
|
||||
|
||||
// Show service status
|
||||
// Service Status
|
||||
try {
|
||||
const isActive =
|
||||
execSync('systemctl is-active nupst.service || true').toString().trim() === 'active';
|
||||
const isEnabled =
|
||||
execSync('systemctl is-enabled nupst.service || true').toString().trim() === 'enabled';
|
||||
|
||||
const statusBoxWidth = 45;
|
||||
logger.logBoxTitle('Service Status', statusBoxWidth);
|
||||
logger.logBoxLine(`Service Active: ${isActive ? 'Yes' : 'No'}`);
|
||||
logger.logBoxLine(`Service Enabled: ${isEnabled ? 'Yes' : 'No'}`);
|
||||
logger.logBoxEnd();
|
||||
logger.log('');
|
||||
logger.logBox('Service Status', [
|
||||
`Active: ${isActive ? theme.success('Yes') : theme.dim('No')}`,
|
||||
`Enabled: ${isEnabled ? theme.success('Yes') : theme.dim('No')}`,
|
||||
], 50, isActive ? 'success' : 'default');
|
||||
logger.log('');
|
||||
} catch (_error) {
|
||||
// Ignore errors checking service status
|
||||
}
|
||||
|
Reference in New Issue
Block a user