feat: add Clone Database to Docker functionality
Clone any database to a local Docker PostgreSQL container with schema and/or data transfer via pg_dump. Supports three modes: schema only, full clone, and sample data. Includes container lifecycle management (start/stop/remove) in the Admin panel, progress tracking with collapsible process log, and automatic connection creation. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
111
src/hooks/use-docker.ts
Normal file
111
src/hooks/use-docker.ts
Normal file
@@ -0,0 +1,111 @@
|
||||
import { useState, useEffect, useCallback, useRef } from "react";
|
||||
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
import {
|
||||
checkDocker,
|
||||
listTuskContainers,
|
||||
cloneToDocker,
|
||||
startContainer,
|
||||
stopContainer,
|
||||
removeContainer,
|
||||
onCloneProgress,
|
||||
} from "@/lib/tauri";
|
||||
import type { CloneToDockerParams, CloneProgress, CloneResult } from "@/types";
|
||||
|
||||
export function useDockerStatus() {
|
||||
return useQuery({
|
||||
queryKey: ["docker-status"],
|
||||
queryFn: checkDocker,
|
||||
staleTime: 30_000,
|
||||
});
|
||||
}
|
||||
|
||||
export function useTuskContainers() {
|
||||
return useQuery({
|
||||
queryKey: ["tusk-containers"],
|
||||
queryFn: listTuskContainers,
|
||||
refetchInterval: 10_000,
|
||||
});
|
||||
}
|
||||
|
||||
export function useCloneToDocker() {
|
||||
const [progress, setProgress] = useState<CloneProgress | null>(null);
|
||||
const cloneIdRef = useRef<string>("");
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const mutation = useMutation({
|
||||
mutationFn: ({
|
||||
params,
|
||||
cloneId,
|
||||
}: {
|
||||
params: CloneToDockerParams;
|
||||
cloneId: string;
|
||||
}) => {
|
||||
cloneIdRef.current = cloneId;
|
||||
setProgress(null);
|
||||
return cloneToDocker(params, cloneId);
|
||||
},
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ["connections"] });
|
||||
queryClient.invalidateQueries({ queryKey: ["tusk-containers"] });
|
||||
},
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const unlistenPromise = onCloneProgress((p) => {
|
||||
if (p.clone_id === cloneIdRef.current) {
|
||||
setProgress(p);
|
||||
}
|
||||
});
|
||||
return () => {
|
||||
unlistenPromise.then((unlisten) => unlisten());
|
||||
};
|
||||
}, []);
|
||||
|
||||
const mutationRef = useRef(mutation);
|
||||
mutationRef.current = mutation;
|
||||
|
||||
const reset = useCallback(() => {
|
||||
mutationRef.current.reset();
|
||||
setProgress(null);
|
||||
cloneIdRef.current = "";
|
||||
}, []);
|
||||
|
||||
return {
|
||||
clone: mutation.mutate,
|
||||
result: mutation.data as CloneResult | undefined,
|
||||
error: mutation.error ? String(mutation.error) : null,
|
||||
isCloning: mutation.isPending,
|
||||
progress,
|
||||
reset,
|
||||
};
|
||||
}
|
||||
|
||||
export function useStartContainer() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (name: string) => startContainer(name),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ["tusk-containers"] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useStopContainer() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (name: string) => stopContainer(name),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ["tusk-containers"] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useRemoveContainer() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (name: string) => removeContainer(name),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ["tusk-containers"] });
|
||||
},
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user