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
+35
View File
@@ -26,6 +26,7 @@ import type { TBindValue } from './types.ts';
import { logger } from '../logging.ts';
import { getErrorMessage } from '../utils/error.ts';
import { MigrationRunner } from './migrations/index.ts';
import { SecretSettingsManager } from './secret-settings.ts';
// Import repositories
import {
@@ -50,6 +51,7 @@ export class OneboxDatabase {
private metricsRepo!: MetricsRepository;
private platformRepo!: PlatformRepository;
private backupRepo!: BackupRepository;
public secretSettings!: SecretSettingsManager;
constructor(dbPath = './.nogit/onebox.db') {
this.dbPath = dbPath;
@@ -84,6 +86,7 @@ export class OneboxDatabase {
this.metricsRepo = new MetricsRepository(queryFn);
this.platformRepo = new PlatformRepository(queryFn);
this.backupRepo = new BackupRepository(queryFn);
this.secretSettings = new SecretSettingsManager(this.authRepo);
} catch (error) {
logger.error(`Failed to initialize database: ${getErrorMessage(error)}`);
throw error;
@@ -229,6 +232,14 @@ export class OneboxDatabase {
)
`);
this.query(`
CREATE TABLE IF NOT EXISTS secret_settings (
key TEXT PRIMARY KEY,
value TEXT NOT NULL,
updated_at INTEGER NOT NULL
)
`);
// Version table for migrations
this.query(`
CREATE TABLE IF NOT EXISTS migrations (
@@ -333,10 +344,34 @@ export class OneboxDatabase {
this.authRepo.setSetting(key, value);
}
deleteSetting(key: string): void {
this.authRepo.deleteSetting(key);
}
getAllSettings(): Record<string, string> {
return this.authRepo.getAllSettings();
}
async getSecretSetting(key: string): Promise<string | null> {
return await this.secretSettings.get(key);
}
async setSecretSetting(key: string, value: string | null): Promise<void> {
await this.secretSettings.set(key, value);
}
async hasSecretSetting(key: string): Promise<boolean> {
return await this.secretSettings.has(key);
}
isSecretSettingKey(key: string): boolean {
return this.secretSettings.isSecretKey(key);
}
getCanonicalSecretSettingKeys(): string[] {
return this.secretSettings.getCanonicalKeys();
}
// ============ Users CRUD (delegated to repository) ============
async createUser(user: Omit<IUser, 'id'>): Promise<IUser> {