store connection secrets in system keyring

This commit is contained in:
SuLe
2026-05-02 03:37:52 +08:00
parent 3f71cd6dfa
commit d14329ea15
12 changed files with 935 additions and 36 deletions
+10 -4
View File
@@ -596,7 +596,9 @@ function handleKeydown(e: KeyboardEvent) {
onMounted(() => {
applyTheme();
connectionStore.initFromDisk();
connectionStore.initFromDisk().catch((e: any) => {
toast(t("connection.loadFailed", { message: e?.message || String(e) }), 5000);
});
settingsStore.initAiConfig();
window.addEventListener("keydown", handleKeydown, true);
setupFileDrop();
@@ -671,9 +673,13 @@ async function setupFileDrop() {
username: "",
password: "",
};
connectionStore.addConnection(config);
connectionStore.connect(config);
toast(t("welcome.fileOpened", { name }));
try {
await connectionStore.addConnection(config);
void connectionStore.connect(config);
toast(t("welcome.fileOpened", { name }));
} catch (e: any) {
toast(t("connection.saveFailed", { message: e?.message || String(e) }), 5000);
}
}
});
}
@@ -269,11 +269,11 @@ async function save() {
try {
if (editingId.value) {
const updated: ConnectionConfig = { ...form.value, id: editingId.value };
store.updateConnection(updated);
await store.updateConnection(updated);
store.stopEditing();
} else {
const config: ConnectionConfig = { ...form.value, id: crypto.randomUUID() };
store.addConnection(config);
await store.addConnection(config);
open.value = false;
await nextTick();
emit("connectStarted", config.name);
+7 -3
View File
@@ -220,11 +220,15 @@ function deleteConnection() {
showDeleteConfirm.value = true;
}
function confirmDelete() {
async function confirmDelete() {
const node = props.node;
if (node.connectionId) {
connectionStore.disconnect(node.connectionId);
connectionStore.removeConnection(node.connectionId);
try {
await connectionStore.disconnect(node.connectionId);
await connectionStore.removeConnection(node.connectionId);
} catch (e: any) {
toast(t("connection.saveFailed", { message: e?.message || String(e) }), 5000);
}
}
}
+2
View File
@@ -51,6 +51,8 @@ export default {
connecting: "Connecting to {name}...",
connectSuccess: "Connected to {name}",
connectFailed: "Connection failed: {message}",
saveFailed: "Failed to save connection: {message}",
loadFailed: "Failed to load saved connections: {message}",
sshTunnel: "SSH Tunnel",
sshEnable: "Connect via SSH tunnel",
sshHost: "SSH Host",
+2
View File
@@ -53,6 +53,8 @@ export default {
connecting: "正在连接 {name}...",
connectSuccess: "已连接 {name}",
connectFailed: "连接失败:{message}",
saveFailed: "保存连接失败:{message}",
loadFailed: "加载已保存连接失败:{message}",
sshTunnel: "SSH 隧道",
sshEnable: "通过 SSH 隧道连接",
sshHost: "SSH 主机",
+20 -14
View File
@@ -130,16 +130,18 @@ export const useConnectionStore = defineStore("connection", () => {
}
}
function addConnection(config: ConnectionConfig) {
async function addConnection(config: ConnectionConfig) {
const normalized = normalizeConnection(config);
const existing = connections.value.findIndex((c) => c.id === normalized.id);
const nextConnections = [...connections.value];
if (existing >= 0) {
connections.value[existing] = normalized;
nextConnections[existing] = normalized;
} else {
connections.value.push(normalized);
nextConnections.push(normalized);
}
await persistConnections(nextConnections);
connections.value = nextConnections;
upsertConnectionNode(normalized);
persistConnections();
}
function invalidateCompletionCache(connectionId: string) {
@@ -152,20 +154,25 @@ export const useConnectionStore = defineStore("connection", () => {
);
}
function removeConnection(id: string) {
connections.value = connections.value.filter((c) => c.id !== id);
async function removeConnection(id: string) {
const nextConnections = connections.value.filter((c) => c.id !== id);
await persistConnections(nextConnections);
connections.value = nextConnections;
treeNodes.value = treeNodes.value.filter((n) => n.id !== id);
if (activeConnectionId.value === id) {
activeConnectionId.value = null;
}
invalidateCompletionCache(id);
persistConnections();
}
function updateConnection(config: ConnectionConfig) {
async function updateConnection(config: ConnectionConfig) {
config = normalizeConnection(config);
const idx = connections.value.findIndex((c) => c.id === config.id);
if (idx >= 0) connections.value[idx] = config;
if (idx < 0) return;
const nextConnections = [...connections.value];
nextConnections[idx] = config;
await persistConnections(nextConnections);
connections.value = nextConnections;
const node = findNode(treeNodes.value, config.id);
if (node) {
node.label = config.name;
@@ -174,7 +181,6 @@ export const useConnectionStore = defineStore("connection", () => {
}
connectedIds.value.delete(config.id);
invalidateCompletionCache(config.id);
persistConnections();
}
async function connect(config: ConnectionConfig) {
@@ -561,8 +567,8 @@ export const useConnectionStore = defineStore("connection", () => {
return null;
}
function persistConnections() {
api.saveConnections(connections.value).catch(() => {});
async function persistConnections(nextConnections: ConnectionConfig[] = connections.value) {
await api.saveConnections(nextConnections);
}
async function exportConnectionsToFile() {
@@ -570,7 +576,7 @@ export const useConnectionStore = defineStore("connection", () => {
const { writeTextFile } = await import("@tauri-apps/plugin-fs");
const path = await save({ filters: [{ name: "JSON", extensions: ["json"] }], defaultPath: "dbx-connections.json" });
if (!path) return;
const data = connections.value.map((c) => ({ ...c, password: "", ssh_password: "" }));
const data = connections.value.map((c) => ({ ...c, password: "", ssh_password: "", connection_string: undefined }));
await writeTextFile(path, JSON.stringify(data, null, 2));
}
@@ -585,7 +591,7 @@ export const useConnectionStore = defineStore("connection", () => {
if (!connections.value.find((c) => c.id === config.id)) {
config.id = crypto.randomUUID();
const normalized = normalizeConnection(config);
addConnection(normalized);
await addConnection(normalized);
}
}
}