"use client"; import { useState } from "react"; import { Archive, Download, Trash2, History, PlayCircle } from "lucide-react"; import { api, useApi, toast } from "@/lib/api"; import { Card, Button, Input, EmptyState, Modal } from "@/components/ui"; import { fmtBytes, fmtDateTime, timeAgo } from "@/lib/format"; type BackupsData = { backups: Array<{ name: string; size: number; mtime: number }>; totalSize: number; interval: string; retentionDays: string; }; export default function BackupsPage() { const { data, refresh } = useApi("/api/backups", 30000); const [creating, setCreating] = useState(false); const [deleting, setDeleting] = useState(null); const [restoreTarget, setRestoreTarget] = useState(null); const [restoreConfirm, setRestoreConfirm] = useState(""); const [restoring, setRestoring] = useState(false); const backupNow = async () => { setCreating(true); toast("Backup started — this can take a minute on a big world", "info"); try { const res = await api<{ created: string }>("/api/backups", { method: "POST" }); toast(`Backup created: ${res.created}`, "success"); void refresh(); } catch (err) { toast((err as Error).message, "error"); } finally { setCreating(false); } }; const remove = async (name: string) => { setDeleting(name); try { await api(`/api/backups/file?name=${encodeURIComponent(name)}`, { method: "DELETE" }); toast(`${name} deleted`, "warn"); void refresh(); } catch (err) { toast((err as Error).message, "error"); } finally { setDeleting(null); } }; const restore = async () => { if (!restoreTarget) return; setRestoring(true); try { await api("/api/backups/restore", { body: { name: restoreTarget, confirm: restoreConfirm } }); toast("Restore complete — the server is starting with the restored world", "success"); setRestoreTarget(null); setRestoreConfirm(""); } catch (err) { toast((err as Error).message, "error"); } finally { setRestoring(false); } }; return (

Backups

Automatic every {data?.interval ?? "24h"}, kept {data?.retentionDays ?? "7"} days ·{" "} {fmtBytes(data?.totalSize)} total — change in{" "} Configuration

{!data || data.backups.length === 0 ? ( } title="No backups yet" hint='The backup sidecar creates the first one automatically shortly after the server starts, or press "Back up now".' /> ) : ( {data.backups.map((b) => ( ))}
Backup Created Size Actions
{b.name} {timeAgo(b.mtime)} {fmtBytes(b.size)}
)}
{ setRestoreTarget(null); setRestoreConfirm(""); }} title="Restore backup" >

Restoring {restoreTarget} will:

  1. Stop the server (players are disconnected)
  2. Overwrite the current world and settings with the backup —{" "} everything since the backup is lost
  3. Start the server again

Type restore to confirm:

setRestoreConfirm(e.target.value)} placeholder="restore" className="w-full font-mono" />
); }