feat(migrations): add migration system for v3→v4 config format

- Create abstract BaseMigration class with order, shouldRun(), migrate()
- Add MigrationRunner to execute migrations in order
- Add Migration v1→v2 (snmp → upsDevices)
- Add Migration v3→v4 (upsList → upsDevices)
- Update INupstConfig with version field
- Update loadConfig() to run migrations automatically
- Update saveConfig() to ensure version field and remove legacy fields
- Update Docker test scripts to use real UPS data from .nogit/env.json
- Remove colors.bright (not available in @std/fmt/colors)

Config migrations allow users to jump versions (e.g., v1→v4) with all
intermediate migrations running automatically. Version field tracks
config format for future migrations.
This commit is contained in:
2025-10-19 20:41:09 +00:00
parent 49f7a7da8b
commit 016681b77b
9 changed files with 310 additions and 64 deletions

View File

@@ -0,0 +1,56 @@
import { BaseMigration } from './base-migration.ts';
import { logger } from '../logger.ts';
/**
* Migration from v1 (single SNMP config) to v2 (upsDevices array)
*
* Detects old format:
* {
* snmp: { ... },
* thresholds: { ... },
* checkInterval: 30000
* }
*
* Converts to:
* {
* version: "2.0",
* upsDevices: [{ id: "default", name: "Default UPS", snmp: ..., thresholds: ... }],
* groups: [],
* checkInterval: 30000
* }
*/
export class MigrationV1ToV2 extends BaseMigration {
readonly order = 2;
readonly fromVersion = '1.x';
readonly toVersion = '2.0';
async shouldRun(config: any): Promise<boolean> {
// V1 format has snmp field directly at root, no upsDevices or upsList
return !!config.snmp && !config.upsDevices && !config.upsList;
}
async migrate(config: any): Promise<any> {
logger.info(`${this.getName()}: Converting single SNMP config to multi-UPS format...`);
const migrated = {
version: this.toVersion,
upsDevices: [
{
id: 'default',
name: 'Default UPS',
snmp: config.snmp,
thresholds: config.thresholds || {
battery: 60,
runtime: 20,
},
groups: [],
},
],
groups: [],
checkInterval: config.checkInterval || 30000,
};
logger.success(`${this.getName()}: Migration complete`);
return migrated;
}
}