mirror of
https://github.com/t8y2/dbx.git
synced 2026-10-31 00:22:30 +00:00
perf: full-stack performance and security hardening
Frontend performance: - Debounce DataGrid search (150ms) and replace O(n) row wrapping with index array - Replace QueryStore deep watcher with targeted field-level persistence snapshot - Extract shared nodeConfig computed in TreeItem (eliminates 15x redundant getConfig calls) - Clear tab result data on close to free memory - Add LRU eviction for completion caches (50 entry cap) - Replace savedSql version string join with numeric counter Memory leak fixes: - Store Tauri event unlisten handles and clean up on unmount - Clean up DataGrid search timer on unmount Backend security & resource management: - Add 50MB request body size limit - Reject file uploads >100MB and SQL files >200MB - Clean up SSE channels after transfer/import completes - Recover from poisoned mutex instead of panicking - Delete temporary upload files after preview - Validate SQL file paths to prevent directory traversal
This commit is contained in:
@@ -5,6 +5,7 @@ import type { NavigationTarget } from "@/composables/useNavigationTargets";
|
||||
export function useTauriEvents(deps: { openTableTarget: (target: NavigationTarget) => Promise<void> }) {
|
||||
const connectionStore = useConnectionStore();
|
||||
const queryStore = useQueryStore();
|
||||
const unlistenHandles: Array<() => void> = [];
|
||||
|
||||
function setupTauriListeners() {
|
||||
import("@tauri-apps/api/event")
|
||||
@@ -12,53 +13,67 @@ export function useTauriEvents(deps: { openTableTarget: (target: NavigationTarge
|
||||
listen<{ connection_id: string; database: string; schema?: string; table: string }>(
|
||||
"mcp-open-table",
|
||||
async (event) => {
|
||||
const { connection_id, database, schema, table } = event.payload;
|
||||
try {
|
||||
const { connection_id, database, schema, table } = event.payload;
|
||||
if (!connectionStore.connections.length) await connectionStore.initFromDisk();
|
||||
const config = connectionStore.getConfig(connection_id);
|
||||
if (!config) return;
|
||||
connectionStore.activeConnectionId = connection_id;
|
||||
await connectionStore.ensureConnected(connection_id);
|
||||
if (config.db_type === "redis") {
|
||||
queryStore.createTab(connection_id, database || "0", `db${database || "0"}`, "redis");
|
||||
} else if (config.db_type === "mongodb") {
|
||||
queryStore.createTab(connection_id, database, table, "mongo");
|
||||
} else {
|
||||
deps.openTableTarget({ connectionId: connection_id, database, schema, tableName: table });
|
||||
}
|
||||
import("@tauri-apps/api/window").then(({ getCurrentWindow }) =>
|
||||
getCurrentWindow()
|
||||
.setFocus()
|
||||
.catch(() => {}),
|
||||
);
|
||||
} catch (e) {
|
||||
console.error("[DBX] mcp-open-table error:", e);
|
||||
}
|
||||
},
|
||||
).then((unlisten) => unlistenHandles.push(unlisten));
|
||||
|
||||
listen("mcp-reload-connections", async () => {
|
||||
try {
|
||||
await connectionStore.initFromDisk();
|
||||
} catch (e) {
|
||||
console.error("[DBX] mcp-reload-connections error:", e);
|
||||
}
|
||||
}).then((unlisten) => unlistenHandles.push(unlisten));
|
||||
|
||||
listen<{ connection_id: string; database: string; sql: string }>("mcp-execute-query", async (event) => {
|
||||
try {
|
||||
const { connection_id, database, sql } = event.payload;
|
||||
if (!connectionStore.connections.length) await connectionStore.initFromDisk();
|
||||
const config = connectionStore.getConfig(connection_id);
|
||||
if (!config) return;
|
||||
connectionStore.activeConnectionId = connection_id;
|
||||
await connectionStore.ensureConnected(connection_id);
|
||||
if (config.db_type === "redis") {
|
||||
queryStore.createTab(connection_id, database || "0", `db${database || "0"}`, "redis");
|
||||
} else if (config.db_type === "mongodb") {
|
||||
queryStore.createTab(connection_id, database, table, "mongo");
|
||||
} else {
|
||||
deps.openTableTarget({
|
||||
connectionId: connection_id,
|
||||
database,
|
||||
schema,
|
||||
tableName: table,
|
||||
});
|
||||
}
|
||||
const tabId = queryStore.createTab(connection_id, database, undefined, "query");
|
||||
queryStore.updateSql(tabId, sql);
|
||||
await queryStore.executeTabSql(tabId, sql);
|
||||
import("@tauri-apps/api/window").then(({ getCurrentWindow }) =>
|
||||
getCurrentWindow()
|
||||
.setFocus()
|
||||
.catch(() => {}),
|
||||
);
|
||||
},
|
||||
);
|
||||
listen("mcp-reload-connections", async () => {
|
||||
await connectionStore.initFromDisk();
|
||||
});
|
||||
listen<{ connection_id: string; database: string; sql: string }>("mcp-execute-query", async (event) => {
|
||||
const { connection_id, database, sql } = event.payload;
|
||||
if (!connectionStore.connections.length) await connectionStore.initFromDisk();
|
||||
const config = connectionStore.getConfig(connection_id);
|
||||
if (!config) return;
|
||||
connectionStore.activeConnectionId = connection_id;
|
||||
await connectionStore.ensureConnected(connection_id);
|
||||
const tabId = queryStore.createTab(connection_id, database, undefined, "query");
|
||||
queryStore.updateSql(tabId, sql);
|
||||
await queryStore.executeTabSql(tabId, sql);
|
||||
import("@tauri-apps/api/window").then(({ getCurrentWindow }) =>
|
||||
getCurrentWindow()
|
||||
.setFocus()
|
||||
.catch(() => {}),
|
||||
);
|
||||
});
|
||||
} catch (e) {
|
||||
console.error("[DBX] mcp-execute-query error:", e);
|
||||
}
|
||||
}).then((unlisten) => unlistenHandles.push(unlisten));
|
||||
})
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
return { setupTauriListeners };
|
||||
function cleanupTauriListeners() {
|
||||
unlistenHandles.forEach((unlisten) => unlisten());
|
||||
unlistenHandles.length = 0;
|
||||
}
|
||||
|
||||
return { setupTauriListeners, cleanupTauriListeners };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user