fix(migration): detect flat structure in upsDevices for proper v3→v4 migration
All checks were successful
CI / Type Check & Lint (push) Successful in 6s
CI / Build Test (Current Platform) (push) Successful in 6s
CI / Build All Platforms (push) Successful in 52s
Release / build-and-release (push) Successful in 50s

The previous migration only checked for upsList field, but saveConfig()
strips upsList when saving, creating a race condition. If the daemon
restarted with a partially-migrated config (upsDevices with flat structure),
the migration wouldn't run because it only looked for upsList.

Now shouldRun() also detects:
- upsDevices with flat structure (host at top level, no snmp object)

And migrate() handles both:
- config.upsList (pure v3)
- config.upsDevices with flat structure (partially migrated)

This fixes the "Cannot read properties of undefined (reading 'host')"
error that occurred when configs had upsDevices but flat structure.
This commit is contained in:
2025-10-19 21:41:50 +00:00
parent 23fd5cc5cd
commit a87710144c
2 changed files with 18 additions and 5 deletions

View File

@@ -1,6 +1,6 @@
{
"name": "@serve.zone/nupst",
"version": "4.0.4",
"version": "4.0.5",
"exports": "./mod.ts",
"tasks": {
"dev": "deno run --allow-all mod.ts",

View File

@@ -44,17 +44,30 @@ export class MigrationV3ToV4 extends BaseMigration {
readonly toVersion = '4.0';
async shouldRun(config: any): Promise<boolean> {
// V3 format has upsList instead of upsDevices
return !!config.upsList && !config.upsDevices;
// V3 format has upsList OR has upsDevices with flat structure (host at top level)
if (config.upsList && !config.upsDevices) {
return true; // Classic v3 with upsList
}
// Check if upsDevices exists but has flat structure (v3 format)
if (config.upsDevices && config.upsDevices.length > 0) {
const firstDevice = config.upsDevices[0];
// V3 has host at top level, v4 has it nested in snmp object
return !!firstDevice.host && !firstDevice.snmp;
}
return false;
}
async migrate(config: any): Promise<any> {
logger.info(`${this.getName()}: Migrating v3 config to v4 format...`);
logger.dim(` - Renaming upsList → upsDevices`);
logger.dim(` - Restructuring UPS devices (flat → nested snmp config)`);
// Get devices from either upsList or upsDevices (for partially migrated configs)
const sourceDevices = config.upsList || config.upsDevices;
// Transform each UPS device from v3 flat structure to v4 nested structure
const transformedDevices = config.upsList.map((device: any) => {
const transformedDevices = sourceDevices.map((device: any) => {
// Build SNMP config object
const snmpConfig: any = {
host: device.host,