1
0
forked from boos/BoosAPI

feat(settings): add validation for SQLite connection strings and improve error messages

This commit is contained in:
cita
2026-03-05 19:37:09 +08:00
parent b122893796
commit 0d5e56f478
5 changed files with 147 additions and 83 deletions
@@ -106,4 +106,20 @@ describe('settings database runtime config api', () => {
expect(body.success).toBe(false);
expect(body.message || '').toContain('postgres');
});
it('rejects sqlite dialect with network url connection string', async () => {
const response = await app.inject({
method: 'PUT',
url: '/api/settings/database/runtime',
payload: {
dialect: 'sqlite',
connectionString: 'postgres://metapi:secret@127.0.0.1:5432/postgres',
},
});
expect(response.statusCode).toBe(400);
const body = response.json() as { success?: boolean; message?: string };
expect(body.success).toBe(false);
expect(body.message || '').toContain('SQLite');
});
});
@@ -130,14 +130,25 @@ function assertDialectUrl(dialect: MigrationDialect, connectionString: string):
function normalizeSqliteTarget(raw: string): string {
if (!raw) throw new Error('SQLite 目标路径不能为空');
if (raw.startsWith('file://')) {
const parsed = new URL(raw);
const trimmed = raw.trim();
if (!trimmed) throw new Error('SQLite 目标路径不能为空');
if (trimmed === ':memory:') return trimmed;
const lower = trimmed.toLowerCase();
if (lower.startsWith('file://')) {
const parsed = new URL(trimmed);
return decodeURIComponent(parsed.pathname);
}
if (raw.startsWith('sqlite://')) {
return raw.slice('sqlite://'.length).trim();
if (lower.startsWith('sqlite://')) {
return trimmed.slice('sqlite://'.length).trim();
}
return raw;
// Guard against accidentally saving a network URL under sqlite dialect.
// This would be treated as a local sqlite file path and produce a broken runtime DB.
if (/^[a-z][a-z0-9+.-]*:\/\//i.test(trimmed)) {
throw new Error('SQLite 连接串不能是网络 URL,请先选择 MySQL 或 PostgreSQL');
}
return trimmed;
}
export function normalizeMigrationInput(input: DatabaseMigrationInput): NormalizedDatabaseMigrationInput {