feat(sqlite): support RC4 Legacy databases

This commit is contained in:
zipg authored and GitHub committed 2026-08-25 23:37:52 +08:00
1 parent 0dc1f0c14c
commit 1ed20e424e
19 files changed
+187 -49

No files matched your search

@@ -297,6 +297,35 @@ describe("connectionStore save_password opt-out", () => {
expect(store.getConfig("mysql-1")?.password).toBe("typed-pw");
});
it("prompts and retries when an encrypted SQLite file is opened without a password", async () => {
const connectDb = vi.fn().mockRejectedValueOnce(new Error("Selected file is not a valid SQLite database file.")).mockResolvedValueOnce("sqlite-1");
installApiMocks({ connectDb });
installPasswordPromptMock();
requestPassword.mockResolvedValue({ password: "123456", rememberPassword: true });
const { useConnectionStore } = await import("@/stores/connectionStore");
const store = useConnectionStore();
const connection = postgresConnection({
id: "sqlite-1",
name: "Encrypted SQLite",
db_type: "sqlite",
host: "/tmp/encrypted.db",
port: 0,
username: "",
database: undefined,
save_password: true,
password: "",
});
store.connections = [connection];
await store.connect(connection);
expect(requestPassword).toHaveBeenCalledWith({ connectionId: "sqlite-1", connectionName: "Encrypted SQLite" });
expect(connectDb).toHaveBeenCalledTimes(2);
expect(connectDb).toHaveBeenNthCalledWith(1, expect.objectContaining({ password: "" }), expect.any(Number));
expect(connectDb).toHaveBeenNthCalledWith(2, expect.objectContaining({ password: "123456" }), expect.any(Number));
expect(store.getConfig("sqlite-1")?.password).toBe("123456");
});
it("does not persist a recovered password after the connection config changes", async () => {
const connected = deferred<string>();
const connectDb = vi
+6 -5
View File
@@ -141,7 +141,7 @@ import { REDIS_SCAN_PAGE_SIZE_DEFAULT } from "@/lib/redis/redisKeyPattern";
import { normalizeRedisDatabaseAliases, redisDatabaseAlias, redisDatabaseLabel } from "@/lib/redis/redisDatabaseAlias";
import { normalizeRedisKeyTemplates } from "@/lib/redis/redisKeyTemplates";
import { appendAgentDriverUpdateHint, hasAgentDriverUpdate, hasInstalledAgentVersion, type AgentDriverInstallState } from "@/lib/connection/agentDriverInstallHint";
import { appendConnectionErrorHints, isMysqlMissingPasswordFailure } from "@/lib/connection/connectionErrorHints";
import { appendConnectionErrorHints, isMysqlMissingPasswordFailure, isSqliteMissingEncryptionPasswordFailure } from "@/lib/connection/connectionErrorHints";
import { connectionNeedsPasswordPrompt } from "@/lib/connection/connectionPassword";
import { appendVisibleDatabaseSelection } from "@/lib/connection/connectionVisibleDatabases";
import { buildXuguTypeMemberNodes, isXuguTypeMemberContainer } from "@/lib/sidebar/xuguTypeMembers";
@@ -3630,12 +3630,13 @@ export const useConnectionStore = defineStore("connection", () => {
rebuildTreeNodes();
}
async function connectDbWithMissingPasswordRetry(config: ConnectionConfig, localAttempt: number): Promise<{ config: ConnectionConfig; id: string; rememberPassword: boolean }> {
async function connectDbWithPasswordRetry(config: ConnectionConfig, localAttempt: number): Promise<{ config: ConnectionConfig; id: string; rememberPassword: boolean }> {
try {
const id = await withConnectionAttemptTimeout(api.connectDb(config, localAttempt), config);
return { config, id, rememberPassword: false };
} catch (error) {
if (!isMysqlMissingPasswordFailure(config, connectionErrorMessage(error))) throw error;
const message = connectionErrorMessage(error);
if (!isMysqlMissingPasswordFailure(config, message) && !isSqliteMissingEncryptionPasswordFailure(config, message)) throw error;
const prompted = await ensureConnectionPassword(config, true);
config = prompted.config;
ensureLocalConnectionAttemptActive(config.id, localAttempt);
@@ -3661,7 +3662,7 @@ export const useConnectionStore = defineStore("connection", () => {
await ensureSqlServerLegacyCompatibilityComponentInstalled(config);
}
ensureLocalConnectionAttemptActive(config.id, localAttempt);
const connection = await connectDbWithMissingPasswordRetry(config, localAttempt);
const connection = await connectDbWithPasswordRetry(config, localAttempt);
config = connection.config;
rememberPassword ||= connection.rememberPassword;
const id = connection.id;
@@ -3904,7 +3905,7 @@ export const useConnectionStore = defineStore("connection", () => {
await ensureSqlServerLegacyCompatibilityComponentInstalled(config);
}
ensureLocalConnectionAttemptActive(connectionId, localAttempt);
const connection = await connectDbWithMissingPasswordRetry(config, localAttempt);
const connection = await connectDbWithPasswordRetry(config, localAttempt);
config = connection.config;
rememberPassword ||= connection.rememberPassword;
const id = connection.id;