"use client"; import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; import { useState } from "react"; import { toast } from "sonner"; import { Button } from "@/components/ui/button"; import { Alert, AlertDescription } from "@/components/ui/alert"; import { timeAgo } from "@/lib/time"; import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from "@/components/ui/card"; type Backup = { name: string; size: string; createdAt: string; }; export function BackupManager() { const queryClient = useQueryClient(); const [confirmRestore, setConfirmRestore] = useState(null); const [confirmDelete, setConfirmDelete] = useState(null); const { data: backups = [] } = useQuery({ queryKey: ["backups"], queryFn: () => fetch("/api/backups").then((r) => r.json()), staleTime: 30_000, }); const createBackup = useMutation({ mutationFn: async () => { const res = await fetch("/api/backups", { method: "POST" }); return res.json(); }, onSuccess: (data) => { toast.success(data.message || "Backup created"); queryClient.invalidateQueries({ queryKey: ["backups"] }); }, onError: (err) => { toast.error("Backup failed", { description: err.message }); }, }); const restore = useMutation({ mutationFn: async (name: string) => { setConfirmRestore(null); const res = await fetch("/api/backups/restore", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ name }), }); return res.json(); }, onSuccess: (data) => { (data.success ? toast.success : toast.error)(data.message); queryClient.invalidateQueries({ queryKey: ["status"] }); }, onError: (err) => { toast.error("Restore failed", { description: err.message }); }, }); const deleteBackup = useMutation({ mutationFn: async (name: string) => { await fetch("/api/backups", { method: "DELETE", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ name }), }); }, onSuccess: () => { toast.success("Backup deleted"); queryClient.invalidateQueries({ queryKey: ["backups"] }); }, onError: (err) => { toast.error("Delete failed", { description: err.message }); }, }); const isBusy = createBackup.isPending || restore.isPending; return (
World Backups Auto-backup every 6 hours. {backups.length} backup(s) stored.
{restore.isPending && ( Restoring world backup... Server will restart. This may take a minute. )} {backups.length === 0 ? (

No backups yet. Click "Backup Now" to create one.

) : (
    {backups.map((b) => (
  • {b.name}

    {timeAgo(b.createdAt)} — {b.size}

    {confirmRestore === b.name ? ( <> ) : confirmDelete === b.name ? ( <> ) : ( <> )}
  • ))}
)}
); }