feat: add backup and restore mechanism to ConfigManager

This commit is contained in:
Misaka_Company
2026-03-03 13:50:36 +08:00
parent c06880e946
commit b5ef38f486
2 changed files with 113 additions and 4 deletions

View File

@@ -145,6 +145,7 @@ function validateEditableFields(settings: Partial<SettingsData>): {
export class ConfigManager {
private static instance: ConfigManager | null = null
private envPath: string
private backupPath: string
private configCache: Map<string, string> = new Map()
private initialized: boolean = false
@@ -153,6 +154,7 @@ export class ConfigManager {
return
}
this.envPath = path.resolve(__dirname, '../../.env')
this.backupPath = path.resolve(__dirname, '../../.env.backup')
this.initialized = true
}
@@ -599,4 +601,39 @@ export class ConfigManager {
public getDefaultSettings(): SettingsData {
return DEFAULT_SETTINGS
}
/**
* Backup current .env file
*/
private async backupEnvFile(): Promise<boolean> {
try {
if (fs.existsSync(this.envPath)) {
fs.copyFileSync(this.envPath, this.backupPath)
console.log('[ConfigManager] Backup created', this.backupPath)
return true
}
return false
} catch (error) {
console.error('[ConfigManager] Failed to backup .env file:', error)
return false
}
}
/**
* Restore .env file from backup
*/
private async restoreBackup(): Promise<boolean> {
try {
if (fs.existsSync(this.backupPath)) {
fs.copyFileSync(this.backupPath, this.envPath)
await this.loadEnvFile()
console.log('[ConfigManager] Restored from backup')
return true
}
return false
} catch (error) {
console.error('[ConfigManager] Failed to restore backup:', error)
return false
}
}
}

View File

@@ -9,11 +9,38 @@ describe('ConfigManager - deep merge utilities', () => {
// Setup initial state
const initial: SettingsData = {
erp: { url: 'http://old.com', username: 'user1', password: 'pass1', headless: true, ignoreHttpsErrors: true, autoCloseBrowser: true },
database: { dbType: 'mysql', server: '', mysqlHost: 'localhost', mysqlPort: 3306, database: 'db', username: 'user', password: '' },
erp: {
url: 'http://old.com',
username: 'user1',
password: 'pass1',
headless: true,
ignoreHttpsErrors: true,
autoCloseBrowser: true
},
database: {
dbType: 'mysql',
server: '',
mysqlHost: 'localhost',
mysqlPort: 3306,
database: 'db',
username: 'user',
password: ''
},
paths: { dataDir: '/data', defaultOutput: 'out.xlsx', validationOutput: 'validation.xlsx' },
extraction: { batchSize: 100, verbose: true, autoConvert: true, mergeBatches: true, enableDbPersistence: true },
validation: { dataSource: 'database_full', batchSize: 2000, matchMode: 'substring', enableCrud: false, defaultManager: '' },
extraction: {
batchSize: 100,
verbose: true,
autoConvert: true,
mergeBatches: true,
enableDbPersistence: true
},
validation: {
dataSource: 'database_full',
batchSize: 2000,
matchMode: 'substring',
enableCrud: false,
defaultManager: ''
},
ui: { fontFamily: 'Arial', fontSize: 12, productionIdInputWidth: 20 },
execution: { dryRun: false }
}
@@ -41,3 +68,48 @@ describe('ConfigManager - deep merge utilities', () => {
expect(current.paths.dataDir).toBe('/data')
})
})
describe('ConfigManager - backup and restore', () => {
it('should create backup before saving', async () => {
const manager = ConfigManager.getInstance()
await manager.initialize()
const backupSuccess = await manager['backupEnvFile']()
expect(backupSuccess).toBe(true)
// Check backup file exists (in same location as .env file)
const fs = await import('fs')
const path = await import('path')
const backupPath = path.resolve('src/main/.env.backup')
expect(fs.existsSync(backupPath)).toBe(true)
})
it('should restore from backup when save fails', async () => {
const manager = ConfigManager.getInstance()
await manager.initialize()
// Create initial state
const initial = manager.getAllSettings()
const originalUrl = initial.erp.url
// Mock fs.writeFileSync to fail
const fs = await import('fs')
const writeFileSyncSpy = vi.spyOn(fs, 'writeFileSync').mockImplementation(() => {
throw new Error('Disk full')
})
const result = await manager.savePartialSettings({
erp: { url: 'http://should-not-save.com' }
})
expect(result.success).toBe(false)
// Restore should have happened
const current = manager.getAllSettings()
expect(current.erp.url).toBe(originalUrl)
writeFileSyncSpy.mockRestore()
})
})