refactor: create volume & restore snapshot
This commit is contained in:
parent
8a25b79377
commit
e424009dd3
11 changed files with 204 additions and 126 deletions
|
|
@ -1,6 +1,5 @@
|
|||
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";
|
||||
|
|
@ -16,6 +15,7 @@ import { useFileBrowser } from "~/client/hooks/use-file-browser";
|
|||
import { OVERWRITE_MODES, type OverwriteMode } from "~/schemas/restic";
|
||||
import type { Repository, Snapshot } from "~/client/lib/types";
|
||||
import { handleRepositoryError } from "~/client/lib/errors";
|
||||
import { useNavigate } from "@tanstack/react-router";
|
||||
|
||||
type RestoreLocation = "original" | "custom";
|
||||
|
||||
|
|
@ -101,7 +101,7 @@ export function RestoreForm({ snapshot, repository, snapshotId, returnPath }: Re
|
|||
...restoreSnapshotMutation(),
|
||||
onSuccess: () => {
|
||||
toast.success("Restore completed");
|
||||
void navigate(returnPath);
|
||||
void navigate({ to: returnPath });
|
||||
},
|
||||
onError: (error) => {
|
||||
handleRepositoryError("Restore failed", error, repository.id);
|
||||
|
|
@ -156,7 +156,7 @@ export function RestoreForm({ snapshot, repository, snapshotId, returnPath }: Re
|
|||
</p>
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<Button variant="outline" onClick={() => navigate(returnPath)}>
|
||||
<Button variant="outline" onClick={() => navigate({ to: returnPath })}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button variant="primary" onClick={handleRestore} disabled={isRestoring || !canRestore}>
|
||||
|
|
|
|||
|
|
@ -1,62 +0,0 @@
|
|||
import { redirect } from "react-router";
|
||||
import { getBackupSchedule, getRepository, 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 [snapshot, repository] = await Promise.all([
|
||||
getSnapshotDetails({
|
||||
path: {
|
||||
id: schedule.data.repositoryId,
|
||||
snapshotId: params.snapshotId,
|
||||
},
|
||||
}),
|
||||
getRepository({ path: { id: schedule.data.repositoryId } }),
|
||||
]);
|
||||
|
||||
if (!snapshot.data) return redirect(`/backups/${params.id}`);
|
||||
if (!repository.data) return redirect(`/backups/${params.id}`);
|
||||
|
||||
return {
|
||||
snapshot: snapshot.data,
|
||||
repository: repository.data,
|
||||
snapshotId: params.snapshotId,
|
||||
backupId: params.id,
|
||||
};
|
||||
};
|
||||
|
||||
export default function RestoreSnapshotFromBackupPage({ loaderData }: Route.ComponentProps) {
|
||||
const { snapshot, repository, snapshotId, backupId } = loaderData;
|
||||
|
||||
return (
|
||||
<RestoreForm
|
||||
snapshot={snapshot}
|
||||
repository={repository}
|
||||
snapshotId={snapshotId}
|
||||
returnPath={`/backups/${backupId}`}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,48 +1,24 @@
|
|||
import { redirect } from "react-router";
|
||||
import { getRepository, getSnapshotDetails } from "~/client/api-client";
|
||||
import { RestoreForm } from "~/client/components/restore-form";
|
||||
import type { Route } from "./+types/restore-snapshot";
|
||||
import type { Repository, Snapshot } from "~/client/lib/types";
|
||||
|
||||
export const handle = {
|
||||
breadcrumb: (match: Route.MetaArgs) => [
|
||||
{ label: "Repositories", href: "/repositories" },
|
||||
{ label: match.loaderData?.repository.name || match.params.id, href: `/repositories/${match.params.id}` },
|
||||
{ label: match.params.snapshotId, href: `/repositories/${match.params.id}/${match.params.snapshotId}` },
|
||||
{ label: "Restore" },
|
||||
],
|
||||
// export const handle = {
|
||||
// breadcrumb: (match: Route.MetaArgs) => [
|
||||
// { label: "Repositories", href: "/repositories" },
|
||||
// { label: match.loaderData?.repository.name || match.params.id, href: `/repositories/${match.params.id}` },
|
||||
// { label: match.params.snapshotId, href: `/repositories/${match.params.id}/${match.params.snapshotId}` },
|
||||
// { label: "Restore" },
|
||||
// ],
|
||||
// };
|
||||
|
||||
type Props = {
|
||||
snapshot: Snapshot;
|
||||
repository: Repository;
|
||||
snapshotId: string;
|
||||
returnPath: string;
|
||||
};
|
||||
|
||||
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, repository] = await Promise.all([
|
||||
getSnapshotDetails({ path: { id: params.id, snapshotId: params.snapshotId } }),
|
||||
getRepository({ path: { id: params.id } }),
|
||||
]);
|
||||
|
||||
if (!snapshot.data) return redirect("/repositories");
|
||||
if (!repository.data) return redirect(`/repositories`);
|
||||
|
||||
return { snapshot: snapshot.data, id: params.id, repository: repository.data, snapshotId: params.snapshotId };
|
||||
};
|
||||
|
||||
export default function RestoreSnapshotPage({ loaderData }: Route.ComponentProps) {
|
||||
const { snapshot, id, snapshotId, repository } = loaderData;
|
||||
|
||||
return (
|
||||
<RestoreForm
|
||||
snapshot={snapshot}
|
||||
repository={repository}
|
||||
snapshotId={snapshotId}
|
||||
returnPath={`/repositories/${id}/${snapshotId}`}
|
||||
/>
|
||||
);
|
||||
export function RestoreSnapshotPage(props: Props) {
|
||||
const { snapshot, returnPath, snapshotId, repository } = props;
|
||||
|
||||
return <RestoreForm snapshot={snapshot} repository={repository} snapshotId={snapshotId} returnPath={returnPath} />;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,31 +1,20 @@
|
|||
import { useMutation } from "@tanstack/react-query";
|
||||
import { HardDrive, Plus } from "lucide-react";
|
||||
import { useId } from "react";
|
||||
import { useNavigate } from "react-router";
|
||||
import { toast } from "sonner";
|
||||
import { createVolumeMutation } from "~/client/api-client/@tanstack/react-query.gen";
|
||||
import { CreateVolumeForm, type FormValues } from "~/client/modules/volumes/components/create-volume-form";
|
||||
import { Button } from "~/client/components/ui/button";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "~/client/components/ui/card";
|
||||
import { parseError } from "~/client/lib/errors";
|
||||
import type { Route } from "./+types/create-volume";
|
||||
import { Alert, AlertDescription } from "~/client/components/ui/alert";
|
||||
import { useNavigate } from "@tanstack/react-router";
|
||||
|
||||
export const handle = {
|
||||
breadcrumb: () => [{ label: "Volumes", href: "/volumes" }, { label: "Create" }],
|
||||
};
|
||||
|
||||
export function meta(_: Route.MetaArgs) {
|
||||
return [
|
||||
{ title: "Zerobyte - Create Volume" },
|
||||
{
|
||||
name: "description",
|
||||
content: "Create a new storage volume with automatic mounting and health checks.",
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
export default function CreateVolume() {
|
||||
export function CreateVolumePage() {
|
||||
const navigate = useNavigate();
|
||||
const formId = useId();
|
||||
|
||||
|
|
@ -33,7 +22,7 @@ export default function CreateVolume() {
|
|||
...createVolumeMutation(),
|
||||
onSuccess: (data) => {
|
||||
toast.success("Volume created successfully");
|
||||
void navigate(`/volumes/${data.shortId}`);
|
||||
void navigate({ to: `/volumes/${data.shortId}` });
|
||||
},
|
||||
});
|
||||
|
||||
|
|
@ -69,7 +58,7 @@ export default function CreateVolume() {
|
|||
)}
|
||||
<CreateVolumeForm mode="create" formId={formId} onSubmit={handleSubmit} loading={createVolume.isPending} />
|
||||
<div className="flex justify-end gap-2 pt-4 border-t">
|
||||
<Button type="button" variant="secondary" onClick={() => navigate("/volumes")}>
|
||||
<Button type="button" variant="secondary" onClick={() => navigate({ to: "/volumes" })}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button type="submit" form={formId} loading={createVolume.isPending}>
|
||||
|
|
|
|||
|
|
@ -17,12 +17,15 @@ import { Route as authDownloadRecoveryKeyRouteImport } from './routes/(auth)/dow
|
|||
import { Route as dashboardVolumesIndexRouteImport } from './routes/(dashboard)/volumes/index'
|
||||
import { Route as dashboardRepositoriesIndexRouteImport } from './routes/(dashboard)/repositories/index'
|
||||
import { Route as dashboardBackupsIndexRouteImport } from './routes/(dashboard)/backups/index'
|
||||
import { Route as dashboardVolumesCreateRouteImport } from './routes/(dashboard)/volumes/create'
|
||||
import { Route as dashboardVolumesVolumeIdRouteImport } from './routes/(dashboard)/volumes/$volumeId'
|
||||
import { Route as dashboardRepositoriesCreateRouteImport } from './routes/(dashboard)/repositories/create'
|
||||
import { Route as dashboardRepositoriesRepositoryIdRouteImport } from './routes/(dashboard)/repositories/$repositoryId'
|
||||
import { Route as dashboardBackupsCreateRouteImport } from './routes/(dashboard)/backups/create'
|
||||
import { Route as dashboardBackupsScheduleIdRouteImport } from './routes/(dashboard)/backups/$scheduleId'
|
||||
import { Route as dashboardRepositoriesRepoIdSnapshotIdRouteImport } from './routes/(dashboard)/repositories/$repoId.$snapshotId'
|
||||
import { Route as dashboardRepositoriesRepoIdSnapIdRestoreRouteImport } from './routes/(dashboard)/repositories/$repoId.$snapId.restore'
|
||||
import { Route as dashboardBackupsBackupIdSnapshotIdRestoreRouteImport } from './routes/(dashboard)/backups/$backupId.$snapshotId.restore'
|
||||
|
||||
const dashboardRouteRoute = dashboardRouteRouteImport.update({
|
||||
id: '/(dashboard)',
|
||||
|
|
@ -64,6 +67,11 @@ const dashboardBackupsIndexRoute = dashboardBackupsIndexRouteImport.update({
|
|||
path: '/backups/',
|
||||
getParentRoute: () => dashboardRouteRoute,
|
||||
} as any)
|
||||
const dashboardVolumesCreateRoute = dashboardVolumesCreateRouteImport.update({
|
||||
id: '/volumes/create',
|
||||
path: '/volumes/create',
|
||||
getParentRoute: () => dashboardRouteRoute,
|
||||
} as any)
|
||||
const dashboardVolumesVolumeIdRoute =
|
||||
dashboardVolumesVolumeIdRouteImport.update({
|
||||
id: '/volumes/$volumeId',
|
||||
|
|
@ -99,6 +107,18 @@ const dashboardRepositoriesRepoIdSnapshotIdRoute =
|
|||
path: '/repositories/$repoId/$snapshotId',
|
||||
getParentRoute: () => dashboardRouteRoute,
|
||||
} as any)
|
||||
const dashboardRepositoriesRepoIdSnapIdRestoreRoute =
|
||||
dashboardRepositoriesRepoIdSnapIdRestoreRouteImport.update({
|
||||
id: '/repositories/$repoId/$snapId/restore',
|
||||
path: '/repositories/$repoId/$snapId/restore',
|
||||
getParentRoute: () => dashboardRouteRoute,
|
||||
} as any)
|
||||
const dashboardBackupsBackupIdSnapshotIdRestoreRoute =
|
||||
dashboardBackupsBackupIdSnapshotIdRestoreRouteImport.update({
|
||||
id: '/backups/$backupId/$snapshotId/restore',
|
||||
path: '/backups/$backupId/$snapshotId/restore',
|
||||
getParentRoute: () => dashboardRouteRoute,
|
||||
} as any)
|
||||
|
||||
export interface FileRoutesByFullPath {
|
||||
'/': typeof IndexRoute
|
||||
|
|
@ -110,10 +130,13 @@ export interface FileRoutesByFullPath {
|
|||
'/repositories/$repositoryId': typeof dashboardRepositoriesRepositoryIdRoute
|
||||
'/repositories/create': typeof dashboardRepositoriesCreateRoute
|
||||
'/volumes/$volumeId': typeof dashboardVolumesVolumeIdRoute
|
||||
'/volumes/create': typeof dashboardVolumesCreateRoute
|
||||
'/backups/': typeof dashboardBackupsIndexRoute
|
||||
'/repositories/': typeof dashboardRepositoriesIndexRoute
|
||||
'/volumes/': typeof dashboardVolumesIndexRoute
|
||||
'/repositories/$repoId/$snapshotId': typeof dashboardRepositoriesRepoIdSnapshotIdRoute
|
||||
'/backups/$backupId/$snapshotId/restore': typeof dashboardBackupsBackupIdSnapshotIdRestoreRoute
|
||||
'/repositories/$repoId/$snapId/restore': typeof dashboardRepositoriesRepoIdSnapIdRestoreRoute
|
||||
}
|
||||
export interface FileRoutesByTo {
|
||||
'/': typeof IndexRoute
|
||||
|
|
@ -125,10 +148,13 @@ export interface FileRoutesByTo {
|
|||
'/repositories/$repositoryId': typeof dashboardRepositoriesRepositoryIdRoute
|
||||
'/repositories/create': typeof dashboardRepositoriesCreateRoute
|
||||
'/volumes/$volumeId': typeof dashboardVolumesVolumeIdRoute
|
||||
'/volumes/create': typeof dashboardVolumesCreateRoute
|
||||
'/backups': typeof dashboardBackupsIndexRoute
|
||||
'/repositories': typeof dashboardRepositoriesIndexRoute
|
||||
'/volumes': typeof dashboardVolumesIndexRoute
|
||||
'/repositories/$repoId/$snapshotId': typeof dashboardRepositoriesRepoIdSnapshotIdRoute
|
||||
'/backups/$backupId/$snapshotId/restore': typeof dashboardBackupsBackupIdSnapshotIdRestoreRoute
|
||||
'/repositories/$repoId/$snapId/restore': typeof dashboardRepositoriesRepoIdSnapIdRestoreRoute
|
||||
}
|
||||
export interface FileRoutesById {
|
||||
__root__: typeof rootRouteImport
|
||||
|
|
@ -142,10 +168,13 @@ export interface FileRoutesById {
|
|||
'/(dashboard)/repositories/$repositoryId': typeof dashboardRepositoriesRepositoryIdRoute
|
||||
'/(dashboard)/repositories/create': typeof dashboardRepositoriesCreateRoute
|
||||
'/(dashboard)/volumes/$volumeId': typeof dashboardVolumesVolumeIdRoute
|
||||
'/(dashboard)/volumes/create': typeof dashboardVolumesCreateRoute
|
||||
'/(dashboard)/backups/': typeof dashboardBackupsIndexRoute
|
||||
'/(dashboard)/repositories/': typeof dashboardRepositoriesIndexRoute
|
||||
'/(dashboard)/volumes/': typeof dashboardVolumesIndexRoute
|
||||
'/(dashboard)/repositories/$repoId/$snapshotId': typeof dashboardRepositoriesRepoIdSnapshotIdRoute
|
||||
'/(dashboard)/backups/$backupId/$snapshotId/restore': typeof dashboardBackupsBackupIdSnapshotIdRestoreRoute
|
||||
'/(dashboard)/repositories/$repoId/$snapId/restore': typeof dashboardRepositoriesRepoIdSnapIdRestoreRoute
|
||||
}
|
||||
export interface FileRouteTypes {
|
||||
fileRoutesByFullPath: FileRoutesByFullPath
|
||||
|
|
@ -159,10 +188,13 @@ export interface FileRouteTypes {
|
|||
| '/repositories/$repositoryId'
|
||||
| '/repositories/create'
|
||||
| '/volumes/$volumeId'
|
||||
| '/volumes/create'
|
||||
| '/backups/'
|
||||
| '/repositories/'
|
||||
| '/volumes/'
|
||||
| '/repositories/$repoId/$snapshotId'
|
||||
| '/backups/$backupId/$snapshotId/restore'
|
||||
| '/repositories/$repoId/$snapId/restore'
|
||||
fileRoutesByTo: FileRoutesByTo
|
||||
to:
|
||||
| '/'
|
||||
|
|
@ -174,10 +206,13 @@ export interface FileRouteTypes {
|
|||
| '/repositories/$repositoryId'
|
||||
| '/repositories/create'
|
||||
| '/volumes/$volumeId'
|
||||
| '/volumes/create'
|
||||
| '/backups'
|
||||
| '/repositories'
|
||||
| '/volumes'
|
||||
| '/repositories/$repoId/$snapshotId'
|
||||
| '/backups/$backupId/$snapshotId/restore'
|
||||
| '/repositories/$repoId/$snapId/restore'
|
||||
id:
|
||||
| '__root__'
|
||||
| '/'
|
||||
|
|
@ -190,10 +225,13 @@ export interface FileRouteTypes {
|
|||
| '/(dashboard)/repositories/$repositoryId'
|
||||
| '/(dashboard)/repositories/create'
|
||||
| '/(dashboard)/volumes/$volumeId'
|
||||
| '/(dashboard)/volumes/create'
|
||||
| '/(dashboard)/backups/'
|
||||
| '/(dashboard)/repositories/'
|
||||
| '/(dashboard)/volumes/'
|
||||
| '/(dashboard)/repositories/$repoId/$snapshotId'
|
||||
| '/(dashboard)/backups/$backupId/$snapshotId/restore'
|
||||
| '/(dashboard)/repositories/$repoId/$snapId/restore'
|
||||
fileRoutesById: FileRoutesById
|
||||
}
|
||||
export interface RootRouteChildren {
|
||||
|
|
@ -262,6 +300,13 @@ declare module '@tanstack/react-router' {
|
|||
preLoaderRoute: typeof dashboardBackupsIndexRouteImport
|
||||
parentRoute: typeof dashboardRouteRoute
|
||||
}
|
||||
'/(dashboard)/volumes/create': {
|
||||
id: '/(dashboard)/volumes/create'
|
||||
path: '/volumes/create'
|
||||
fullPath: '/volumes/create'
|
||||
preLoaderRoute: typeof dashboardVolumesCreateRouteImport
|
||||
parentRoute: typeof dashboardRouteRoute
|
||||
}
|
||||
'/(dashboard)/volumes/$volumeId': {
|
||||
id: '/(dashboard)/volumes/$volumeId'
|
||||
path: '/volumes/$volumeId'
|
||||
|
|
@ -304,6 +349,20 @@ declare module '@tanstack/react-router' {
|
|||
preLoaderRoute: typeof dashboardRepositoriesRepoIdSnapshotIdRouteImport
|
||||
parentRoute: typeof dashboardRouteRoute
|
||||
}
|
||||
'/(dashboard)/repositories/$repoId/$snapId/restore': {
|
||||
id: '/(dashboard)/repositories/$repoId/$snapId/restore'
|
||||
path: '/repositories/$repoId/$snapId/restore'
|
||||
fullPath: '/repositories/$repoId/$snapId/restore'
|
||||
preLoaderRoute: typeof dashboardRepositoriesRepoIdSnapIdRestoreRouteImport
|
||||
parentRoute: typeof dashboardRouteRoute
|
||||
}
|
||||
'/(dashboard)/backups/$backupId/$snapshotId/restore': {
|
||||
id: '/(dashboard)/backups/$backupId/$snapshotId/restore'
|
||||
path: '/backups/$backupId/$snapshotId/restore'
|
||||
fullPath: '/backups/$backupId/$snapshotId/restore'
|
||||
preLoaderRoute: typeof dashboardBackupsBackupIdSnapshotIdRestoreRouteImport
|
||||
parentRoute: typeof dashboardRouteRoute
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -313,10 +372,13 @@ interface dashboardRouteRouteChildren {
|
|||
dashboardRepositoriesRepositoryIdRoute: typeof dashboardRepositoriesRepositoryIdRoute
|
||||
dashboardRepositoriesCreateRoute: typeof dashboardRepositoriesCreateRoute
|
||||
dashboardVolumesVolumeIdRoute: typeof dashboardVolumesVolumeIdRoute
|
||||
dashboardVolumesCreateRoute: typeof dashboardVolumesCreateRoute
|
||||
dashboardBackupsIndexRoute: typeof dashboardBackupsIndexRoute
|
||||
dashboardRepositoriesIndexRoute: typeof dashboardRepositoriesIndexRoute
|
||||
dashboardVolumesIndexRoute: typeof dashboardVolumesIndexRoute
|
||||
dashboardRepositoriesRepoIdSnapshotIdRoute: typeof dashboardRepositoriesRepoIdSnapshotIdRoute
|
||||
dashboardBackupsBackupIdSnapshotIdRestoreRoute: typeof dashboardBackupsBackupIdSnapshotIdRestoreRoute
|
||||
dashboardRepositoriesRepoIdSnapIdRestoreRoute: typeof dashboardRepositoriesRepoIdSnapIdRestoreRoute
|
||||
}
|
||||
|
||||
const dashboardRouteRouteChildren: dashboardRouteRouteChildren = {
|
||||
|
|
@ -326,11 +388,16 @@ const dashboardRouteRouteChildren: dashboardRouteRouteChildren = {
|
|||
dashboardRepositoriesRepositoryIdRoute,
|
||||
dashboardRepositoriesCreateRoute: dashboardRepositoriesCreateRoute,
|
||||
dashboardVolumesVolumeIdRoute: dashboardVolumesVolumeIdRoute,
|
||||
dashboardVolumesCreateRoute: dashboardVolumesCreateRoute,
|
||||
dashboardBackupsIndexRoute: dashboardBackupsIndexRoute,
|
||||
dashboardRepositoriesIndexRoute: dashboardRepositoriesIndexRoute,
|
||||
dashboardVolumesIndexRoute: dashboardVolumesIndexRoute,
|
||||
dashboardRepositoriesRepoIdSnapshotIdRoute:
|
||||
dashboardRepositoriesRepoIdSnapshotIdRoute,
|
||||
dashboardBackupsBackupIdSnapshotIdRestoreRoute:
|
||||
dashboardBackupsBackupIdSnapshotIdRestoreRoute,
|
||||
dashboardRepositoriesRepoIdSnapIdRestoreRoute:
|
||||
dashboardRepositoriesRepoIdSnapIdRestoreRoute,
|
||||
}
|
||||
|
||||
const dashboardRouteRouteWithChildren = dashboardRouteRoute._addFileChildren(
|
||||
|
|
|
|||
|
|
@ -0,0 +1,38 @@
|
|||
import { createFileRoute } from "@tanstack/react-router";
|
||||
import { getBackupSchedule } from "~/client/api-client";
|
||||
import { getRepositoryOptions, getSnapshotDetailsOptions } from "~/client/api-client/@tanstack/react-query.gen";
|
||||
import { RestoreSnapshotPage } from "~/client/modules/repositories/routes/restore-snapshot";
|
||||
|
||||
export const Route = createFileRoute("/(dashboard)/backups/$backupId/$snapshotId/restore")({
|
||||
component: RouteComponent,
|
||||
loader: async ({ params, context }) => {
|
||||
const schedule = await getBackupSchedule({ path: { scheduleId: params.backupId } });
|
||||
|
||||
if (!schedule.data) {
|
||||
throw new Response("Not Found", { status: 404 });
|
||||
}
|
||||
|
||||
const [snapshot, repository] = await Promise.all([
|
||||
context.queryClient.ensureQueryData({
|
||||
...getSnapshotDetailsOptions({ path: { id: schedule.data?.repositoryId, snapshotId: params.snapshotId } }),
|
||||
}),
|
||||
context.queryClient.ensureQueryData({ ...getRepositoryOptions({ path: { id: schedule.data?.repositoryId } }) }),
|
||||
]);
|
||||
|
||||
return { snapshot, repository };
|
||||
},
|
||||
});
|
||||
|
||||
function RouteComponent() {
|
||||
const { backupId, snapshotId } = Route.useParams();
|
||||
const { snapshot, repository } = Route.useLoaderData();
|
||||
|
||||
return (
|
||||
<RestoreSnapshotPage
|
||||
returnPath={`/backups/${backupId}`}
|
||||
snapshotId={snapshotId}
|
||||
snapshot={snapshot}
|
||||
repository={repository}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
import { createFileRoute } from "@tanstack/react-router";
|
||||
import { getRepositoryOptions, getSnapshotDetailsOptions } from "~/client/api-client/@tanstack/react-query.gen";
|
||||
import { RestoreSnapshotPage } from "~/client/modules/repositories/routes/restore-snapshot";
|
||||
|
||||
export const Route = createFileRoute("/(dashboard)/repositories/$repoId/$snapId/restore")({
|
||||
component: RouteComponent,
|
||||
errorComponent: (e) => <div>{e.error.message}</div>,
|
||||
loader: async ({ params, context }) => {
|
||||
const [snapshot, repository] = await Promise.all([
|
||||
context.queryClient.ensureQueryData({
|
||||
...getSnapshotDetailsOptions({ path: { id: params.repoId, snapshotId: params.snapId } }),
|
||||
}),
|
||||
context.queryClient.ensureQueryData({ ...getRepositoryOptions({ path: { id: params.repoId } }) }),
|
||||
]);
|
||||
|
||||
return { snapshot, repository };
|
||||
},
|
||||
head: ({ params }) => ({
|
||||
meta: [
|
||||
{ title: `Zerobyte - Restore Snapshot ${params.snapId}` },
|
||||
{
|
||||
name: "description",
|
||||
content: "Restore files from a backup snapshot.",
|
||||
},
|
||||
],
|
||||
}),
|
||||
});
|
||||
|
||||
function RouteComponent() {
|
||||
const { repoId, snapId } = Route.useParams();
|
||||
const { snapshot, repository } = Route.useLoaderData();
|
||||
|
||||
return (
|
||||
<RestoreSnapshotPage
|
||||
returnPath={`/repositories/${repoId}/${snapId}`}
|
||||
snapshot={snapshot}
|
||||
repository={repository}
|
||||
snapshotId={snapId}
|
||||
/>
|
||||
);
|
||||
}
|
||||
19
app/routes/(dashboard)/volumes/create.tsx
Normal file
19
app/routes/(dashboard)/volumes/create.tsx
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
import { createFileRoute } from "@tanstack/react-router";
|
||||
import { CreateVolumePage } from "~/client/modules/volumes/routes/create-volume";
|
||||
|
||||
export const Route = createFileRoute("/(dashboard)/volumes/create")({
|
||||
component: RouteComponent,
|
||||
head: () => ({
|
||||
meta: [
|
||||
{ title: "Zerobyte - Create Volume" },
|
||||
{
|
||||
name: "description",
|
||||
content: "Create a new storage volume with automatic mounting and health checks.",
|
||||
},
|
||||
],
|
||||
}),
|
||||
});
|
||||
|
||||
function RouteComponent() {
|
||||
return <CreateVolumePage />;
|
||||
}
|
||||
|
|
@ -5,6 +5,7 @@ import type { QueryClient } from "@tanstack/react-query";
|
|||
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
|
||||
import { TanStackRouterDevtools } from "@tanstack/react-router-devtools";
|
||||
import { Toaster } from "~/client/components/ui/sonner";
|
||||
import { useServerEvents } from "~/client/hooks/use-server-events";
|
||||
|
||||
export const Route = createRootRouteWithContext<{ queryClient: QueryClient }>()({
|
||||
server: {
|
||||
|
|
@ -38,6 +39,8 @@ export const Route = createRootRouteWithContext<{ queryClient: QueryClient }>()(
|
|||
});
|
||||
|
||||
function RootLayout() {
|
||||
useServerEvents();
|
||||
|
||||
return (
|
||||
<html lang="en">
|
||||
<head>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,16 @@
|
|||
import { createFileRoute, redirect } from "@tanstack/react-router";
|
||||
|
||||
export const Route = createFileRoute("/")({
|
||||
head: () => ({
|
||||
meta: [
|
||||
{ title: "Zerobyte" },
|
||||
{
|
||||
name: "description",
|
||||
content: "Zerobyte - Manage your backups and storage volumes with ease.",
|
||||
},
|
||||
],
|
||||
}),
|
||||
beforeLoad: () => {
|
||||
redirect({ to: "/volumes" });
|
||||
throw redirect({ to: "/volumes" });
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -315,8 +315,6 @@ const getSnapshotDetails = async (id: string, snapshotId: string) => {
|
|||
const organizationId = getOrganizationId();
|
||||
const repository = await findRepository(id);
|
||||
|
||||
console.log("Getting snapshot details for:", id, snapshotId);
|
||||
|
||||
if (!repository) {
|
||||
throw new NotFoundError("Repository not found");
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue