refactor: API route for backup/restore

This commit is contained in:
charlesgauthereau
2026-01-17 17:27:33 +01:00
parent 0216c40bfe
commit 7d818206b0
14 changed files with 415 additions and 237 deletions
@@ -3,7 +3,6 @@
import {useEffect, useState} from "react";
import {useMutation} from "@tanstack/react-query";
import {toast} from "sonner";
import {FormControl, FormField, FormItem, FormLabel, FormMessage, useZodForm} from "@/components/ui/form";
import {Input} from "@/components/ui/input";
import {Form} from "@/components/ui/form";
@@ -1,21 +1,25 @@
"use client";
import {useRouter} from "next/navigation";
import {Info} from "lucide-react";
import {useMutation} from "@tanstack/react-query";
import {toast} from "sonner";
import {Card, CardContent, CardHeader} from "@/components/ui/card";
import {FormControl, FormField, FormItem, FormLabel, FormMessage, useZodForm} from "@/components/ui/form";
import {Input} from "@/components/ui/input";
import {CardContent, CardHeader} from "@/components/ui/card";
import {Form} from "@/components/ui/form";
import {Button} from "@/components/ui/button";
import {TooltipProvider, TooltipTrigger, Tooltip, TooltipContent} from "@/components/ui/tooltip";
import {RegisterSchema, RegisterType} from "@/components/wrappers/auth/register/register-form/register-form.schema";
import {PasswordInput} from "@/components/ui/password-input";
import {signUp} from "@/lib/auth/auth-client";
import Link from "next/link";
import {useZodForm} from "@/components/ui/form";
import {CardAuth} from "@/features/layout/card-auth";
import {FormField} from "@/components/ui/form";
import {FormItem} from "@/components/ui/form";
import {FormLabel} from "@/components/ui/form";
import {FormControl} from "@/components/ui/form";
import {FormMessage} from "@/components/ui/form";
import {Input} from "@/components/ui/input";
import {PasswordInput} from "@/components/ui/password-input";
import {Button} from "@/components/ui/button";
import Link from "next/link";
import {Info} from "lucide-react";
export type registerFormProps = {
defaultValues?: RegisterType;
@@ -26,9 +30,11 @@ export const RegisterForm = (props: registerFormProps) => {
const form = useZodForm({
schema: RegisterSchema,
});
const router = useRouter();
const mutation = useMutation({
mutationFn: async (values: RegisterType) => {
// @ts-ignore
await signUp.email(values, {
onSuccess: () => {
toast.success(`Account successfully created`);
@@ -1,5 +1,5 @@
"use client"
import {BackupWith} from "@/db/schema/07_database";
import {BackupWith, Restoration} from "@/db/schema/07_database";
import React, {useState} from "react";
import {Swiper, SwiperSlide} from "swiper/react";
@@ -7,7 +7,7 @@ import "swiper/css";
import "swiper/css/pagination";
import {Pagination, Mousewheel} from "swiper/modules";
import {DatabaseActionKind} from "@/components/wrappers/dashboard/database/backup/backup-modal-context";
import {DatabaseActionKind, useBackupModal} from "@/components/wrappers/dashboard/database/backup/backup-modal-context";
import {Form, FormControl, FormField, FormItem, FormLabel, FormMessage, useZodForm} from "@/components/ui/form";
import {
BackupActionsSchema,
@@ -22,8 +22,14 @@ import {truncateWords} from "@/utils/text";
import {useIsMobile} from "@/hooks/use-mobile";
import {Badge} from "@/components/ui/badge";
import {getStatusColor, getStatusIcon} from "@/components/wrappers/dashboard/admin/notifications/logs/columns";
import {downloadBackupAction} from "@/components/wrappers/dashboard/database/backup/actions/backup-actions.action";
import {
createRestorationBackupAction,
downloadBackupAction
} from "@/components/wrappers/dashboard/database/backup/actions/backup-actions.action";
import {toast} from "sonner";
import {SafeActionResult} from "next-safe-action";
import {ServerActionResult} from "@/types/action-type";
import {ZodString} from "zod";
type BackupActionsFormProps = {
backup: BackupWith;
@@ -32,6 +38,7 @@ type BackupActionsFormProps = {
export const BackupActionsForm = ({backup, action}: BackupActionsFormProps) => {
const isMobile = useIsMobile();
const {closeModal} = useBackupModal();
const form = useZodForm({
schema: BackupActionsSchema,
@@ -39,18 +46,38 @@ export const BackupActionsForm = ({backup, action}: BackupActionsFormProps) => {
const mutation = useMutation({
mutationFn: async (values: BackupActionsType) => {
// implement your mutation logic here
console.log(values);
const result = await downloadBackupAction({backupStorageId: values.backupStorageId})
let result: SafeActionResult<string, ZodString, readonly [], {
_errors?: string[] | undefined;
}, readonly [], ServerActionResult<string | Restoration>, object> | undefined
if (action === "download") {
result = await downloadBackupAction({backupStorageId: values.backupStorageId})
} else if (action === "restore") {
result = await createRestorationBackupAction({
databaseId: backup.databaseId,
backupStorageId: values.backupStorageId,
backupId: backup.id
})
}
const inner = result?.data;
console.log(inner);
if (inner?.success) {
toast.success(inner.actionSuccess?.message);
if (action === "download") {
console.log(inner.value)
const url = inner.value
if (typeof url === "string") {
window.open(url, "_self");
}
closeModal()
} else if (action === "restore") {
closeModal()
}
} else {
toast.error(inner?.actionError?.message);
}
@@ -146,7 +173,8 @@ export const BackupActionsForm = ({backup, action}: BackupActionsFormProps) => {
)}
/>
<div className="flex flex-col items-center gap-y-6 w-full">
<ButtonWithLoading className="mt-2 w-full" isPending={mutation.isPending} disabled={mutation.isPending }>
<ButtonWithLoading className="mt-2 w-full" isPending={mutation.isPending}
disabled={mutation.isPending}>
Confirm
</ButtonWithLoading>
</div>
@@ -7,6 +7,7 @@ import {dispatchStorage} from "@/features/storages/dispatch";
import {db} from "@/db";
import {eq} from "drizzle-orm";
import * as drizzleDb from "@/db";
import {Restoration} from "@/db/schema/07_database";
export const downloadBackupAction = userAction.schema(
@@ -32,9 +33,6 @@ export const downloadBackupAction = userAction.schema(
},
};
}
console.log(backupStorage);
if (backupStorage.status != "success" || !backupStorage.path) {
return {
success: false,
@@ -56,8 +54,6 @@ export const downloadBackupAction = userAction.schema(
const result = await dispatchStorage(input, undefined, backupStorage.storageChannelId);
console.log(result);
return {
success: true,
value: result.url,
@@ -78,4 +74,49 @@ export const downloadBackupAction = userAction.schema(
},
};
}
});
});
export const createRestorationBackupAction = userAction
.schema(
z.object({
backupId: z.string(),
databaseId: z.string(),
backupStorageId: z.string(),
})
)
.action(async ({parsedInput}): Promise<ServerActionResult<Restoration>> => {
try {
const restorationData = await db
.insert(drizzleDb.schemas.restoration)
.values({
databaseId: parsedInput.databaseId,
backupId: parsedInput.backupId,
backupStorageId: parsedInput.backupStorageId,
status: "waiting",
})
.returning()
.execute();
const createdRestoration = restorationData[0];
return {
success: true,
value: createdRestoration,
actionSuccess: {
message: "Restoration has been successfully created.",
messageParams: {restorationId: createdRestoration.id},
},
};
} catch (error) {
return {
success: false,
actionError: {
message: "Failed to create restoration.",
status: 500,
cause: error instanceof Error ? error.message : "Unknown error",
messageParams: {message: "Error creating the restoration"},
},
};
}
});