import { DrawerLogs } from "@/components/shared/drawer-logs"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, } from "@/components/ui/command"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, } from "@/components/ui/dialog"; import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage, } from "@/components/ui/form"; import { Input } from "@/components/ui/input"; import { Popover, PopoverContent, PopoverTrigger, } from "@/components/ui/popover"; import { ScrollArea } from "@/components/ui/scroll-area"; import { cn } from "@/lib/utils"; import { api } from "@/utils/api"; import { zodResolver } from "@hookform/resolvers/zod"; import copy from "copy-to-clipboard"; import { debounce } from "lodash"; import { CheckIcon, ChevronsUpDown, Copy, RotateCcw } from "lucide-react"; import { useState } from "react"; import { useForm } from "react-hook-form"; import { toast } from "sonner"; import { z } from "zod"; import { type LogLine, parseLogs } from "../../docker/logs/utils"; import { formatBytes } from "../../database/backups/restore-backup"; interface Props { id: string; type: "application" | "compose"; serverId?: string; } const RestoreBackupSchema = z.object({ destinationId: z .string({ required_error: "Please select a destination", }) .min(1, { message: "Destination is required", }), backupFile: z .string({ required_error: "Please select a backup file", }) .min(1, { message: "Backup file is required", }), volumeName: z .string({ required_error: "Please enter a volume name", }) .min(1, { message: "Volume name is required", }), }); export const RestoreVolumeBackups = ({ id, type, serverId }: Props) => { const [isOpen, setIsOpen] = useState(false); const [search, setSearch] = useState(""); const [debouncedSearchTerm, setDebouncedSearchTerm] = useState(""); const { data: destinations = [] } = api.destination.all.useQuery(); const form = useForm>({ defaultValues: { destinationId: "", backupFile: "", volumeName: "", }, resolver: zodResolver(RestoreBackupSchema), }); const destinationId = form.watch("destinationId"); const volumeName = form.watch("volumeName"); const backupFile = form.watch("backupFile"); const debouncedSetSearch = debounce((value: string) => { setDebouncedSearchTerm(value); }, 350); const handleSearchChange = (value: string) => { setSearch(value); debouncedSetSearch(value); }; const { data: files = [], isLoading } = api.backup.listBackupFiles.useQuery( { destinationId: destinationId, search: debouncedSearchTerm, serverId: serverId ?? "", }, { enabled: isOpen && !!destinationId, }, ); const [isDrawerOpen, setIsDrawerOpen] = useState(false); const [filteredLogs, setFilteredLogs] = useState([]); const [isDeploying, setIsDeploying] = useState(false); api.volumeBackups.restoreVolumeBackupWithLogs.useSubscription( { id, serviceType: type, serverId, destinationId, volumeName, backupFileName: backupFile, }, { enabled: isDeploying, onData(log) { if (!isDrawerOpen) { setIsDrawerOpen(true); } if (log === "Restore completed successfully!") { setIsDeploying(false); } const parsedLogs = parseLogs(log); setFilteredLogs((prev) => [...prev, ...parsedLogs]); }, onError(error) { console.error("Restore logs error:", error); setIsDeploying(false); }, }, ); const onSubmit = async () => { setIsDeploying(true); }; return ( Restore Volume Backup Select a destination and search for volume backup files
( Destination No destinations found. {destinations.map((destination) => ( { form.setValue( "destinationId", destination.destinationId, ); }} > {destination.name} ))} )} /> ( Search Backup Files {field.value && ( {field.value} { e.stopPropagation(); e.preventDefault(); copy(field.value); toast.success("Backup file copied to clipboard"); }} /> )} {isLoading ? (
Loading backup files...
) : files.length === 0 && search ? (
No backup files found for "{search}"
) : files.length === 0 ? (
No backup files available
) : ( {files?.map((file) => ( { form.setValue("backupFile", file.Path); if (file.IsDir) { setSearch(`${file.Path}/`); setDebouncedSearchTerm(`${file.Path}/`); } else { setSearch(file.Path); setDebouncedSearchTerm(file.Path); } }} >
{file.Path}
Size: {formatBytes(file.Size)} {file.IsDir && ( Directory )} {file.Hashes?.MD5 && ( MD5: {file.Hashes.MD5} )}
))}
)}
)} /> ( Volume Name )} /> { setIsDrawerOpen(false); setFilteredLogs([]); setIsDeploying(false); // refetch(); }} filteredLogs={filteredLogs} />
); };