mirror of
https://github.com/t8y2/dbx.git
synced 2026-10-31 16:24:33 +00:00
feat: export entire database as SQL file
Add a new "Export Database" feature accessible from the sidebar right-click menu on database/schema nodes. Generates a .sql file containing CREATE TABLE DDL, batched INSERT statements, and view/procedure/function source code. Backend (database_export.rs): - Stream-writes SQL to file with configurable batch size - Supports all SQL database types via existing DDL/INSERT generators - Progress events with cancellation support - Graceful error handling (continues on per-table failures) Frontend (DatabaseExportDialog.vue): - Connection/database/schema selector - Checkboxes: structure, data, objects - Real-time progress with current table and row count - Cancel button Wired up through Tauri commands, Web SSE routes, and API layer.
This commit is contained in:
@@ -98,6 +98,10 @@ export const previewTableImportFile = forward("previewTableImportFile");
|
||||
export const importTableFile = forward("importTableFile");
|
||||
export const cancelTableImport = forward("cancelTableImport");
|
||||
|
||||
// Database Export
|
||||
export const exportDatabaseSql = forward("exportDatabaseSql");
|
||||
export const cancelDatabaseExport = forward("cancelDatabaseExport");
|
||||
|
||||
// Redis
|
||||
export const redisListDatabases = forward("redisListDatabases");
|
||||
export const redisScanKeys = forward("redisScanKeys");
|
||||
@@ -172,4 +176,6 @@ export type {
|
||||
TableImportRequest,
|
||||
TableImportSummary,
|
||||
TableImportProgress,
|
||||
DatabaseExportRequest,
|
||||
ExportProgress,
|
||||
} from "./tauri";
|
||||
|
||||
@@ -38,6 +38,8 @@ import type {
|
||||
TableImportRequest,
|
||||
TableImportSummary,
|
||||
TableImportProgress,
|
||||
DatabaseExportRequest,
|
||||
ExportProgress,
|
||||
} from "./tauri";
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -497,6 +499,44 @@ export async function cancelTableImport(importId: string): Promise<boolean> {
|
||||
return post("/api/import/cancel", { importId });
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Database Export
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export async function exportDatabaseSql(
|
||||
request: DatabaseExportRequest,
|
||||
onProgress: (progress: ExportProgress) => void,
|
||||
): Promise<void> {
|
||||
// 1. POST to start the export
|
||||
const res = await fetch("/api/export/database", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ request }),
|
||||
});
|
||||
if (!res.ok) throw new Error(await res.text());
|
||||
|
||||
// 2. SSE to listen for progress
|
||||
return new Promise((resolve, reject) => {
|
||||
const es = new EventSource(`/api/export/database/progress/${request.exportId}`);
|
||||
es.onmessage = (e) => {
|
||||
const progress: ExportProgress = JSON.parse(e.data);
|
||||
onProgress(progress);
|
||||
if (progress.status === "Done" || progress.status === "Error" || progress.status === "Cancelled") {
|
||||
es.close();
|
||||
resolve();
|
||||
}
|
||||
};
|
||||
es.onerror = () => {
|
||||
es.close();
|
||||
reject(new Error("Export SSE connection failed"));
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
export async function cancelDatabaseExport(exportId: string): Promise<void> {
|
||||
await post("/api/export/database/cancel", { exportId });
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Redis
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
@@ -744,3 +744,51 @@ export async function importTableFile(
|
||||
export async function cancelTableImport(importId: string): Promise<boolean> {
|
||||
return invoke("cancel_table_import", { importId });
|
||||
}
|
||||
|
||||
// --- Database Export ---
|
||||
export interface DatabaseExportRequest {
|
||||
exportId: string;
|
||||
connectionId: string;
|
||||
database: string;
|
||||
schema: string;
|
||||
filePath: string;
|
||||
includeStructure: boolean;
|
||||
includeData: boolean;
|
||||
includeObjects: boolean;
|
||||
batchSize: number;
|
||||
}
|
||||
|
||||
export interface ExportProgress {
|
||||
exportId: string;
|
||||
currentObject: string;
|
||||
objectIndex: number;
|
||||
totalObjects: number;
|
||||
rowsExported: number;
|
||||
totalRows: number | null;
|
||||
status: "Running" | "Done" | "Error" | "Cancelled";
|
||||
error: string | null;
|
||||
}
|
||||
|
||||
export async function exportDatabaseSql(
|
||||
request: DatabaseExportRequest,
|
||||
onProgress: (progress: ExportProgress) => void,
|
||||
): Promise<void> {
|
||||
const unlisten: UnlistenFn = await listen<ExportProgress>("database-export-progress", (event) => {
|
||||
if (event.payload.exportId === request.exportId) {
|
||||
onProgress(event.payload);
|
||||
if (event.payload.status === "Done" || event.payload.status === "Error" || event.payload.status === "Cancelled") {
|
||||
unlisten();
|
||||
}
|
||||
}
|
||||
});
|
||||
try {
|
||||
await invoke("export_database_sql", { request });
|
||||
} catch (e) {
|
||||
unlisten();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
export async function cancelDatabaseExport(exportId: string): Promise<void> {
|
||||
await invoke("cancel_database_export", { exportId });
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user