BREAKING CHANGE(snmp): refactor: update SNMP type definitions and interface names for consistency

This commit is contained in:
Philipp Kunz 2025-03-25 10:21:21 +00:00
parent 70c16fa0a6
commit ecfd171f97
9 changed files with 41 additions and 35 deletions

View File

@ -1,5 +1,11 @@
# Changelog # Changelog
## 2025-03-25 - 2.0.0 - BREAKING CHANGE(snmp)
refactor: update SNMP type definitions and interface names for consistency
- Renamed SnmpConfig to ISnmpConfig, OIDSet to IOidSet, UpsStatus to IUpsStatus, and UpsModel to TUpsModel in ts/snmp/types.ts.
- Updated internal references in ts/daemon.ts, ts/snmp/index.ts, ts/snmp/manager.ts, ts/snmp/oid-sets.ts, ts/snmp/packet-creator.ts, and ts/snmp/packet-parser.ts to use the new interface names.
## 2025-03-25 - 1.10.1 - fix(systemd/readme) ## 2025-03-25 - 1.10.1 - fix(systemd/readme)
Improve README documentation and fix UPS status retrieval in systemd service Improve README documentation and fix UPS status retrieval in systemd service

View File

@ -3,6 +3,6 @@
*/ */
export const commitinfo = { export const commitinfo = {
name: '@serve.zone/nupst', name: '@serve.zone/nupst',
version: '1.10.1', version: '2.0.0',
description: 'Node.js UPS Shutdown Tool for SNMP-enabled UPS devices' description: 'Node.js UPS Shutdown Tool for SNMP-enabled UPS devices'
} }

View File

