diff --git a/app/client/components/path-selector.tsx b/app/client/components/path-selector.tsx
new file mode 100644
index 0000000..5d8132b
--- /dev/null
+++ b/app/client/components/path-selector.tsx
@@ -0,0 +1,39 @@
+import { useState } from "react";
+import { DirectoryBrowser } from "./directory-browser";
+import { Button } from "./ui/button";
+
+type Props = {
+ value: string;
+ onChange: (path: string) => void;
+ label?: string;
+};
+
+export const PathSelector = ({ value, onChange }: Props) => {
+ const [showBrowser, setShowBrowser] = useState(false);
+
+ if (showBrowser) {
+ return (
+
+ {
+ onChange(path);
+ setShowBrowser(false);
+ }}
+ selectedPath={value}
+ />
+ setShowBrowser(false)}>
+ Cancel
+
+
+ );
+ }
+
+ return (
+
+
{value}
+
setShowBrowser(true)} size="sm">
+ Change
+
+
+ );
+};
diff --git a/app/client/components/restore-form.tsx b/app/client/components/restore-form.tsx
new file mode 100644
index 0000000..4e4f779
--- /dev/null
+++ b/app/client/components/restore-form.tsx
@@ -0,0 +1,325 @@
+import { useCallback, useState } from "react";
+import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
+import { useNavigate } from "react-router";
+import { toast } from "sonner";
+import { ChevronDown, FileIcon, FolderOpen, RotateCcw } from "lucide-react";
+import { Button } from "~/client/components/ui/button";
+import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "~/client/components/ui/card";
+import { Checkbox } from "~/client/components/ui/checkbox";
+import { Input } from "~/client/components/ui/input";
+import { Label } from "~/client/components/ui/label";
+import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "~/client/components/ui/select";
+import { PathSelector } from "~/client/components/path-selector";
+import { FileTree } from "~/client/components/file-tree";
+import { listSnapshotFilesOptions, restoreSnapshotMutation } from "~/client/api-client/@tanstack/react-query.gen";
+import { useFileBrowser } from "~/client/hooks/use-file-browser";
+import { OVERWRITE_MODES, type OverwriteMode } from "~/schemas/restic";
+import type { Snapshot } from "~/client/lib/types";
+
+type RestoreLocation = "original" | "custom";
+
+interface RestoreFormProps {
+ snapshot: Snapshot;
+ repositoryName: string;
+ snapshotId: string;
+ returnPath: string;
+}
+
+export function RestoreForm({ snapshot, repositoryName, snapshotId, returnPath }: RestoreFormProps) {
+ const navigate = useNavigate();
+ const queryClient = useQueryClient();
+
+ const volumeBasePath = snapshot.paths[0]?.match(/^(.*?_data)(\/|$)/)?.[1] || "/";
+
+ const [restoreLocation, setRestoreLocation] = useState("original");
+ const [customTargetPath, setCustomTargetPath] = useState("");
+ const [overwriteMode, setOverwriteMode] = useState("always");
+ const [showAdvanced, setShowAdvanced] = useState(false);
+ const [excludeXattr, setExcludeXattr] = useState("");
+ const [deleteExtraFiles, setDeleteExtraFiles] = useState(false);
+
+ const [selectedPaths, setSelectedPaths] = useState>(new Set());
+
+ const { data: filesData, isLoading: filesLoading } = useQuery({
+ ...listSnapshotFilesOptions({
+ path: { name: repositoryName, snapshotId },
+ query: { path: volumeBasePath },
+ }),
+ enabled: !!repositoryName && !!snapshotId,
+ });
+
+ const stripBasePath = useCallback(
+ (path: string): string => {
+ if (!volumeBasePath) return path;
+ if (path === volumeBasePath) return "/";
+ if (path.startsWith(`${volumeBasePath}/`)) {
+ const stripped = path.slice(volumeBasePath.length);
+ return stripped;
+ }
+ return path;
+ },
+ [volumeBasePath],
+ );
+
+ const addBasePath = useCallback(
+ (displayPath: string): string => {
+ const vbp = volumeBasePath === "/" ? "" : volumeBasePath;
+
+ if (!vbp) return displayPath;
+ if (displayPath === "/") return vbp;
+ return `${vbp}${displayPath}`;
+ },
+ [volumeBasePath],
+ );
+
+ const fileBrowser = useFileBrowser({
+ initialData: filesData,
+ isLoading: filesLoading,
+ fetchFolder: async (path) => {
+ return await queryClient.ensureQueryData(
+ listSnapshotFilesOptions({
+ path: { name: repositoryName, snapshotId },
+ query: { path },
+ }),
+ );
+ },
+ prefetchFolder: (path) => {
+ queryClient.prefetchQuery(
+ listSnapshotFilesOptions({
+ path: { name: repositoryName, snapshotId },
+ query: { path },
+ }),
+ );
+ },
+ pathTransform: {
+ strip: stripBasePath,
+ add: addBasePath,
+ },
+ });
+
+ const { mutate: restoreSnapshot, isPending: isRestoring } = useMutation({
+ ...restoreSnapshotMutation(),
+ onSuccess: (data) => {
+ toast.success("Restore completed", {
+ description: `Successfully restored ${data.filesRestored} file(s). ${data.filesSkipped} file(s) skipped.`,
+ });
+ navigate(returnPath);
+ },
+ onError: (error) => {
+ toast.error("Restore failed", { description: error.message || "Failed to restore snapshot" });
+ },
+ });
+
+ const handleRestore = useCallback(() => {
+ if (!repositoryName || !snapshotId) return;
+
+ const excludeXattrArray = excludeXattr
+ ?.split(",")
+ .map((s) => s.trim())
+ .filter(Boolean);
+
+ const isCustomLocation = restoreLocation === "custom";
+ const targetPath = isCustomLocation && customTargetPath.trim() ? customTargetPath.trim() : undefined;
+
+ const pathsArray = Array.from(selectedPaths);
+ const includePaths = pathsArray.map((path) => addBasePath(path));
+
+ restoreSnapshot({
+ path: { name: repositoryName },
+ body: {
+ snapshotId,
+ include: includePaths.length > 0 ? includePaths : undefined,
+ delete: deleteExtraFiles,
+ excludeXattr: excludeXattrArray && excludeXattrArray.length > 0 ? excludeXattrArray : undefined,
+ targetPath,
+ overwrite: overwriteMode,
+ },
+ });
+ }, [
+ repositoryName,
+ snapshotId,
+ excludeXattr,
+ restoreLocation,
+ customTargetPath,
+ selectedPaths,
+ addBasePath,
+ deleteExtraFiles,
+ overwriteMode,
+ restoreSnapshot,
+ ]);
+
+ const canRestore = restoreLocation === "original" || customTargetPath.trim();
+
+ return (
+
+
+
+
Restore Snapshot
+
+ {repositoryName} / {snapshotId}
+
+
+
+ navigate(returnPath)}>
+ Cancel
+
+
+ {isRestoring
+ ? "Restoring..."
+ : selectedPaths.size > 0
+ ? `Restore ${selectedPaths.size} ${selectedPaths.size === 1 ? "item" : "items"}`
+ : "Restore All"}
+
+
+
+
+
+
+
+
+ Restore Location
+ Choose where to restore the files
+
+
+
+ setRestoreLocation("original")}
+ >
+
+ Original location
+
+ setRestoreLocation("custom")}
+ >
+
+ Custom location
+
+
+ {restoreLocation === "custom" && (
+
+
+
Files will be restored directly to this path
+
+ )}
+
+
+
+
+
+ Overwrite Mode
+ How to handle existing files
+
+
+ setOverwriteMode(value as OverwriteMode)}>
+
+
+
+
+ Always overwrite
+ Only if content changed
+ Only if snapshot is newer
+ Never overwrite
+
+
+
+ {overwriteMode === OVERWRITE_MODES.always &&
+ "Existing files will always be replaced with the snapshot version."}
+ {overwriteMode === OVERWRITE_MODES.ifChanged &&
+ "Files are only replaced if their content differs from the snapshot."}
+ {overwriteMode === OVERWRITE_MODES.ifNewer &&
+ "Files are only replaced if the snapshot version has a newer modification time."}
+ {overwriteMode === OVERWRITE_MODES.never &&
+ "Existing files will never be replaced, only missing files are restored."}
+
+
+
+
+
+ setShowAdvanced(!showAdvanced)}>
+
+ Advanced options
+
+
+
+ {showAdvanced && (
+
+
+
+ Exclude extended attributes
+
+
setExcludeXattr(e.target.value)}
+ />
+
+ Exclude specific extended attributes during restore (comma-separated)
+
+
+
+
+
+ )}
+
+
+
+
+ Select Files to Restore
+
+ {selectedPaths.size > 0
+ ? `${selectedPaths.size} ${selectedPaths.size === 1 ? "item" : "items"} selected`
+ : "Select specific files or folders, or leave empty to restore everything"}
+
+
+
+ {fileBrowser.isLoading && (
+
+ )}
+
+ {fileBrowser.isEmpty && (
+
+
+
No files in this snapshot
+
+ )}
+
+ {!fileBrowser.isLoading && !fileBrowser.isEmpty && (
+
+
+
+ )}
+
+
+
+
+ );
+}
diff --git a/app/client/modules/backups/components/snapshot-file-browser.tsx b/app/client/modules/backups/components/snapshot-file-browser.tsx
index 90382ae..8ace50b 100644
--- a/app/client/modules/backups/components/snapshot-file-browser.tsx
+++ b/app/client/modules/backups/components/snapshot-file-browser.tsx
@@ -1,54 +1,26 @@
-import { useCallback, useState } from "react";
-import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
-import { ChevronDown, FileIcon, FolderOpen, RotateCcw } from "lucide-react";
+import { useCallback } from "react";
+import { useQuery, useQueryClient } from "@tanstack/react-query";
+import { FileIcon } from "lucide-react";
+import { Link } from "react-router";
import { FileTree } from "~/client/components/file-tree";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "~/client/components/ui/card";
-import { Button } from "~/client/components/ui/button";
-import { Checkbox } from "~/client/components/ui/checkbox";
-import { Label } from "~/client/components/ui/label";
-import { Input } from "~/client/components/ui/input";
-import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "~/client/components/ui/select";
-import {
- AlertDialog,
- AlertDialogAction,
- AlertDialogCancel,
- AlertDialogContent,
- AlertDialogDescription,
- AlertDialogFooter,
- AlertDialogHeader,
- AlertDialogTitle,
-} from "~/client/components/ui/alert-dialog";
-import { Tooltip, TooltipContent, TooltipTrigger } from "~/client/components/ui/tooltip";
-import type { Snapshot, Volume } from "~/client/lib/types";
-import { toast } from "sonner";
-import { listSnapshotFilesOptions, restoreSnapshotMutation } from "~/client/api-client/@tanstack/react-query.gen";
+import { Button, buttonVariants } from "~/client/components/ui/button";
+import type { Snapshot } from "~/client/lib/types";
+import { listSnapshotFilesOptions } from "~/client/api-client/@tanstack/react-query.gen";
import { useFileBrowser } from "~/client/hooks/use-file-browser";
-import { OVERWRITE_MODES, type OverwriteMode } from "~/schemas/restic";
-
-type RestoreLocation = "original" | "custom";
interface Props {
snapshot: Snapshot;
repositoryName: string;
- volume?: Volume;
+ backupId?: string;
onDeleteSnapshot?: (snapshotId: string) => void;
isDeletingSnapshot?: boolean;
}
export const SnapshotFileBrowser = (props: Props) => {
- const { snapshot, repositoryName, volume, onDeleteSnapshot, isDeletingSnapshot } = props;
-
- const isReadOnly = volume?.config && "readOnly" in volume.config && volume.config.readOnly === true;
+ const { snapshot, repositoryName, backupId, onDeleteSnapshot, isDeletingSnapshot } = props;
const queryClient = useQueryClient();
- const [selectedPaths, setSelectedPaths] = useState>(new Set());
- const [showRestoreDialog, setShowRestoreDialog] = useState(false);
- const [deleteExtraFiles, setDeleteExtraFiles] = useState(false);
- const [showAdvanced, setShowAdvanced] = useState(false);
- const [excludeXattr, setExcludeXattr] = useState("");
- const [restoreLocation, setRestoreLocation] = useState("original");
- const [customTargetPath, setCustomTargetPath] = useState("");
- const [overwriteMode, setOverwriteMode] = useState("always");
const volumeBasePath = snapshot.paths[0]?.match(/^(.*?_data)(\/|$)/)?.[1] || "/";
@@ -108,61 +80,6 @@ export const SnapshotFileBrowser = (props: Props) => {
},
});
- const { mutate: restoreSnapshot, isPending: isRestoring } = useMutation({
- ...restoreSnapshotMutation(),
- onSuccess: (data) => {
- toast.success("Restore completed", {
- description: `Successfully restored ${data.filesRestored} file(s). ${data.filesSkipped} file(s) skipped.`,
- });
- setSelectedPaths(new Set());
- },
- onError: (error) => {
- toast.error("Restore failed", { description: error.message || "Failed to restore snapshot" });
- },
- });
-
- const handleRestoreClick = useCallback(() => {
- setShowRestoreDialog(true);
- }, []);
-
- const handleConfirmRestore = useCallback(() => {
- const pathsArray = Array.from(selectedPaths);
- const includePaths = pathsArray.map((path) => addBasePath(path));
-
- const excludeXattrArray = excludeXattr
- ?.split(",")
- .map((s) => s.trim())
- .filter(Boolean);
-
- const isCustomLocation = restoreLocation === "custom";
- const targetPath = isCustomLocation && customTargetPath.trim() ? customTargetPath.trim() : undefined;
-
- restoreSnapshot({
- path: { name: repositoryName },
- body: {
- snapshotId: snapshot.short_id,
- include: includePaths,
- delete: deleteExtraFiles,
- excludeXattr: excludeXattrArray && excludeXattrArray.length > 0 ? excludeXattrArray : undefined,
- targetPath,
- overwrite: overwriteMode,
- },
- });
-
- setShowRestoreDialog(false);
- }, [
- selectedPaths,
- addBasePath,
- repositoryName,
- snapshot.short_id,
- restoreSnapshot,
- deleteExtraFiles,
- excludeXattr,
- restoreLocation,
- customTargetPath,
- overwriteMode,
- ]);
-
return (
@@ -173,30 +90,16 @@ export const SnapshotFileBrowser = (props: Props) => {
{`Viewing snapshot from ${new Date(snapshot?.time ?? 0).toLocaleString()}`}
- {selectedPaths.size > 0 && (
-
-
-
-
- {isRestoring
- ? "Restoring..."
- : `Restore ${selectedPaths.size} selected ${selectedPaths.size === 1 ? "item" : "items"}`}
-
-
-
- {isReadOnly && (
-
- Volume is mounted as read-only.
- Please remount with read-only disabled to restore files.
-
- )}
-
- )}
+
+ Restore
+
{onDeleteSnapshot && (
{
expandedFolders={fileBrowser.expandedFolders}
loadingFolders={fileBrowser.loadingFolders}
className="px-2 py-2"
- withCheckboxes={true}
- selectedPaths={selectedPaths}
- onSelectionChange={setSelectedPaths}
/>
)}
-
-
-
-
- Confirm Restore
-
- {selectedPaths.size > 0
- ? `This will restore ${selectedPaths.size} selected ${selectedPaths.size === 1 ? "item" : "items"} from the snapshot.`
- : "This will restore everything from the snapshot."}
-
-
-
-
-
Restore Location
-
- setRestoreLocation("original")}
- >
-
- Original location
-
- setRestoreLocation("custom")}
- >
-
- Custom location
-
-
- {restoreLocation === "custom" && (
-
-
setCustomTargetPath(e.target.value)}
- />
-
Files will be restored directly to this path
-
- )}
-
-
-
-
Overwrite Mode
-
setOverwriteMode(value as OverwriteMode)}>
-
-
-
-
- Always overwrite
- Only if content changed
- Only if snapshot is newer
- Never overwrite
-
-
-
- {overwriteMode === OVERWRITE_MODES.always &&
- "Existing files will always be replaced with the snapshot version."}
- {overwriteMode === OVERWRITE_MODES.ifChanged &&
- "Files are only replaced if their content differs from the snapshot."}
- {overwriteMode === OVERWRITE_MODES.ifNewer &&
- "Files are only replaced if the snapshot version has a newer modification time."}
- {overwriteMode === OVERWRITE_MODES.never &&
- "Existing files will never be replaced, only missing files are restored."}
-
-
-
-
-
setShowAdvanced(!showAdvanced)}
- className="h-auto p-0 text-sm font-normal"
- >
- Advanced Options
-
-
-
- {showAdvanced && (
-
-
-
- Exclude Extended Attributes
-
-
setExcludeXattr(e.target.value)}
- />
-
- Exclude specific extended attributes during restore (comma-separated)
-
-
-
-
-
- )}
-
-
-
- Cancel
-
- Confirm Restore
-
-
-
-
);
};
diff --git a/app/client/modules/backups/routes/backup-details.tsx b/app/client/modules/backups/routes/backup-details.tsx
index c7c8288..09433c1 100644
--- a/app/client/modules/backups/routes/backup-details.tsx
+++ b/app/client/modules/backups/routes/backup-details.tsx
@@ -240,7 +240,7 @@ export default function ScheduleDetailsPage({ params, loaderData }: Route.Compon
key={selectedSnapshot?.short_id}
snapshot={selectedSnapshot}
repositoryName={schedule.repository.name}
- volume={schedule.volume}
+ backupId={schedule.id.toString()}
onDeleteSnapshot={handleDeleteSnapshot}
isDeletingSnapshot={deleteSnapshot.isPending}
/>
diff --git a/app/client/modules/backups/routes/restore-snapshot.tsx b/app/client/modules/backups/routes/restore-snapshot.tsx
new file mode 100644
index 0000000..0a8ce52
--- /dev/null
+++ b/app/client/modules/backups/routes/restore-snapshot.tsx
@@ -0,0 +1,54 @@
+import { redirect } from "react-router";
+import { getBackupSchedule, getSnapshotDetails } from "~/client/api-client";
+import { RestoreForm } from "~/client/components/restore-form";
+import type { Route } from "./+types/restore-snapshot";
+
+export const handle = {
+ breadcrumb: (match: Route.MetaArgs) => [
+ { label: "Backups", href: "/backups" },
+ { label: `Schedule #${match.params.id}`, href: `/backups/${match.params.id}` },
+ { label: match.params.snapshotId },
+ { label: "Restore" },
+ ],
+};
+
+export function meta({ params }: Route.MetaArgs) {
+ return [
+ { title: `Zerobyte - Restore Snapshot ${params.snapshotId}` },
+ {
+ name: "description",
+ content: "Restore files from a backup snapshot.",
+ },
+ ];
+}
+
+export const clientLoader = async ({ params }: Route.ClientLoaderArgs) => {
+ const schedule = await getBackupSchedule({ path: { scheduleId: params.id } });
+ if (!schedule.data) return redirect("/backups");
+
+ const repositoryName = schedule.data.repository.name;
+ const snapshot = await getSnapshotDetails({
+ path: { name: repositoryName, snapshotId: params.snapshotId },
+ });
+ if (!snapshot.data) return redirect(`/backups/${params.id}`);
+
+ return {
+ snapshot: snapshot.data,
+ repositoryName,
+ snapshotId: params.snapshotId,
+ backupId: params.id,
+ };
+};
+
+export default function RestoreSnapshotFromBackupPage({ loaderData }: Route.ComponentProps) {
+ const { snapshot, repositoryName, snapshotId, backupId } = loaderData;
+
+ return (
+
+ );
+}
diff --git a/app/client/modules/notifications/components/create-notification-form.tsx b/app/client/modules/notifications/components/create-notification-form.tsx
index fcf31bd..4c809df 100644
--- a/app/client/modules/notifications/components/create-notification-form.tsx
+++ b/app/client/modules/notifications/components/create-notification-form.tsx
@@ -30,7 +30,6 @@ type Props = {
mode?: "create" | "update";
initialValues?: Partial;
formId?: string;
- loading?: boolean;
className?: string;
};
diff --git a/app/client/modules/notifications/routes/create-notification.tsx b/app/client/modules/notifications/routes/create-notification.tsx
index 6f6f0dd..7990d65 100644
--- a/app/client/modules/notifications/routes/create-notification.tsx
+++ b/app/client/modules/notifications/routes/create-notification.tsx
@@ -62,12 +62,7 @@ export default function CreateNotification() {
)}
-
+
navigate("/notifications")}>
Cancel
diff --git a/app/client/modules/notifications/routes/notification-details.tsx b/app/client/modules/notifications/routes/notification-details.tsx
index b832dbb..3a4ced5 100644
--- a/app/client/modules/notifications/routes/notification-details.tsx
+++ b/app/client/modules/notifications/routes/notification-details.tsx
@@ -171,20 +171,18 @@ export default function NotificationDetailsPage({ loaderData }: Route.ComponentP
)}
- <>
-
-
-
- Save Changes
-
-
- >
+
+
+
+ Save Changes
+
+
diff --git a/app/client/modules/repositories/components/restore-snapshot-dialog.tsx b/app/client/modules/repositories/components/restore-snapshot-dialog.tsx
deleted file mode 100644
index f7e64d5..0000000
--- a/app/client/modules/repositories/components/restore-snapshot-dialog.tsx
+++ /dev/null
@@ -1,100 +0,0 @@
-import { useMutation } from "@tanstack/react-query";
-import { RotateCcw } from "lucide-react";
-import { useId, useState } from "react";
-import { toast } from "sonner";
-import { restoreSnapshotMutation } from "~/client/api-client/@tanstack/react-query.gen";
-import { parseError } from "~/client/lib/errors";
-import { Button } from "~/client/components/ui/button";
-import {
- Dialog,
- DialogContent,
- DialogDescription,
- DialogFooter,
- DialogHeader,
- DialogTitle,
- DialogTrigger,
-} from "~/client/components/ui/dialog";
-import { ScrollArea } from "~/client/components/ui/scroll-area";
-import { RestoreSnapshotForm, type RestoreSnapshotFormValues } from "./restore-snapshot-form";
-
-type Props = {
- name: string;
- snapshotId: string;
-};
-
-export const RestoreSnapshotDialog = ({ name, snapshotId }: Props) => {
- const [open, setOpen] = useState(false);
- const formId = useId();
-
- const restore = useMutation({
- ...restoreSnapshotMutation(),
- onSuccess: (data) => {
- toast.success("Snapshot restored successfully", {
- description: `${data.filesRestored} files restored, ${data.filesSkipped} files skipped`,
- });
- setOpen(false);
- },
- onError: (error) => {
- toast.error("Failed to restore snapshot", {
- description: parseError(error)?.message,
- });
- },
- });
-
- const handleSubmit = (values: RestoreSnapshotFormValues) => {
- const include = values.include
- ?.split(",")
- .map((s) => s.trim())
- .filter(Boolean);
-
- const exclude = values.exclude
- ?.split(",")
- .map((s) => s.trim())
- .filter(Boolean);
-
- const excludeXattr = values.excludeXattr
- ?.split(",")
- .map((s) => s.trim())
- .filter(Boolean);
-
- restore.mutate({
- path: { name },
- body: {
- snapshotId,
- include: include && include.length > 0 ? include : undefined,
- exclude: exclude && exclude.length > 0 ? exclude : undefined,
- excludeXattr: excludeXattr && excludeXattr.length > 0 ? excludeXattr : undefined,
- },
- });
- };
-
- return (
-
-
-
-
- Restore
-
-
-
-
-
- Restore Snapshot
-
- Restore snapshot {snapshotId.substring(0, 8)} to a local filesystem path
-
-
-
-
- setOpen(false)}>
- Cancel
-
-
- {restore.isPending ? "Restoring..." : "Restore"}
-
-
-
-
-
- );
-};
diff --git a/app/client/modules/repositories/components/restore-snapshot-form.tsx b/app/client/modules/repositories/components/restore-snapshot-form.tsx
deleted file mode 100644
index e39fa80..0000000
--- a/app/client/modules/repositories/components/restore-snapshot-form.tsx
+++ /dev/null
@@ -1,138 +0,0 @@
-import { arktypeResolver } from "@hookform/resolvers/arktype";
-import { type } from "arktype";
-import { ChevronDown } from "lucide-react";
-import { useState } from "react";
-import { useForm } from "react-hook-form";
-import {
- Form,
- FormControl,
- FormDescription,
- FormField,
- FormItem,
- FormLabel,
- FormMessage,
-} from "~/client/components/ui/form";
-import { Input } from "~/client/components/ui/input";
-import { Button } from "~/client/components/ui/button";
-
-const restoreSnapshotFormSchema = type({
- path: "string?",
- include: "string?",
- exclude: "string?",
- excludeXattr: "string?",
-});
-
-export type RestoreSnapshotFormValues = typeof restoreSnapshotFormSchema.inferIn;
-
-type Props = {
- formId: string;
- onSubmit: (values: RestoreSnapshotFormValues) => void;
- className?: string;
-};
-
-export const RestoreSnapshotForm = ({ formId, onSubmit, className }: Props) => {
- const [showAdvanced, setShowAdvanced] = useState(false);
-
- const form = useForm({
- resolver: arktypeResolver(restoreSnapshotFormSchema),
- defaultValues: {
- path: "",
- include: "",
- exclude: "",
- excludeXattr: "",
- },
- });
-
- const handleSubmit = (values: RestoreSnapshotFormValues) => {
- onSubmit(values);
- };
-
- return (
-
-
- );
-};
diff --git a/app/client/modules/repositories/routes/restore-snapshot.tsx b/app/client/modules/repositories/routes/restore-snapshot.tsx
new file mode 100644
index 0000000..a1d7075
--- /dev/null
+++ b/app/client/modules/repositories/routes/restore-snapshot.tsx
@@ -0,0 +1,45 @@
+import { redirect } from "react-router";
+import { getSnapshotDetails } from "~/client/api-client";
+import { RestoreForm } from "~/client/components/restore-form";
+import type { Route } from "./+types/restore-snapshot";
+
+export const handle = {
+ breadcrumb: (match: Route.MetaArgs) => [
+ { label: "Repositories", href: "/repositories" },
+ { label: match.params.name, href: `/repositories/${match.params.name}` },
+ { label: match.params.snapshotId, href: `/repositories/${match.params.name}/${match.params.snapshotId}` },
+ { label: "Restore" },
+ ],
+};
+
+export function meta({ params }: Route.MetaArgs) {
+ return [
+ { title: `Zerobyte - Restore Snapshot ${params.snapshotId}` },
+ {
+ name: "description",
+ content: "Restore files from a backup snapshot.",
+ },
+ ];
+}
+
+export const clientLoader = async ({ params }: Route.ClientLoaderArgs) => {
+ const snapshot = await getSnapshotDetails({
+ path: { name: params.name, snapshotId: params.snapshotId },
+ });
+ if (snapshot.data) return { snapshot: snapshot.data, name: params.name, snapshotId: params.snapshotId };
+
+ return redirect("/repositories");
+};
+
+export default function RestoreSnapshotPage({ loaderData }: Route.ComponentProps) {
+ const { snapshot, name, snapshotId } = loaderData;
+
+ return (
+
+ );
+}
diff --git a/app/client/modules/repositories/routes/snapshot-details.tsx b/app/client/modules/repositories/routes/snapshot-details.tsx
index 1697574..cc9ed36 100644
--- a/app/client/modules/repositories/routes/snapshot-details.tsx
+++ b/app/client/modules/repositories/routes/snapshot-details.tsx
@@ -2,7 +2,6 @@ import { useQuery } from "@tanstack/react-query";
import { redirect, useParams } from "react-router";
import { listSnapshotFilesOptions } from "~/client/api-client/@tanstack/react-query.gen";
import { Card, CardContent, CardHeader, CardTitle } from "~/client/components/ui/card";
-import { RestoreSnapshotDialog } from "../components/restore-snapshot-dialog";
import { SnapshotFileBrowser } from "~/client/modules/backups/components/snapshot-file-browser";
import { getSnapshotDetails } from "~/client/api-client";
import type { Route } from "./+types/snapshot-details";
@@ -63,7 +62,6 @@ export default function SnapshotDetailsPage({ loaderData }: Route.ComponentProps
{name}
Snapshot: {snapshotId}
-
diff --git a/app/drizzle/meta/0016_snapshot.json b/app/drizzle/meta/0016_snapshot.json
index 411b2cb..5d2f85e 100644
--- a/app/drizzle/meta/0016_snapshot.json
+++ b/app/drizzle/meta/0016_snapshot.json
@@ -1,688 +1,653 @@
{
- "id": "e50ff0fb-4111-4d20-b550-9407ee397517",
- "prevId": "e52fe10a-3f36-4b21-abef-c15990d28363",
- "version": "6",
- "dialect": "sqlite",
- "tables": {
- "app_metadata": {
- "name": "app_metadata",
- "columns": {
- "key": {
- "name": "key",
- "type": "text",
- "primaryKey": true,
- "notNull": true,
- "autoincrement": false
- },
- "value": {
- "name": "value",
- "type": "text",
- "primaryKey": false,
- "notNull": true,
- "autoincrement": false
- },
- "created_at": {
- "name": "created_at",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "autoincrement": false,
- "default": "(unixepoch())"
- },
- "updated_at": {
- "name": "updated_at",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "autoincrement": false,
- "default": "(unixepoch())"
- }
- },
- "indexes": {},
- "foreignKeys": {},
- "compositePrimaryKeys": {},
- "uniqueConstraints": {},
- "checkConstraints": {}
- },
- "backup_schedule_notifications_table": {
- "name": "backup_schedule_notifications_table",
- "columns": {
- "schedule_id": {
- "name": "schedule_id",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "autoincrement": false
- },
- "destination_id": {
- "name": "destination_id",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "autoincrement": false
- },
- "notify_on_start": {
- "name": "notify_on_start",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "autoincrement": false,
- "default": false
- },
- "notify_on_success": {
- "name": "notify_on_success",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "autoincrement": false,
- "default": false
- },
- "notify_on_failure": {
- "name": "notify_on_failure",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "autoincrement": false,
- "default": true
- },
- "created_at": {
- "name": "created_at",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "autoincrement": false,
- "default": "(unixepoch())"
- }
- },
- "indexes": {},
- "foreignKeys": {
- "backup_schedule_notifications_table_schedule_id_backup_schedules_table_id_fk": {
- "name": "backup_schedule_notifications_table_schedule_id_backup_schedules_table_id_fk",
- "tableFrom": "backup_schedule_notifications_table",
- "columnsFrom": [
- "schedule_id"
- ],
- "tableTo": "backup_schedules_table",
- "columnsTo": [
- "id"
- ],
- "onUpdate": "no action",
- "onDelete": "cascade"
- },
- "backup_schedule_notifications_table_destination_id_notification_destinations_table_id_fk": {
- "name": "backup_schedule_notifications_table_destination_id_notification_destinations_table_id_fk",
- "tableFrom": "backup_schedule_notifications_table",
- "columnsFrom": [
- "destination_id"
- ],
- "tableTo": "notification_destinations_table",
- "columnsTo": [
- "id"
- ],
- "onUpdate": "no action",
- "onDelete": "cascade"
- }
- },
- "compositePrimaryKeys": {
- "backup_schedule_notifications_table_schedule_id_destination_id_pk": {
- "columns": [
- "schedule_id",
- "destination_id"
- ],
- "name": "backup_schedule_notifications_table_schedule_id_destination_id_pk"
- }
- },
- "uniqueConstraints": {},
- "checkConstraints": {}
- },
- "backup_schedules_table": {
- "name": "backup_schedules_table",
- "columns": {
- "id": {
- "name": "id",
- "type": "integer",
- "primaryKey": true,
- "notNull": true,
- "autoincrement": true
- },
- "volume_id": {
- "name": "volume_id",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "autoincrement": false
- },
- "repository_id": {
- "name": "repository_id",
- "type": "text",
- "primaryKey": false,
- "notNull": true,
- "autoincrement": false
- },
- "enabled": {
- "name": "enabled",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "autoincrement": false,
- "default": true
- },
- "cron_expression": {
- "name": "cron_expression",
- "type": "text",
- "primaryKey": false,
- "notNull": true,
- "autoincrement": false
- },
- "retention_policy": {
- "name": "retention_policy",
- "type": "text",
- "primaryKey": false,
- "notNull": false,
- "autoincrement": false
- },
- "exclude_patterns": {
- "name": "exclude_patterns",
- "type": "text",
- "primaryKey": false,
- "notNull": false,
- "autoincrement": false,
- "default": "'[]'"
- },
- "include_patterns": {
- "name": "include_patterns",
- "type": "text",
- "primaryKey": false,
- "notNull": false,
- "autoincrement": false,
- "default": "'[]'"
- },
- "last_backup_at": {
- "name": "last_backup_at",
- "type": "integer",
- "primaryKey": false,
- "notNull": false,
- "autoincrement": false
- },
- "last_backup_status": {
- "name": "last_backup_status",
- "type": "text",
- "primaryKey": false,
- "notNull": false,
- "autoincrement": false
- },
- "last_backup_error": {
- "name": "last_backup_error",
- "type": "text",
- "primaryKey": false,
- "notNull": false,
- "autoincrement": false
- },
- "next_backup_at": {
- "name": "next_backup_at",
- "type": "integer",
- "primaryKey": false,
- "notNull": false,
- "autoincrement": false
- },
- "created_at": {
- "name": "created_at",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "autoincrement": false,
- "default": "(unixepoch())"
- },
- "updated_at": {
- "name": "updated_at",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "autoincrement": false,
- "default": "(unixepoch())"
- }
- },
- "indexes": {},
- "foreignKeys": {
- "backup_schedules_table_volume_id_volumes_table_id_fk": {
- "name": "backup_schedules_table_volume_id_volumes_table_id_fk",
- "tableFrom": "backup_schedules_table",
- "columnsFrom": [
- "volume_id"
- ],
- "tableTo": "volumes_table",
- "columnsTo": [
- "id"
- ],
- "onUpdate": "no action",
- "onDelete": "cascade"
- },
- "backup_schedules_table_repository_id_repositories_table_id_fk": {
- "name": "backup_schedules_table_repository_id_repositories_table_id_fk",
- "tableFrom": "backup_schedules_table",
- "columnsFrom": [
- "repository_id"
- ],
- "tableTo": "repositories_table",
- "columnsTo": [
- "id"
- ],
- "onUpdate": "no action",
- "onDelete": "cascade"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {},
- "checkConstraints": {}
- },
- "notification_destinations_table": {
- "name": "notification_destinations_table",
- "columns": {
- "id": {
- "name": "id",
- "type": "integer",
- "primaryKey": true,
- "notNull": true,
- "autoincrement": true
- },
- "name": {
- "name": "name",
- "type": "text",
- "primaryKey": false,
- "notNull": true,
- "autoincrement": false
- },
- "enabled": {
- "name": "enabled",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "autoincrement": false,
- "default": true
- },
- "type": {
- "name": "type",
- "type": "text",
- "primaryKey": false,
- "notNull": true,
- "autoincrement": false
- },
- "config": {
- "name": "config",
- "type": "text",
- "primaryKey": false,
- "notNull": true,
- "autoincrement": false
- },
- "created_at": {
- "name": "created_at",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "autoincrement": false,
- "default": "(unixepoch())"
- },
- "updated_at": {
- "name": "updated_at",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "autoincrement": false,
- "default": "(unixepoch())"
- }
- },
- "indexes": {
- "notification_destinations_table_name_unique": {
- "name": "notification_destinations_table_name_unique",
- "columns": [
- "name"
- ],
- "isUnique": true
- }
- },
- "foreignKeys": {},
- "compositePrimaryKeys": {},
- "uniqueConstraints": {},
- "checkConstraints": {}
- },
- "repositories_table": {
- "name": "repositories_table",
- "columns": {
- "id": {
- "name": "id",
- "type": "text",
- "primaryKey": true,
- "notNull": true,
- "autoincrement": false
- },
- "short_id": {
- "name": "short_id",
- "type": "text",
- "primaryKey": false,
- "notNull": true,
- "autoincrement": false
- },
- "name": {
- "name": "name",
- "type": "text",
- "primaryKey": false,
- "notNull": true,
- "autoincrement": false
- },
- "type": {
- "name": "type",
- "type": "text",
- "primaryKey": false,
- "notNull": true,
- "autoincrement": false
- },
- "config": {
- "name": "config",
- "type": "text",
- "primaryKey": false,
- "notNull": true,
- "autoincrement": false
- },
- "compression_mode": {
- "name": "compression_mode",
- "type": "text",
- "primaryKey": false,
- "notNull": false,
- "autoincrement": false,
- "default": "'auto'"
- },
- "status": {
- "name": "status",
- "type": "text",
- "primaryKey": false,
- "notNull": false,
- "autoincrement": false,
- "default": "'unknown'"
- },
- "last_checked": {
- "name": "last_checked",
- "type": "integer",
- "primaryKey": false,
- "notNull": false,
- "autoincrement": false
- },
- "last_error": {
- "name": "last_error",
- "type": "text",
- "primaryKey": false,
- "notNull": false,
- "autoincrement": false
- },
- "created_at": {
- "name": "created_at",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "autoincrement": false,
- "default": "(unixepoch())"
- },
- "updated_at": {
- "name": "updated_at",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "autoincrement": false,
- "default": "(unixepoch())"
- }
- },
- "indexes": {
- "repositories_table_short_id_unique": {
- "name": "repositories_table_short_id_unique",
- "columns": [
- "short_id"
- ],
- "isUnique": true
- },
- "repositories_table_name_unique": {
- "name": "repositories_table_name_unique",
- "columns": [
- "name"
- ],
- "isUnique": true
- }
- },
- "foreignKeys": {},
- "compositePrimaryKeys": {},
- "uniqueConstraints": {},
- "checkConstraints": {}
- },
- "sessions_table": {
- "name": "sessions_table",
- "columns": {
- "id": {
- "name": "id",
- "type": "text",
- "primaryKey": true,
- "notNull": true,
- "autoincrement": false
- },
- "user_id": {
- "name": "user_id",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "autoincrement": false
- },
- "expires_at": {
- "name": "expires_at",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "autoincrement": false
- },
- "created_at": {
- "name": "created_at",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "autoincrement": false,
- "default": "(unixepoch())"
- }
- },
- "indexes": {},
- "foreignKeys": {
- "sessions_table_user_id_users_table_id_fk": {
- "name": "sessions_table_user_id_users_table_id_fk",
- "tableFrom": "sessions_table",
- "columnsFrom": [
- "user_id"
- ],
- "tableTo": "users_table",
- "columnsTo": [
- "id"
- ],
- "onUpdate": "no action",
- "onDelete": "cascade"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {},
- "checkConstraints": {}
- },
- "users_table": {
- "name": "users_table",
- "columns": {
- "id": {
- "name": "id",
- "type": "integer",
- "primaryKey": true,
- "notNull": true,
- "autoincrement": true
- },
- "username": {
- "name": "username",
- "type": "text",
- "primaryKey": false,
- "notNull": true,
- "autoincrement": false
- },
- "password_hash": {
- "name": "password_hash",
- "type": "text",
- "primaryKey": false,
- "notNull": true,
- "autoincrement": false
- },
- "has_downloaded_restic_password": {
- "name": "has_downloaded_restic_password",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "autoincrement": false,
- "default": false
- },
- "created_at": {
- "name": "created_at",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "autoincrement": false,
- "default": "(unixepoch())"
- },
- "updated_at": {
- "name": "updated_at",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "autoincrement": false,
- "default": "(unixepoch())"
- }
- },
- "indexes": {
- "users_table_username_unique": {
- "name": "users_table_username_unique",
- "columns": [
- "username"
- ],
- "isUnique": true
- }
- },
- "foreignKeys": {},
- "compositePrimaryKeys": {},
- "uniqueConstraints": {},
- "checkConstraints": {}
- },
- "volumes_table": {
- "name": "volumes_table",
- "columns": {
- "id": {
- "name": "id",
- "type": "integer",
- "primaryKey": true,
- "notNull": true,
- "autoincrement": true
- },
- "short_id": {
- "name": "short_id",
- "type": "text",
- "primaryKey": false,
- "notNull": true,
- "autoincrement": false
- },
- "name": {
- "name": "name",
- "type": "text",
- "primaryKey": false,
- "notNull": true,
- "autoincrement": false
- },
- "type": {
- "name": "type",
- "type": "text",
- "primaryKey": false,
- "notNull": true,
- "autoincrement": false
- },
- "status": {
- "name": "status",
- "type": "text",
- "primaryKey": false,
- "notNull": true,
- "autoincrement": false,
- "default": "'unmounted'"
- },
- "last_error": {
- "name": "last_error",
- "type": "text",
- "primaryKey": false,
- "notNull": false,
- "autoincrement": false
- },
- "last_health_check": {
- "name": "last_health_check",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "autoincrement": false,
- "default": "(unixepoch())"
- },
- "created_at": {
- "name": "created_at",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "autoincrement": false,
- "default": "(unixepoch())"
- },
- "updated_at": {
- "name": "updated_at",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "autoincrement": false,
- "default": "(unixepoch())"
- },
- "config": {
- "name": "config",
- "type": "text",
- "primaryKey": false,
- "notNull": true,
- "autoincrement": false
- },
- "auto_remount": {
- "name": "auto_remount",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "autoincrement": false,
- "default": true
- }
- },
- "indexes": {
- "volumes_table_short_id_unique": {
- "name": "volumes_table_short_id_unique",
- "columns": [
- "short_id"
- ],
- "isUnique": true
- },
- "volumes_table_name_unique": {
- "name": "volumes_table_name_unique",
- "columns": [
- "name"
- ],
- "isUnique": true
- }
- },
- "foreignKeys": {},
- "compositePrimaryKeys": {},
- "uniqueConstraints": {},
- "checkConstraints": {}
- }
- },
- "views": {},
- "enums": {},
- "_meta": {
- "columns": {},
- "schemas": {},
- "tables": {}
- },
- "internal": {
- "indexes": {}
- }
-}
\ No newline at end of file
+ "id": "e50ff0fb-4111-4d20-b550-9407ee397517",
+ "prevId": "e52fe10a-3f36-4b21-abef-c15990d28363",
+ "version": "6",
+ "dialect": "sqlite",
+ "tables": {
+ "app_metadata": {
+ "name": "app_metadata",
+ "columns": {
+ "key": {
+ "name": "key",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "value": {
+ "name": "value",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(unixepoch())"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(unixepoch())"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "backup_schedule_notifications_table": {
+ "name": "backup_schedule_notifications_table",
+ "columns": {
+ "schedule_id": {
+ "name": "schedule_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "destination_id": {
+ "name": "destination_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "notify_on_start": {
+ "name": "notify_on_start",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": false
+ },
+ "notify_on_success": {
+ "name": "notify_on_success",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": false
+ },
+ "notify_on_failure": {
+ "name": "notify_on_failure",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(unixepoch())"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "backup_schedule_notifications_table_schedule_id_backup_schedules_table_id_fk": {
+ "name": "backup_schedule_notifications_table_schedule_id_backup_schedules_table_id_fk",
+ "tableFrom": "backup_schedule_notifications_table",
+ "columnsFrom": ["schedule_id"],
+ "tableTo": "backup_schedules_table",
+ "columnsTo": ["id"],
+ "onUpdate": "no action",
+ "onDelete": "cascade"
+ },
+ "backup_schedule_notifications_table_destination_id_notification_destinations_table_id_fk": {
+ "name": "backup_schedule_notifications_table_destination_id_notification_destinations_table_id_fk",
+ "tableFrom": "backup_schedule_notifications_table",
+ "columnsFrom": ["destination_id"],
+ "tableTo": "notification_destinations_table",
+ "columnsTo": ["id"],
+ "onUpdate": "no action",
+ "onDelete": "cascade"
+ }
+ },
+ "compositePrimaryKeys": {
+ "backup_schedule_notifications_table_schedule_id_destination_id_pk": {
+ "columns": ["schedule_id", "destination_id"],
+ "name": "backup_schedule_notifications_table_schedule_id_destination_id_pk"
+ }
+ },
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "backup_schedules_table": {
+ "name": "backup_schedules_table",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": true
+ },
+ "volume_id": {
+ "name": "volume_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "repository_id": {
+ "name": "repository_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "enabled": {
+ "name": "enabled",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": true
+ },
+ "cron_expression": {
+ "name": "cron_expression",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "retention_policy": {
+ "name": "retention_policy",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "exclude_patterns": {
+ "name": "exclude_patterns",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "'[]'"
+ },
+ "include_patterns": {
+ "name": "include_patterns",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "'[]'"
+ },
+ "last_backup_at": {
+ "name": "last_backup_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "last_backup_status": {
+ "name": "last_backup_status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "last_backup_error": {
+ "name": "last_backup_error",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "next_backup_at": {
+ "name": "next_backup_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(unixepoch())"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(unixepoch())"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "backup_schedules_table_volume_id_volumes_table_id_fk": {
+ "name": "backup_schedules_table_volume_id_volumes_table_id_fk",
+ "tableFrom": "backup_schedules_table",
+ "columnsFrom": ["volume_id"],
+ "tableTo": "volumes_table",
+ "columnsTo": ["id"],
+ "onUpdate": "no action",
+ "onDelete": "cascade"
+ },
+ "backup_schedules_table_repository_id_repositories_table_id_fk": {
+ "name": "backup_schedules_table_repository_id_repositories_table_id_fk",
+ "tableFrom": "backup_schedules_table",
+ "columnsFrom": ["repository_id"],
+ "tableTo": "repositories_table",
+ "columnsTo": ["id"],
+ "onUpdate": "no action",
+ "onDelete": "cascade"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "notification_destinations_table": {
+ "name": "notification_destinations_table",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "enabled": {
+ "name": "enabled",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": true
+ },
+ "type": {
+ "name": "type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "config": {
+ "name": "config",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(unixepoch())"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(unixepoch())"
+ }
+ },
+ "indexes": {
+ "notification_destinations_table_name_unique": {
+ "name": "notification_destinations_table_name_unique",
+ "columns": ["name"],
+ "isUnique": true
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "repositories_table": {
+ "name": "repositories_table",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "short_id": {
+ "name": "short_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "type": {
+ "name": "type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "config": {
+ "name": "config",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "compression_mode": {
+ "name": "compression_mode",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "'auto'"
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "'unknown'"
+ },
+ "last_checked": {
+ "name": "last_checked",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "last_error": {
+ "name": "last_error",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(unixepoch())"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(unixepoch())"
+ }
+ },
+ "indexes": {
+ "repositories_table_short_id_unique": {
+ "name": "repositories_table_short_id_unique",
+ "columns": ["short_id"],
+ "isUnique": true
+ },
+ "repositories_table_name_unique": {
+ "name": "repositories_table_name_unique",
+ "columns": ["name"],
+ "isUnique": true
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "sessions_table": {
+ "name": "sessions_table",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(unixepoch())"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "sessions_table_user_id_users_table_id_fk": {
+ "name": "sessions_table_user_id_users_table_id_fk",
+ "tableFrom": "sessions_table",
+ "columnsFrom": ["user_id"],
+ "tableTo": "users_table",
+ "columnsTo": ["id"],
+ "onUpdate": "no action",
+ "onDelete": "cascade"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "users_table": {
+ "name": "users_table",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": true
+ },
+ "username": {
+ "name": "username",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "password_hash": {
+ "name": "password_hash",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "has_downloaded_restic_password": {
+ "name": "has_downloaded_restic_password",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(unixepoch())"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(unixepoch())"
+ }
+ },
+ "indexes": {
+ "users_table_username_unique": {
+ "name": "users_table_username_unique",
+ "columns": ["username"],
+ "isUnique": true
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "volumes_table": {
+ "name": "volumes_table",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": true
+ },
+ "short_id": {
+ "name": "short_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "type": {
+ "name": "type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'unmounted'"
+ },
+ "last_error": {
+ "name": "last_error",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "last_health_check": {
+ "name": "last_health_check",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(unixepoch())"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(unixepoch())"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(unixepoch())"
+ },
+ "config": {
+ "name": "config",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "auto_remount": {
+ "name": "auto_remount",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": true
+ }
+ },
+ "indexes": {
+ "volumes_table_short_id_unique": {
+ "name": "volumes_table_short_id_unique",
+ "columns": ["short_id"],
+ "isUnique": true
+ },
+ "volumes_table_name_unique": {
+ "name": "volumes_table_name_unique",
+ "columns": ["name"],
+ "isUnique": true
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ }
+ },
+ "views": {},
+ "enums": {},
+ "_meta": {
+ "columns": {},
+ "schemas": {},
+ "tables": {}
+ },
+ "internal": {
+ "indexes": {}
+ }
+}
diff --git a/app/drizzle/meta/0017_snapshot.json b/app/drizzle/meta/0017_snapshot.json
index 6ca2138..c0dec40 100644
--- a/app/drizzle/meta/0017_snapshot.json
+++ b/app/drizzle/meta/0017_snapshot.json
@@ -1,688 +1,653 @@
{
- "id": "d0bfd316-b8f5-459b-ab17-0ce679479321",
- "prevId": "e50ff0fb-4111-4d20-b550-9407ee397517",
- "version": "6",
- "dialect": "sqlite",
- "tables": {
- "app_metadata": {
- "name": "app_metadata",
- "columns": {
- "key": {
- "name": "key",
- "type": "text",
- "primaryKey": true,
- "notNull": true,
- "autoincrement": false
- },
- "value": {
- "name": "value",
- "type": "text",
- "primaryKey": false,
- "notNull": true,
- "autoincrement": false
- },
- "created_at": {
- "name": "created_at",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "autoincrement": false,
- "default": "(unixepoch())"
- },
- "updated_at": {
- "name": "updated_at",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "autoincrement": false,
- "default": "(unixepoch())"
- }
- },
- "indexes": {},
- "foreignKeys": {},
- "compositePrimaryKeys": {},
- "uniqueConstraints": {},
- "checkConstraints": {}
- },
- "backup_schedule_notifications_table": {
- "name": "backup_schedule_notifications_table",
- "columns": {
- "schedule_id": {
- "name": "schedule_id",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "autoincrement": false
- },
- "destination_id": {
- "name": "destination_id",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "autoincrement": false
- },
- "notify_on_start": {
- "name": "notify_on_start",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "autoincrement": false,
- "default": false
- },
- "notify_on_success": {
- "name": "notify_on_success",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "autoincrement": false,
- "default": false
- },
- "notify_on_failure": {
- "name": "notify_on_failure",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "autoincrement": false,
- "default": true
- },
- "created_at": {
- "name": "created_at",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "autoincrement": false,
- "default": "(unixepoch())"
- }
- },
- "indexes": {},
- "foreignKeys": {
- "backup_schedule_notifications_table_schedule_id_backup_schedules_table_id_fk": {
- "name": "backup_schedule_notifications_table_schedule_id_backup_schedules_table_id_fk",
- "tableFrom": "backup_schedule_notifications_table",
- "columnsFrom": [
- "schedule_id"
- ],
- "tableTo": "backup_schedules_table",
- "columnsTo": [
- "id"
- ],
- "onUpdate": "no action",
- "onDelete": "cascade"
- },
- "backup_schedule_notifications_table_destination_id_notification_destinations_table_id_fk": {
- "name": "backup_schedule_notifications_table_destination_id_notification_destinations_table_id_fk",
- "tableFrom": "backup_schedule_notifications_table",
- "columnsFrom": [
- "destination_id"
- ],
- "tableTo": "notification_destinations_table",
- "columnsTo": [
- "id"
- ],
- "onUpdate": "no action",
- "onDelete": "cascade"
- }
- },
- "compositePrimaryKeys": {
- "backup_schedule_notifications_table_schedule_id_destination_id_pk": {
- "columns": [
- "schedule_id",
- "destination_id"
- ],
- "name": "backup_schedule_notifications_table_schedule_id_destination_id_pk"
- }
- },
- "uniqueConstraints": {},
- "checkConstraints": {}
- },
- "backup_schedules_table": {
- "name": "backup_schedules_table",
- "columns": {
- "id": {
- "name": "id",
- "type": "integer",
- "primaryKey": true,
- "notNull": true,
- "autoincrement": true
- },
- "volume_id": {
- "name": "volume_id",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "autoincrement": false
- },
- "repository_id": {
- "name": "repository_id",
- "type": "text",
- "primaryKey": false,
- "notNull": true,
- "autoincrement": false
- },
- "enabled": {
- "name": "enabled",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "autoincrement": false,
- "default": true
- },
- "cron_expression": {
- "name": "cron_expression",
- "type": "text",
- "primaryKey": false,
- "notNull": true,
- "autoincrement": false
- },
- "retention_policy": {
- "name": "retention_policy",
- "type": "text",
- "primaryKey": false,
- "notNull": false,
- "autoincrement": false
- },
- "exclude_patterns": {
- "name": "exclude_patterns",
- "type": "text",
- "primaryKey": false,
- "notNull": false,
- "autoincrement": false,
- "default": "'[]'"
- },
- "include_patterns": {
- "name": "include_patterns",
- "type": "text",
- "primaryKey": false,
- "notNull": false,
- "autoincrement": false,
- "default": "'[]'"
- },
- "last_backup_at": {
- "name": "last_backup_at",
- "type": "integer",
- "primaryKey": false,
- "notNull": false,
- "autoincrement": false
- },
- "last_backup_status": {
- "name": "last_backup_status",
- "type": "text",
- "primaryKey": false,
- "notNull": false,
- "autoincrement": false
- },
- "last_backup_error": {
- "name": "last_backup_error",
- "type": "text",
- "primaryKey": false,
- "notNull": false,
- "autoincrement": false
- },
- "next_backup_at": {
- "name": "next_backup_at",
- "type": "integer",
- "primaryKey": false,
- "notNull": false,
- "autoincrement": false
- },
- "created_at": {
- "name": "created_at",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "autoincrement": false,
- "default": "(unixepoch())"
- },
- "updated_at": {
- "name": "updated_at",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "autoincrement": false,
- "default": "(unixepoch())"
- }
- },
- "indexes": {},
- "foreignKeys": {
- "backup_schedules_table_volume_id_volumes_table_id_fk": {
- "name": "backup_schedules_table_volume_id_volumes_table_id_fk",
- "tableFrom": "backup_schedules_table",
- "columnsFrom": [
- "volume_id"
- ],
- "tableTo": "volumes_table",
- "columnsTo": [
- "id"
- ],
- "onUpdate": "no action",
- "onDelete": "cascade"
- },
- "backup_schedules_table_repository_id_repositories_table_id_fk": {
- "name": "backup_schedules_table_repository_id_repositories_table_id_fk",
- "tableFrom": "backup_schedules_table",
- "columnsFrom": [
- "repository_id"
- ],
- "tableTo": "repositories_table",
- "columnsTo": [
- "id"
- ],
- "onUpdate": "no action",
- "onDelete": "cascade"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {},
- "checkConstraints": {}
- },
- "notification_destinations_table": {
- "name": "notification_destinations_table",
- "columns": {
- "id": {
- "name": "id",
- "type": "integer",
- "primaryKey": true,
- "notNull": true,
- "autoincrement": true
- },
- "name": {
- "name": "name",
- "type": "text",
- "primaryKey": false,
- "notNull": true,
- "autoincrement": false
- },
- "enabled": {
- "name": "enabled",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "autoincrement": false,
- "default": true
- },
- "type": {
- "name": "type",
- "type": "text",
- "primaryKey": false,
- "notNull": true,
- "autoincrement": false
- },
- "config": {
- "name": "config",
- "type": "text",
- "primaryKey": false,
- "notNull": true,
- "autoincrement": false
- },
- "created_at": {
- "name": "created_at",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "autoincrement": false,
- "default": "(unixepoch())"
- },
- "updated_at": {
- "name": "updated_at",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "autoincrement": false,
- "default": "(unixepoch())"
- }
- },
- "indexes": {
- "notification_destinations_table_name_unique": {
- "name": "notification_destinations_table_name_unique",
- "columns": [
- "name"
- ],
- "isUnique": true
- }
- },
- "foreignKeys": {},
- "compositePrimaryKeys": {},
- "uniqueConstraints": {},
- "checkConstraints": {}
- },
- "repositories_table": {
- "name": "repositories_table",
- "columns": {
- "id": {
- "name": "id",
- "type": "text",
- "primaryKey": true,
- "notNull": true,
- "autoincrement": false
- },
- "short_id": {
- "name": "short_id",
- "type": "text",
- "primaryKey": false,
- "notNull": true,
- "autoincrement": false
- },
- "name": {
- "name": "name",
- "type": "text",
- "primaryKey": false,
- "notNull": true,
- "autoincrement": false
- },
- "type": {
- "name": "type",
- "type": "text",
- "primaryKey": false,
- "notNull": true,
- "autoincrement": false
- },
- "config": {
- "name": "config",
- "type": "text",
- "primaryKey": false,
- "notNull": true,
- "autoincrement": false
- },
- "compression_mode": {
- "name": "compression_mode",
- "type": "text",
- "primaryKey": false,
- "notNull": false,
- "autoincrement": false,
- "default": "'auto'"
- },
- "status": {
- "name": "status",
- "type": "text",
- "primaryKey": false,
- "notNull": false,
- "autoincrement": false,
- "default": "'unknown'"
- },
- "last_checked": {
- "name": "last_checked",
- "type": "integer",
- "primaryKey": false,
- "notNull": false,
- "autoincrement": false
- },
- "last_error": {
- "name": "last_error",
- "type": "text",
- "primaryKey": false,
- "notNull": false,
- "autoincrement": false
- },
- "created_at": {
- "name": "created_at",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "autoincrement": false,
- "default": "(unixepoch())"
- },
- "updated_at": {
- "name": "updated_at",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "autoincrement": false,
- "default": "(unixepoch())"
- }
- },
- "indexes": {
- "repositories_table_short_id_unique": {
- "name": "repositories_table_short_id_unique",
- "columns": [
- "short_id"
- ],
- "isUnique": true
- },
- "repositories_table_name_unique": {
- "name": "repositories_table_name_unique",
- "columns": [
- "name"
- ],
- "isUnique": true
- }
- },
- "foreignKeys": {},
- "compositePrimaryKeys": {},
- "uniqueConstraints": {},
- "checkConstraints": {}
- },
- "sessions_table": {
- "name": "sessions_table",
- "columns": {
- "id": {
- "name": "id",
- "type": "text",
- "primaryKey": true,
- "notNull": true,
- "autoincrement": false
- },
- "user_id": {
- "name": "user_id",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "autoincrement": false
- },
- "expires_at": {
- "name": "expires_at",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "autoincrement": false
- },
- "created_at": {
- "name": "created_at",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "autoincrement": false,
- "default": "(unixepoch())"
- }
- },
- "indexes": {},
- "foreignKeys": {
- "sessions_table_user_id_users_table_id_fk": {
- "name": "sessions_table_user_id_users_table_id_fk",
- "tableFrom": "sessions_table",
- "columnsFrom": [
- "user_id"
- ],
- "tableTo": "users_table",
- "columnsTo": [
- "id"
- ],
- "onUpdate": "no action",
- "onDelete": "cascade"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {},
- "checkConstraints": {}
- },
- "users_table": {
- "name": "users_table",
- "columns": {
- "id": {
- "name": "id",
- "type": "integer",
- "primaryKey": true,
- "notNull": true,
- "autoincrement": true
- },
- "username": {
- "name": "username",
- "type": "text",
- "primaryKey": false,
- "notNull": true,
- "autoincrement": false
- },
- "password_hash": {
- "name": "password_hash",
- "type": "text",
- "primaryKey": false,
- "notNull": true,
- "autoincrement": false
- },
- "has_downloaded_restic_password": {
- "name": "has_downloaded_restic_password",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "autoincrement": false,
- "default": false
- },
- "created_at": {
- "name": "created_at",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "autoincrement": false,
- "default": "(unixepoch())"
- },
- "updated_at": {
- "name": "updated_at",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "autoincrement": false,
- "default": "(unixepoch())"
- }
- },
- "indexes": {
- "users_table_username_unique": {
- "name": "users_table_username_unique",
- "columns": [
- "username"
- ],
- "isUnique": true
- }
- },
- "foreignKeys": {},
- "compositePrimaryKeys": {},
- "uniqueConstraints": {},
- "checkConstraints": {}
- },
- "volumes_table": {
- "name": "volumes_table",
- "columns": {
- "id": {
- "name": "id",
- "type": "integer",
- "primaryKey": true,
- "notNull": true,
- "autoincrement": true
- },
- "short_id": {
- "name": "short_id",
- "type": "text",
- "primaryKey": false,
- "notNull": true,
- "autoincrement": false
- },
- "name": {
- "name": "name",
- "type": "text",
- "primaryKey": false,
- "notNull": true,
- "autoincrement": false
- },
- "type": {
- "name": "type",
- "type": "text",
- "primaryKey": false,
- "notNull": true,
- "autoincrement": false
- },
- "status": {
- "name": "status",
- "type": "text",
- "primaryKey": false,
- "notNull": true,
- "autoincrement": false,
- "default": "'unmounted'"
- },
- "last_error": {
- "name": "last_error",
- "type": "text",
- "primaryKey": false,
- "notNull": false,
- "autoincrement": false
- },
- "last_health_check": {
- "name": "last_health_check",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "autoincrement": false,
- "default": "(unixepoch())"
- },
- "created_at": {
- "name": "created_at",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "autoincrement": false,
- "default": "(unixepoch())"
- },
- "updated_at": {
- "name": "updated_at",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "autoincrement": false,
- "default": "(unixepoch())"
- },
- "config": {
- "name": "config",
- "type": "text",
- "primaryKey": false,
- "notNull": true,
- "autoincrement": false
- },
- "auto_remount": {
- "name": "auto_remount",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "autoincrement": false,
- "default": true
- }
- },
- "indexes": {
- "volumes_table_short_id_unique": {
- "name": "volumes_table_short_id_unique",
- "columns": [
- "short_id"
- ],
- "isUnique": true
- },
- "volumes_table_name_unique": {
- "name": "volumes_table_name_unique",
- "columns": [
- "name"
- ],
- "isUnique": true
- }
- },
- "foreignKeys": {},
- "compositePrimaryKeys": {},
- "uniqueConstraints": {},
- "checkConstraints": {}
- }
- },
- "views": {},
- "enums": {},
- "_meta": {
- "columns": {},
- "schemas": {},
- "tables": {}
- },
- "internal": {
- "indexes": {}
- }
-}
\ No newline at end of file
+ "id": "d0bfd316-b8f5-459b-ab17-0ce679479321",
+ "prevId": "e50ff0fb-4111-4d20-b550-9407ee397517",
+ "version": "6",
+ "dialect": "sqlite",
+ "tables": {
+ "app_metadata": {
+ "name": "app_metadata",
+ "columns": {
+ "key": {
+ "name": "key",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "value": {
+ "name": "value",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(unixepoch())"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(unixepoch())"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "backup_schedule_notifications_table": {
+ "name": "backup_schedule_notifications_table",
+ "columns": {
+ "schedule_id": {
+ "name": "schedule_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "destination_id": {
+ "name": "destination_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "notify_on_start": {
+ "name": "notify_on_start",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": false
+ },
+ "notify_on_success": {
+ "name": "notify_on_success",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": false
+ },
+ "notify_on_failure": {
+ "name": "notify_on_failure",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(unixepoch())"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "backup_schedule_notifications_table_schedule_id_backup_schedules_table_id_fk": {
+ "name": "backup_schedule_notifications_table_schedule_id_backup_schedules_table_id_fk",
+ "tableFrom": "backup_schedule_notifications_table",
+ "columnsFrom": ["schedule_id"],
+ "tableTo": "backup_schedules_table",
+ "columnsTo": ["id"],
+ "onUpdate": "no action",
+ "onDelete": "cascade"
+ },
+ "backup_schedule_notifications_table_destination_id_notification_destinations_table_id_fk": {
+ "name": "backup_schedule_notifications_table_destination_id_notification_destinations_table_id_fk",
+ "tableFrom": "backup_schedule_notifications_table",
+ "columnsFrom": ["destination_id"],
+ "tableTo": "notification_destinations_table",
+ "columnsTo": ["id"],
+ "onUpdate": "no action",
+ "onDelete": "cascade"
+ }
+ },
+ "compositePrimaryKeys": {
+ "backup_schedule_notifications_table_schedule_id_destination_id_pk": {
+ "columns": ["schedule_id", "destination_id"],
+ "name": "backup_schedule_notifications_table_schedule_id_destination_id_pk"
+ }
+ },
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "backup_schedules_table": {
+ "name": "backup_schedules_table",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": true
+ },
+ "volume_id": {
+ "name": "volume_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "repository_id": {
+ "name": "repository_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "enabled": {
+ "name": "enabled",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": true
+ },
+ "cron_expression": {
+ "name": "cron_expression",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "retention_policy": {
+ "name": "retention_policy",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "exclude_patterns": {
+ "name": "exclude_patterns",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "'[]'"
+ },
+ "include_patterns": {
+ "name": "include_patterns",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "'[]'"
+ },
+ "last_backup_at": {
+ "name": "last_backup_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "last_backup_status": {
+ "name": "last_backup_status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "last_backup_error": {
+ "name": "last_backup_error",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "next_backup_at": {
+ "name": "next_backup_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(unixepoch())"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(unixepoch())"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "backup_schedules_table_volume_id_volumes_table_id_fk": {
+ "name": "backup_schedules_table_volume_id_volumes_table_id_fk",
+ "tableFrom": "backup_schedules_table",
+ "columnsFrom": ["volume_id"],
+ "tableTo": "volumes_table",
+ "columnsTo": ["id"],
+ "onUpdate": "no action",
+ "onDelete": "cascade"
+ },
+ "backup_schedules_table_repository_id_repositories_table_id_fk": {
+ "name": "backup_schedules_table_repository_id_repositories_table_id_fk",
+ "tableFrom": "backup_schedules_table",
+ "columnsFrom": ["repository_id"],
+ "tableTo": "repositories_table",
+ "columnsTo": ["id"],
+ "onUpdate": "no action",
+ "onDelete": "cascade"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "notification_destinations_table": {
+ "name": "notification_destinations_table",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "enabled": {
+ "name": "enabled",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": true
+ },
+ "type": {
+ "name": "type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "config": {
+ "name": "config",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(unixepoch())"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(unixepoch())"
+ }
+ },
+ "indexes": {
+ "notification_destinations_table_name_unique": {
+ "name": "notification_destinations_table_name_unique",
+ "columns": ["name"],
+ "isUnique": true
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "repositories_table": {
+ "name": "repositories_table",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "short_id": {
+ "name": "short_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "type": {
+ "name": "type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "config": {
+ "name": "config",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "compression_mode": {
+ "name": "compression_mode",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "'auto'"
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "'unknown'"
+ },
+ "last_checked": {
+ "name": "last_checked",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "last_error": {
+ "name": "last_error",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(unixepoch())"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(unixepoch())"
+ }
+ },
+ "indexes": {
+ "repositories_table_short_id_unique": {
+ "name": "repositories_table_short_id_unique",
+ "columns": ["short_id"],
+ "isUnique": true
+ },
+ "repositories_table_name_unique": {
+ "name": "repositories_table_name_unique",
+ "columns": ["name"],
+ "isUnique": true
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "sessions_table": {
+ "name": "sessions_table",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(unixepoch())"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "sessions_table_user_id_users_table_id_fk": {
+ "name": "sessions_table_user_id_users_table_id_fk",
+ "tableFrom": "sessions_table",
+ "columnsFrom": ["user_id"],
+ "tableTo": "users_table",
+ "columnsTo": ["id"],
+ "onUpdate": "no action",
+ "onDelete": "cascade"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "users_table": {
+ "name": "users_table",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": true
+ },
+ "username": {
+ "name": "username",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "password_hash": {
+ "name": "password_hash",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "has_downloaded_restic_password": {
+ "name": "has_downloaded_restic_password",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(unixepoch())"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(unixepoch())"
+ }
+ },
+ "indexes": {
+ "users_table_username_unique": {
+ "name": "users_table_username_unique",
+ "columns": ["username"],
+ "isUnique": true
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ },
+ "volumes_table": {
+ "name": "volumes_table",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": true
+ },
+ "short_id": {
+ "name": "short_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "type": {
+ "name": "type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'unmounted'"
+ },
+ "last_error": {
+ "name": "last_error",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "last_health_check": {
+ "name": "last_health_check",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(unixepoch())"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(unixepoch())"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(unixepoch())"
+ },
+ "config": {
+ "name": "config",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "auto_remount": {
+ "name": "auto_remount",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": true
+ }
+ },
+ "indexes": {
+ "volumes_table_short_id_unique": {
+ "name": "volumes_table_short_id_unique",
+ "columns": ["short_id"],
+ "isUnique": true
+ },
+ "volumes_table_name_unique": {
+ "name": "volumes_table_name_unique",
+ "columns": ["name"],
+ "isUnique": true
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "checkConstraints": {}
+ }
+ },
+ "views": {},
+ "enums": {},
+ "_meta": {
+ "columns": {},
+ "schemas": {},
+ "tables": {}
+ },
+ "internal": {
+ "indexes": {}
+ }
+}
diff --git a/app/drizzle/meta/_journal.json b/app/drizzle/meta/_journal.json
index e21e045..e11599d 100644
--- a/app/drizzle/meta/_journal.json
+++ b/app/drizzle/meta/_journal.json
@@ -1,132 +1,132 @@
{
- "version": "7",
- "dialect": "sqlite",
- "entries": [
- {
- "idx": 0,
- "version": "6",
- "when": 1755765658194,
- "tag": "0000_known_madelyne_pryor",
- "breakpoints": true
- },
- {
- "idx": 1,
- "version": "6",
- "when": 1755775437391,
- "tag": "0001_far_frank_castle",
- "breakpoints": true
- },
- {
- "idx": 2,
- "version": "6",
- "when": 1756930554198,
- "tag": "0002_cheerful_randall",
- "breakpoints": true
- },
- {
- "idx": 3,
- "version": "6",
- "when": 1758653407064,
- "tag": "0003_mature_hellcat",
- "breakpoints": true
- },
- {
- "idx": 4,
- "version": "6",
- "when": 1758961535488,
- "tag": "0004_wealthy_tomas",
- "breakpoints": true
- },
- {
- "idx": 5,
- "version": "6",
- "when": 1759416698274,
- "tag": "0005_simple_alice",
- "breakpoints": true
- },
- {
- "idx": 6,
- "version": "6",
- "when": 1760734377440,
- "tag": "0006_secret_micromacro",
- "breakpoints": true
- },
- {
- "idx": 7,
- "version": "6",
- "when": 1761224911352,
- "tag": "0007_watery_sersi",
- "breakpoints": true
- },
- {
- "idx": 8,
- "version": "6",
- "when": 1761414054481,
- "tag": "0008_silent_lady_bullseye",
- "breakpoints": true
- },
- {
- "idx": 9,
- "version": "6",
- "when": 1762095226041,
- "tag": "0009_little_adam_warlock",
- "breakpoints": true
- },
- {
- "idx": 10,
- "version": "6",
- "when": 1762610065889,
- "tag": "0010_perfect_proemial_gods",
- "breakpoints": true
- },
- {
- "idx": 11,
- "version": "6",
- "when": 1763644043601,
- "tag": "0011_familiar_stone_men",
- "breakpoints": true
- },
- {
- "idx": 12,
- "version": "6",
- "when": 1764100562084,
- "tag": "0012_add_short_ids",
- "breakpoints": true
- },
- {
- "idx": 13,
- "version": "6",
- "when": 1764182159797,
- "tag": "0013_elite_sprite",
- "breakpoints": true
- },
- {
- "idx": 14,
- "version": "6",
- "when": 1764182405089,
- "tag": "0014_wild_echo",
- "breakpoints": true
- },
- {
- "idx": 15,
- "version": "6",
- "when": 1764182465287,
- "tag": "0015_jazzy_sersi",
- "breakpoints": true
- },
- {
- "idx": 16,
- "version": "6",
- "when": 1764194697035,
- "tag": "0016_fix-timestamps-to-ms",
- "breakpoints": true
- },
- {
- "idx": 17,
- "version": "6",
- "when": 1764357897219,
- "tag": "0017_fix-compression-modes",
- "breakpoints": true
- }
- ]
-}
\ No newline at end of file
+ "version": "7",
+ "dialect": "sqlite",
+ "entries": [
+ {
+ "idx": 0,
+ "version": "6",
+ "when": 1755765658194,
+ "tag": "0000_known_madelyne_pryor",
+ "breakpoints": true
+ },
+ {
+ "idx": 1,
+ "version": "6",
+ "when": 1755775437391,
+ "tag": "0001_far_frank_castle",
+ "breakpoints": true
+ },
+ {
+ "idx": 2,
+ "version": "6",
+ "when": 1756930554198,
+ "tag": "0002_cheerful_randall",
+ "breakpoints": true
+ },
+ {
+ "idx": 3,
+ "version": "6",
+ "when": 1758653407064,
+ "tag": "0003_mature_hellcat",
+ "breakpoints": true
+ },
+ {
+ "idx": 4,
+ "version": "6",
+ "when": 1758961535488,
+ "tag": "0004_wealthy_tomas",
+ "breakpoints": true
+ },
+ {
+ "idx": 5,
+ "version": "6",
+ "when": 1759416698274,
+ "tag": "0005_simple_alice",
+ "breakpoints": true
+ },
+ {
+ "idx": 6,
+ "version": "6",
+ "when": 1760734377440,
+ "tag": "0006_secret_micromacro",
+ "breakpoints": true
+ },
+ {
+ "idx": 7,
+ "version": "6",
+ "when": 1761224911352,
+ "tag": "0007_watery_sersi",
+ "breakpoints": true
+ },
+ {
+ "idx": 8,
+ "version": "6",
+ "when": 1761414054481,
+ "tag": "0008_silent_lady_bullseye",
+ "breakpoints": true
+ },
+ {
+ "idx": 9,
+ "version": "6",
+ "when": 1762095226041,
+ "tag": "0009_little_adam_warlock",
+ "breakpoints": true
+ },
+ {
+ "idx": 10,
+ "version": "6",
+ "when": 1762610065889,
+ "tag": "0010_perfect_proemial_gods",
+ "breakpoints": true
+ },
+ {
+ "idx": 11,
+ "version": "6",
+ "when": 1763644043601,
+ "tag": "0011_familiar_stone_men",
+ "breakpoints": true
+ },
+ {
+ "idx": 12,
+ "version": "6",
+ "when": 1764100562084,
+ "tag": "0012_add_short_ids",
+ "breakpoints": true
+ },
+ {
+ "idx": 13,
+ "version": "6",
+ "when": 1764182159797,
+ "tag": "0013_elite_sprite",
+ "breakpoints": true
+ },
+ {
+ "idx": 14,
+ "version": "6",
+ "when": 1764182405089,
+ "tag": "0014_wild_echo",
+ "breakpoints": true
+ },
+ {
+ "idx": 15,
+ "version": "6",
+ "when": 1764182465287,
+ "tag": "0015_jazzy_sersi",
+ "breakpoints": true
+ },
+ {
+ "idx": 16,
+ "version": "6",
+ "when": 1764194697035,
+ "tag": "0016_fix-timestamps-to-ms",
+ "breakpoints": true
+ },
+ {
+ "idx": 17,
+ "version": "6",
+ "when": 1764357897219,
+ "tag": "0017_fix-compression-modes",
+ "breakpoints": true
+ }
+ ]
+}
diff --git a/app/routes.ts b/app/routes.ts
index a7a2eb3..6880e2d 100644
--- a/app/routes.ts
+++ b/app/routes.ts
@@ -12,10 +12,12 @@ export default [
route("backups", "./client/modules/backups/routes/backups.tsx"),
route("backups/create", "./client/modules/backups/routes/create-backup.tsx"),
route("backups/:id", "./client/modules/backups/routes/backup-details.tsx"),
+ route("backups/:id/:snapshotId/restore", "./client/modules/backups/routes/restore-snapshot.tsx"),
route("repositories", "./client/modules/repositories/routes/repositories.tsx"),
route("repositories/create", "./client/modules/repositories/routes/create-repository.tsx"),
route("repositories/:name", "./client/modules/repositories/routes/repository-details.tsx"),
route("repositories/:name/:snapshotId", "./client/modules/repositories/routes/snapshot-details.tsx"),
+ route("repositories/:name/:snapshotId/restore", "./client/modules/repositories/routes/restore-snapshot.tsx"),
route("notifications", "./client/modules/notifications/routes/notifications.tsx"),
route("notifications/create", "./client/modules/notifications/routes/create-notification.tsx"),
route("notifications/:id", "./client/modules/notifications/routes/notification-details.tsx"),
diff --git a/app/server/modules/auth/auth.controller.ts b/app/server/modules/auth/auth.controller.ts
index d6252aa..c78d281 100644
--- a/app/server/modules/auth/auth.controller.ts
+++ b/app/server/modules/auth/auth.controller.ts
@@ -21,7 +21,6 @@ import {
} from "./auth.dto";
import { authService } from "./auth.service";
import { toMessage } from "../../utils/errors";
-import { logger } from "~/server/utils/logger";
const COOKIE_NAME = "session_id";
const COOKIE_OPTIONS = {
diff --git a/app/server/modules/lifecycle/checkpoint.ts b/app/server/modules/lifecycle/checkpoint.ts
index 4fbde5d..d9f80a2 100644
--- a/app/server/modules/lifecycle/checkpoint.ts
+++ b/app/server/modules/lifecycle/checkpoint.ts
@@ -2,7 +2,6 @@ import { eq, sql } from "drizzle-orm";
import { db } from "../../db/db";
import { appMetadataTable, usersTable } from "../../db/schema";
import { logger } from "../../utils/logger";
-import { REQUIRED_MIGRATIONS } from "~/server/core/constants";
const MIGRATION_KEY_PREFIX = "migration:";