import { useMutation } from "@tanstack/react-query"; import { AlertTriangle, Download } from "lucide-react"; import { useState } from "react"; import { toast } from "sonner"; import { AuthLayout } from "~/client/components/auth-layout"; import { Alert, AlertDescription, AlertTitle } from "~/client/components/ui/alert"; import { Button } from "~/client/components/ui/button"; import { Input } from "~/client/components/ui/input"; import { Label } from "~/client/components/ui/label"; import { downloadResticPasswordMutation } from "~/client/api-client/@tanstack/react-query.gen"; import { parseError } from "~/client/lib/errors"; import { RECOVERY_KEY_DOWNLOAD_SKIPPED_COOKIE_MAX_AGE, RECOVERY_KEY_DOWNLOAD_SKIPPED_COOKIE_NAME, } from "~/lib/recovery-key-skip"; import { useNavigate } from "@tanstack/react-router"; const RECOVERY_KEY_CREDENTIAL_REQUIRED_MESSAGE = "Downloading the recovery key requires a local credential password. Ask an operator to run `docker exec -it zerobyte bun run cli reset-password` for your user, then sign in with that password and try again."; type Props = { hasCredentialPassword: boolean; userId: string | null; }; export function DownloadRecoveryKeyPage({ hasCredentialPassword, userId }: Props) { const navigate = useNavigate(); const [password, setPassword] = useState(""); const [blockedMessage, setBlockedMessage] = useState(null); const downloadResticPassword = useMutation({ ...downloadResticPasswordMutation(), onSuccess: (data) => { const blob = new Blob([data], { type: "text/plain" }); const url = window.URL.createObjectURL(blob); const a = document.createElement("a"); a.href = url; a.download = "restic.pass"; document.body.appendChild(a); a.click(); document.body.removeChild(a); window.setTimeout(() => window.URL.revokeObjectURL(url), 60_000); toast.success("Recovery key downloaded successfully!"); setBlockedMessage(null); void navigate({ to: "/volumes", replace: true }); }, onError: (error) => { const message = parseError(error)?.message; setBlockedMessage(message?.includes("credential password") ? message : null); toast.error("Failed to download recovery key", { description: message }); }, }); const handleSubmit = (e: React.SubmitEvent) => { e.preventDefault(); if (!password) { toast.error("Password is required"); return; } setBlockedMessage(null); downloadResticPassword.mutate({ body: { password } }); }; const handleSkip = () => { if (!userId) return; document.cookie = `${RECOVERY_KEY_DOWNLOAD_SKIPPED_COOKIE_NAME}=${userId}; path=/; max-age=${RECOVERY_KEY_DOWNLOAD_SKIPPED_COOKIE_MAX_AGE}`; void navigate({ to: "/volumes", replace: true }); }; return ( Important: Save This File Securely Your Restic password is essential for recovering your backup data. If you previously downloaded this file, replace that saved copy with the new download. If you lose access to this server without this file, your backups will be unrecoverable. Store it in a password manager or encrypted storage.
{(!hasCredentialPassword || blockedMessage) && ( Local password required {blockedMessage ?? RECOVERY_KEY_CREDENTIAL_REQUIRED_MESSAGE} )} {hasCredentialPassword && (
setPassword(e.target.value)} placeholder="Enter your password" required disabled={downloadResticPassword.isPending} />

Enter your account password to download the recovery key

)}
{hasCredentialPassword && ( )}
); }