1
0
forked from boos/BoosAPI

fix: exclude runtime db config from backup and migration (#260)

This commit is contained in:
Cita
2026-03-23 20:05:00 +08:00
committed by GitHub
parent 1449f08e6c
commit c838e3263b
4 changed files with 96 additions and 0 deletions
+46
View File
@@ -290,6 +290,52 @@ describe('backupService', () => {
]);
});
it('does not export runtime database config in preferences backups', async () => {
await db.insert(schema.settings).values([
{ key: 'db_type', value: JSON.stringify('postgres') },
{ key: 'db_url', value: JSON.stringify('postgres://metapi:secret@db.example.com:5432/metapi') },
{ key: 'db_ssl', value: JSON.stringify(true) },
{ key: 'routing_fallback_unit_cost', value: JSON.stringify(0.25) },
]).run();
const exported = await backupService.exportBackup('preferences') as any;
const exportedSettingKeys = exported.preferences.settings.map((row: { key: string }) => row.key);
expect(exportedSettingKeys).toContain('routing_fallback_unit_cost');
expect(exportedSettingKeys).not.toContain('db_type');
expect(exportedSettingKeys).not.toContain('db_url');
expect(exportedSettingKeys).not.toContain('db_ssl');
});
it('ignores imported runtime database config settings', async () => {
const result = await backupService.importBackup({
version: '2.1',
timestamp: Date.now(),
type: 'preferences',
preferences: {
settings: [
{ key: 'db_type', value: 'postgres' },
{ key: 'db_url', value: 'postgres://metapi:secret@db.example.com:5432/metapi' },
{ key: 'db_ssl', value: true },
{ key: 'routing_fallback_unit_cost', value: 0.25 },
],
},
});
expect(result.sections.preferences).toBe(true);
expect(result.appliedSettings).toEqual([
{ key: 'routing_fallback_unit_cost', value: 0.25 },
]);
const settingsRows = await db.select().from(schema.settings).all();
const savedKeys = settingsRows.map((row) => row.key);
expect(savedKeys).toContain('routing_fallback_unit_cost');
expect(savedKeys).not.toContain('db_type');
expect(savedKeys).not.toContain('db_url');
expect(savedKeys).not.toContain('db_ssl');
});
it('preserves local logs and runtime stats when importing account backups', async () => {
const exportedAt = '2026-03-20T09:00:00.000Z';
const localRuntimeAt = '2026-03-21T10:30:00.000Z';
+4
View File
@@ -228,6 +228,10 @@ interface BackupImportResult {
const EXCLUDED_SETTING_KEYS = new Set<string>([
// Keep current admin login credential unchanged to avoid accidental lock-out.
'auth_token',
// Runtime database selection is environment-bound and must not be propagated by backups.
'db_type',
'db_url',
'db_ssl',
]);
const BACKUP_WEBDAV_CONFIG_SETTING_KEY = 'backup_webdav_config_v1';
const BACKUP_WEBDAV_STATE_SETTING_KEY = 'backup_webdav_state_v1';
@@ -465,4 +465,46 @@ describe('databaseMigrationService', () => {
vi.resetModules();
}
});
it('excludes runtime database config settings from migration statements', () => {
const statements = __databaseMigrationServiceTestUtils.buildStatements({
version: 'test',
timestamp: Date.now(),
accounts: {
sites: [],
siteAnnouncements: [],
siteDisabledModels: [],
accounts: [],
accountTokens: [],
checkinLogs: [],
modelAvailability: [],
tokenModelAvailability: [],
tokenRoutes: [],
routeChannels: [],
routeGroupSources: [],
proxyLogs: [],
proxyVideoTasks: [],
proxyFiles: [],
downstreamApiKeys: [],
events: [],
},
preferences: {
settings: [
{ key: 'db_type', value: 'sqlite' },
{ key: 'db_url', value: '/app/data/hub.db' },
{ key: 'db_ssl', value: false },
{ key: 'routing_fallback_unit_cost', value: 0.25 },
],
},
} as any);
const migratedSettingKeys = statements
.filter((statement) => statement.table === 'settings')
.map((statement) => statement.values[0]);
expect(migratedSettingKeys).toContain('routing_fallback_unit_cost');
expect(migratedSettingKeys).not.toContain('db_type');
expect(migratedSettingKeys).not.toContain('db_url');
expect(migratedSettingKeys).not.toContain('db_ssl');
});
});
@@ -85,6 +85,7 @@ interface InsertStatement {
}
const DIALECTS: MigrationDialect[] = ['sqlite', 'mysql', 'postgres'];
const RUNTIME_DATABASE_SETTING_KEYS = new Set(['db_type', 'db_url', 'db_ssl']);
function asString(value: unknown): string {
return typeof value === 'string' ? value.trim() : '';
@@ -628,6 +629,9 @@ function buildStatements(snapshot: BackupSnapshot): InsertStatement[] {
}
for (const row of snapshot.preferences.settings) {
if (RUNTIME_DATABASE_SETTING_KEYS.has(row.key)) {
continue;
}
statements.push({
table: 'settings',
columns: ['key', 'value'],