feat: add secret settings manager and migration for legacy settings

- Implemented SecretSettingsManager to handle secret settings with encryption.
- Added functionality to migrate legacy plaintext settings into encrypted storage.
- Introduced methods for setting, getting, and clearing secret settings.
- Created tests for verifying the migration and canonicalization of secret settings.
- Updated app state to handle service updates via socket communication.
- Added interface for push service updates to manage service state changes.
This commit is contained in:
2026-04-19 01:47:06 +00:00
parent 618d4d674f
commit 061ce7c3f2
17 changed files with 413 additions and 73 deletions
+10 -13
View File
@@ -11,12 +11,13 @@ export class SettingsHandler {
this.registerHandlers();
}
private getSettingsObject(): interfaces.data.ISettings {
private async getSettingsObject(): Promise<interfaces.data.ISettings> {
const db = this.opsServerRef.oneboxRef.database;
const settingsMap = db.getAllSettings(); // Returns Record<string, string>
const cloudflareToken = await db.getSecretSetting('cloudflareToken');
const settingsMap = db.getAllSettings();
return {
cloudflareToken: settingsMap['cloudflareToken'] || settingsMap['cloudflareAPIKey'] || '',
cloudflareToken: cloudflareToken || '',
cloudflareZoneId: settingsMap['cloudflareZoneId'] || '',
autoRenewCerts: settingsMap['autoRenewCerts'] === 'true',
renewalThreshold: parseInt(settingsMap['renewalThreshold'] || '30', 10),
@@ -33,7 +34,7 @@ export class SettingsHandler {
'getSettings',
async (dataArg) => {
await requireAdminIdentity(this.opsServerRef.adminHandler, dataArg);
const settings = this.getSettingsObject();
const settings = await this.getSettingsObject();
return { settings };
},
),
@@ -50,16 +51,15 @@ export class SettingsHandler {
// Store each setting as key-value pair
for (const [key, value] of Object.entries(updates)) {
if (value !== undefined) {
if (key === 'cloudflareToken') {
db.setSetting('cloudflareToken', String(value));
db.setSetting('cloudflareAPIKey', String(value));
if (db.isSecretSettingKey(key)) {
await db.setSecretSetting(key, String(value));
} else {
db.setSetting(key, String(value));
}
}
}
const settings = this.getSettingsObject();
const settings = await this.getSettingsObject();
return { settings };
},
),
@@ -70,8 +70,7 @@ export class SettingsHandler {
'setBackupPassword',
async (dataArg) => {
await requireAdminIdentity(this.opsServerRef.adminHandler, dataArg);
this.opsServerRef.oneboxRef.database.setSetting('backup_encryption_password', dataArg.password);
this.opsServerRef.oneboxRef.database.setSetting('backupPassword', dataArg.password);
await this.opsServerRef.oneboxRef.database.setSecretSetting('backupPassword', dataArg.password);
return { ok: true };
},
),
@@ -82,9 +81,7 @@ export class SettingsHandler {
'getBackupPasswordStatus',
async (dataArg) => {
await requireAdminIdentity(this.opsServerRef.adminHandler, dataArg);
const backupPassword = this.opsServerRef.oneboxRef.database.getSetting('backupPassword')
|| this.opsServerRef.oneboxRef.database.getSetting('backup_encryption_password');
const isConfigured = !!backupPassword;
const isConfigured = await this.opsServerRef.oneboxRef.database.hasSecretSetting('backupPassword');
return { status: { isConfigured } };
},
),