@ -1,13 +1,13 @@
import * as fs from 'fs'; import * as fs from 'fs';
import * as path from 'path'; import * as path from 'path';
import { NupstSnmp, type SnmpConfig } from './snmp.js'; import { NupstSnmp, type ISnmpConfig } from './snmp.js';
/** /**
* Configuration interface for the daemon * Configuration interface for the daemon
*/ */
export interface NupstConfig { export interface INupstConfig {
/** SNMP configuration settings */ /** SNMP configuration settings */
snmp: SnmpConfig; snmp: ISnmpConfig;
/** Threshold settings for initiating shutdown */ /** Threshold settings for initiating shutdown */
thresholds: { thresholds: {
/** Shutdown when battery below this percentage */ /** Shutdown when battery below this percentage */
@ -28,7 +28,7 @@ export class NupstDaemon {
private readonly CONFIG_PATH = '/etc/nupst/config.json'; private readonly CONFIG_PATH = '/etc/nupst/config.json';
/** Default configuration */ /** Default configuration */
private readonly DEFAULT_CONFIG: NupstConfig = { private readonly DEFAULT_CONFIG: INupstConfig = {
snmp: { snmp: {
host: '127.0.0.1', host: '127.0.0.1',
port: 161, port: 161,
@ -52,7 +52,7 @@ export class NupstDaemon {
checkInterval: 30000, // Check every 30 seconds checkInterval: 30000, // Check every 30 seconds
}; };
private config: NupstConfig; private config: INupstConfig;
private snmp: NupstSnmp; private snmp: NupstSnmp;
private isRunning: boolean = false; private isRunning: boolean = false;
@ -68,7 +68,7 @@ export class NupstDaemon {
* Load configuration from file * Load configuration from file
* @throws Error if configuration file doesn't exist * @throws Error if configuration file doesn't exist
*/ */
public async loadConfig(): Promise<NupstConfig> { public async loadConfig(): Promise<INupstConfig> {
try { try {
// Check if config file exists // Check if config file exists
const configExists = fs.existsSync(this.CONFIG_PATH); const configExists = fs.existsSync(this.CONFIG_PATH);
@ -95,7 +95,7 @@ export class NupstDaemon {
/** /**
* Save configuration to file * Save configuration to file
*/ */
public async saveConfig(config: NupstConfig): Promise<void> { public async saveConfig(config: INupstConfig): Promise<void> {
try { try {
const configDir = path.dirname(this.CONFIG_PATH); const configDir = path.dirname(this.CONFIG_PATH);
if (!fs.existsSync(configDir)) { if (!fs.existsSync(configDir)) {
@ -125,7 +125,7 @@ export class NupstDaemon {
/** /**
* Get the current configuration * Get the current configuration
*/ */
public getConfig(): NupstConfig { public getConfig(): INupstConfig {
return this.config; return this.config;
} }

View File

@ -4,7 +4,7 @@
*/ */
// Re-export all public types // Re-export all public types
export type { UpsStatus, OIDSet, UpsModel, SnmpConfig } from './types.js'; export type { IUpsStatus, IOidSet, TUpsModel, ISnmpConfig } from './types.js';
// Re-export the SNMP manager class // Re-export the SNMP manager class
export { NupstSnmp } from './manager.js'; export { NupstSnmp } from './manager.js';

View File

@ -1,7 +1,7 @@
import { exec } from 'child_process'; import { exec } from 'child_process';
import { promisify } from 'util'; import { promisify } from 'util';
import * as dgram from 'dgram'; import * as dgram from 'dgram';
import type { OIDSet, SnmpConfig, UpsModel, UpsStatus } from './types.js'; import type { IOidSet, ISnmpConfig, TUpsModel, IUpsStatus } from './types.js';
import { UpsOidSets } from './oid-sets.js'; import { UpsOidSets } from './oid-sets.js';
import { SnmpPacketCreator } from './packet-creator.js'; import { SnmpPacketCreator } from './packet-creator.js';
import { SnmpPacketParser } from './packet-parser.js'; import { SnmpPacketParser } from './packet-parser.js';
@ -14,12 +14,12 @@ const execAsync = promisify(exec);
*/ */
export class NupstSnmp { export class NupstSnmp {
// Active OID set // Active OID set
private activeOIDs: OIDSet; private activeOIDs: IOidSet;
// Reference to the parent Nupst instance // Reference to the parent Nupst instance
private nupst: any; // Type 'any' to avoid circular dependency private nupst: any; // Type 'any' to avoid circular dependency
// Default SNMP configuration // Default SNMP configuration
private readonly DEFAULT_CONFIG: SnmpConfig = { private readonly DEFAULT_CONFIG: ISnmpConfig = {
host: '127.0.0.1', // Default to localhost host: '127.0.0.1', // Default to localhost
port: 161, // Default SNMP port port: 161, // Default SNMP port
community: 'public', // Default community string for v1/v2c community: 'public', // Default community string for v1/v2c
@ -64,7 +64,7 @@ export class NupstSnmp {
* Set active OID set based on UPS model * Set active OID set based on UPS model
* @param config SNMP configuration * @param config SNMP configuration
*/ */
private setActiveOIDs(config: SnmpConfig): void { private setActiveOIDs(config: ISnmpConfig): void {
// If custom OIDs are provided, use them // If custom OIDs are provided, use them
if (config.upsModel === 'custom' && config.customOIDs) { if (config.upsModel === 'custom' && config.customOIDs) {
this.activeOIDs = config.customOIDs; this.activeOIDs = config.customOIDs;
@ -206,7 +206,7 @@ export class NupstSnmp {
* @param config SNMP configuration * @param config SNMP configuration
* @returns Promise resolving to the UPS status * @returns Promise resolving to the UPS status
*/ */
public async getUpsStatus(config = this.DEFAULT_CONFIG): Promise<UpsStatus> { public async getUpsStatus(config = this.DEFAULT_CONFIG): Promise<IUpsStatus> {
try { try {
// Set active OID set based on UPS model in config // Set active OID set based on UPS model in config
this.setActiveOIDs(config); this.setActiveOIDs(config);
@ -408,12 +408,12 @@ export class NupstSnmp {
* @param config SNMP configuration * @param config SNMP configuration
* @returns Promise resolving to the discovered engine ID * @returns Promise resolving to the discovered engine ID
*/ */
public async discoverEngineId(config: SnmpConfig): Promise<Buffer> { public async discoverEngineId(config: ISnmpConfig): Promise<Buffer> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const socket = dgram.createSocket('udp4'); const socket = dgram.createSocket('udp4');
// Create a proper discovery message (SNMPv3 with noAuthNoPriv) // Create a proper discovery message (SNMPv3 with noAuthNoPriv)
const discoveryConfig: SnmpConfig = { const discoveryConfig: ISnmpConfig = {
...config, ...config,
securityLevel: 'noAuthNoPriv', securityLevel: 'noAuthNoPriv',
username: '', // Empty username for discovery username: '', // Empty username for discovery

View File

@ -1,4 +1,4 @@
import type { OIDSet, UpsModel } from './types.js'; import type { IOidSet, TUpsModel } from './types.js';
/** /**
* OID sets for different UPS models * OID sets for different UPS models
@ -8,7 +8,7 @@ export class UpsOidSets {
/** /**
* OID sets for different UPS models * OID sets for different UPS models
*/ */
private static readonly UPS_OID_SETS: Record<UpsModel, OIDSet> = { private static readonly UPS_OID_SETS: Record<TUpsModel, IOidSet> = {
// Cyberpower OIDs for RMCARD205 (based on CyberPower_MIB_v2.11) // Cyberpower OIDs for RMCARD205 (based on CyberPower_MIB_v2.11)
cyberpower: { cyberpower: {
POWER_STATUS: '1.3.6.1.4.1.3808.1.1.1.4.1.1.0', // upsBaseOutputStatus (2=online, 3=on battery) POWER_STATUS: '1.3.6.1.4.1.3808.1.1.1.4.1.1.0', // upsBaseOutputStatus (2=online, 3=on battery)
@ -57,7 +57,7 @@ export class UpsOidSets {
* @param model UPS model name * @param model UPS model name
* @returns OID set for the model * @returns OID set for the model
*/ */
public static getOidSet(model: UpsModel): OIDSet { public static getOidSet(model: TUpsModel): IOidSet {
return this.UPS_OID_SETS[model]; return this.UPS_OID_SETS[model];
} }

View File

@ -1,5 +1,5 @@
import * as crypto from 'crypto'; import * as crypto from 'crypto';
import type { SnmpConfig, SnmpV3SecurityParams } from './types.js'; import type { ISnmpConfig, ISnmpV3SecurityParams } from './types.js';
import { SnmpEncoder } from './encoder.js'; import { SnmpEncoder } from './encoder.js';
/** /**
@ -118,7 +118,7 @@ export class SnmpPacketCreator {
*/ */
public static createSnmpV3GetRequest( public static createSnmpV3GetRequest(
oid: string, oid: string,
config: SnmpConfig, config: ISnmpConfig,
engineID: Buffer, engineID: Buffer,
engineBoots: number, engineBoots: number,
engineTime: number, engineTime: number,
@ -145,7 +145,7 @@ export class SnmpPacketCreator {
} }
// Create security parameters // Create security parameters
const securityParams: SnmpV3SecurityParams = { const securityParams: ISnmpV3SecurityParams = {
msgAuthoritativeEngineID: engineID, msgAuthoritativeEngineID: engineID,
msgAuthoritativeEngineBoots: engineBoots, msgAuthoritativeEngineBoots: engineBoots,
msgAuthoritativeEngineTime: engineTime, msgAuthoritativeEngineTime: engineTime,
@ -366,7 +366,7 @@ export class SnmpPacketCreator {
* @param config SNMP configuration * @param config SNMP configuration
* @returns Encrypted data * @returns Encrypted data
*/ */
private static simulateEncryption(data: Buffer, config: SnmpConfig): Buffer { private static simulateEncryption(data: Buffer, config: ISnmpConfig): Buffer {
// This is a placeholder - in a real implementation, you would: // This is a placeholder - in a real implementation, you would:
// 1. Generate an initialization vector (IV) // 1. Generate an initialization vector (IV)
// 2. Use the privacy key derived from the privKey // 2. Use the privacy key derived from the privKey
@ -427,7 +427,7 @@ export class SnmpPacketCreator {
* @param authParamsBuf Authentication parameters buffer * @param authParamsBuf Authentication parameters buffer
* @returns Authenticated message * @returns Authenticated message
*/ */
private static addAuthentication(message: Buffer, config: SnmpConfig, authParamsBuf: Buffer): Buffer { private static addAuthentication(message: Buffer, config: ISnmpConfig, authParamsBuf: Buffer): Buffer {
// In a real implementation, this would: // In a real implementation, this would:
// 1. Zero out the authentication parameters field // 1. Zero out the authentication parameters field
// 2. Calculate HMAC-MD5 or HMAC-SHA1 over the entire message // 2. Calculate HMAC-MD5 or HMAC-SHA1 over the entire message
@ -548,7 +548,7 @@ export class SnmpPacketCreator {
* @param requestID Request ID * @param requestID Request ID
* @returns Discovery message * @returns Discovery message
*/ */
public static createDiscoveryMessage(config: SnmpConfig, requestID: number): Buffer { public static createDiscoveryMessage(config: ISnmpConfig, requestID: number): Buffer {
// Basic SNMPv3 header for discovery // Basic SNMPv3 header for discovery
const msgIdBuf = Buffer.concat([ const msgIdBuf = Buffer.concat([
Buffer.from([0x02, 0x04]), // ASN.1 Integer, length 4 Buffer.from([0x02, 0x04]), // ASN.1 Integer, length 4

View File

@ -1,4 +1,4 @@
import type { SnmpConfig } from './types.js'; import type { ISnmpConfig } from './types.js';
import { SnmpEncoder } from './encoder.js'; import { SnmpEncoder } from './encoder.js';
/** /**
@ -13,7 +13,7 @@ export class SnmpPacketParser {
* @param debug Whether to enable debug output * @param debug Whether to enable debug output
* @returns Parsed value or null if parsing failed * @returns Parsed value or null if parsing failed
*/ */
public static parseSnmpResponse(buffer: Buffer, config: SnmpConfig, debug: boolean = false): any { public static parseSnmpResponse(buffer: Buffer, config: ISnmpConfig, debug: boolean = false): any {
// Check if we have a response packet // Check if we have a response packet
if (buffer[0] !== 0x30) { if (buffer[0] !== 0x30) {
throw new Error('Invalid SNMP response format'); throw new Error('Invalid SNMP response format');

View File

@ -5,7 +5,7 @@
/** /**
* UPS status interface * UPS status interface
*/ */
export interface UpsStatus { export interface IUpsStatus {
/** Current power status */ /** Current power status */
powerStatus: 'online' | 'onBattery' | 'unknown'; powerStatus: 'online' | 'onBattery' | 'unknown';
/** Battery capacity percentage */ /** Battery capacity percentage */
@ -19,7 +19,7 @@ export interface UpsStatus {
/** /**
* SNMP OID Sets for different UPS brands * SNMP OID Sets for different UPS brands
*/ */
export interface OIDSet { export interface IOidSet {
/** OID for power status */ /** OID for power status */
POWER_STATUS: string; POWER_STATUS: string;
/** OID for battery capacity */ /** OID for battery capacity */
@ -31,12 +31,12 @@ export interface OIDSet {
/** /**
* Supported UPS model types * Supported UPS model types
*/ */
export type UpsModel = 'cyberpower' | 'apc' | 'eaton' | 'tripplite' | 'liebert' | 'custom'; export type TUpsModel = 'cyberpower' | 'apc' | 'eaton' | 'tripplite' | 'liebert' | 'custom';
/** /**
* SNMP Configuration interface * SNMP Configuration interface
*/ */
export interface SnmpConfig { export interface ISnmpConfig {
/** SNMP server host */ /** SNMP server host */
host: string; host: string;
/** SNMP server port (default 161) */ /** SNMP server port (default 161) */
@ -66,15 +66,15 @@ export interface SnmpConfig {
// UPS model and custom OIDs // UPS model and custom OIDs
/** UPS model for OID selection */ /** UPS model for OID selection */
upsModel?: UpsModel; upsModel?: TUpsModel;
/** Custom OIDs when using custom UPS model */ /** Custom OIDs when using custom UPS model */
customOIDs?: OIDSet; customOIDs?: IOidSet;
} }
/** /**
* SNMPv3 security parameters * SNMPv3 security parameters
*/ */
export interface SnmpV3SecurityParams { export interface ISnmpV3SecurityParams {
/** Engine ID for the SNMP server */ /** Engine ID for the SNMP server */
msgAuthoritativeEngineID: Buffer; msgAuthoritativeEngineID: Buffer;
/** Engine boots counter */ /** Engine boots counter */