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:
t8y2
2026-05-11 00:18:09 +08:00
parent 741783445d
commit f456ddd979
13 changed files with 230 additions and 87 deletions
+50 -35
View File
@@ -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 };
}