Working on the s3 save dump feature.

This commit is contained in:
charlesgauthereau
2025-08-02 20:13:29 +02:00
parent d13e4b7d40
commit 449c9c2cfe
11 changed files with 213 additions and 55 deletions
+21 -4
View File
@@ -12,18 +12,23 @@ import {
import {Button} from "@/components/ui/button";
import {Download, MoreHorizontal, Trash2} from "lucide-react";
import {ReloadIcon} from "@radix-ui/react-icons";
import {getFileUrlPresignedLocal} from "@/features/upload/private/upload.action";
import {
getFileUrlPresignedLocal,
getFileUrlPresignedS3,
getFileUrlPreSignedS3Action
} from "@/features/upload/private/upload.action";
import {useMutation} from "@tanstack/react-query";
import {createRestorationAction, deleteBackupAction} from "@/features/dashboard/restore/restore.action";
import {toast} from "sonner";
import {useRouter} from "next/navigation";
import {StatusBadge} from "@/components/wrappers/common/status-badge";
import {Backup} from "@/db/schema/06_database";
import {Backup, DatabaseWith} from "@/db/schema/06_database";
import {formatFrenchDate} from "@/utils/date-formatting";
import {TooltipCustom} from "@/components/wrappers/common/tooltip-custom";
import {Setting} from "@/db/schema/00_setting";
export function backupColumns(isAlreadyRestore: boolean): ColumnDef<Backup>[] {
export function backupColumns(isAlreadyRestore: boolean, settings: Setting, database: DatabaseWith): ColumnDef<Backup>[] {
return [
{
@@ -98,7 +103,19 @@ export function backupColumns(isAlreadyRestore: boolean): ColumnDef<Backup>[] {
};
const handleDownload = async (fileName: string) => {
const url = await getFileUrlPresignedLocal(fileName);
let url: string = "";
if (settings.storage == "local") {
url = await getFileUrlPresignedLocal(fileName);
} else if (settings.storage == "s3") {
const data = await getFileUrlPreSignedS3Action(`backups/${database.project?.slug}/${fileName}`)
if (data?.data?.success) {
url = data.data.value ?? "";
} else {
// @ts-ignore
const errorMessage = data?.data?.actionError?.message || "Failed to get file!";
toast.error(errorMessage);
}
}
window.open(url, "_self");
};
+78 -5
View File
@@ -1,16 +1,25 @@
"use server";
import { mkdir, writeFile } from "fs/promises";
import {mkdir, writeFile} from "fs/promises";
import path from "path";
import * as fs from "node:fs";
import { getServerUrl } from "@/utils/get-server-url";
import {getServerUrl} from "@/utils/get-server-url";
import {createPresignedUrlToDownload, saveFileInBucket} from "@/utils/s3-file-management";
import crypto from "crypto";
import {env} from "@/env.mjs";
import {userAction} from "@/safe-actions";
import {z} from "zod";
import {ServerActionResult} from "@/types/action-type";
import {Backup} from "@/db/schema/06_database";
import {db} from "@/db";
import * as drizzleDb from "@/db";
const privateLocalDir = "private/uploads/files/";
const privateS3Dir = "backups/";
export async function uploadLocalPrivate(fileName: string, buffer: any) {
try {
await mkdir(path.join(process.cwd(), privateLocalDir), { recursive: true });
await mkdir(path.join(process.cwd(), privateLocalDir), {recursive: true});
await writeFile(path.join(process.cwd(), privateLocalDir, fileName), buffer);
return {
@@ -24,10 +33,31 @@ export async function uploadLocalPrivate(fileName: string, buffer: any) {
}
}
export async function uploadS3Private(fileName: string, buffer: any, bucketName: string) {
try {
await saveFileInBucket({
bucketName,
fileName: `${privateS3Dir}${fileName}`,
file: buffer,
});
return {
success: true,
filePath: `${privateS3Dir}${fileName}`,
message: "File uploaded successfully",
};
} catch (error) {
console.error("Error occurred:", error);
throw new Error("An error occurred while importing the private file");
}
}
export async function getFileUrlPresignedLocal(fileName: string) {
try {
const filePath = path.join(privateLocalDir, fileName);
await mkdir(path.join(process.cwd(), privateLocalDir), { recursive: true });
await mkdir(path.join(process.cwd(), privateLocalDir), {recursive: true});
if (!fs.existsSync(filePath)) {
console.error("File not found at:", filePath);
@@ -43,3 +73,46 @@ export async function getFileUrlPresignedLocal(fileName: string) {
throw error;
}
}
export async function getFileUrlPresignedS3(fileName: string) {
try {
return await createPresignedUrlToDownload({
bucketName: env.S3_BUCKET_NAME!,
fileName: fileName,
});
} catch (error) {
throw error;
}
}
export const getFileUrlPreSignedS3Action = userAction.schema(z.string()).action(async ({parsedInput}): Promise<ServerActionResult<string>> => {
try {
const url = await createPresignedUrlToDownload({
bucketName: env.S3_BUCKET_NAME!,
fileName: parsedInput,
});
return {
success: true,
value: url,
actionSuccess: {
message: "Successfully get url",
messageParams: {fileName: parsedInput},
},
};
} catch (error) {
console.error("Error creating backup:", error);
return {
success: false,
actionError: {
message: "Failed to create url pre signed s3.",
status: 500,
cause: error instanceof Error ? error.message : "Unknown error",
messageParams: {fileName: parsedInput},
},
};
}
});
+7 -5
View File
@@ -30,11 +30,13 @@ export const uploadImageAction = userAction.schema(z.instanceof(FormData)).actio
let result: void | UploadedObjectInfo;
const bucketName = "public-image-bucket";
if (settings.storage === "local") {
result = await uploadLocal(fileName, buffer);
} else if (settings.storage === "s3") {
result = await uploadS3Compatible(bucketName, fileName, buffer);
}
// TODO : Do not delete
// if (settings.storage === "local") {
// result = await uploadLocal(fileName, buffer);
// } else if (settings.storage === "s3") {
// result = await uploadS3Compatible(bucketName, fileName, buffer);
// }
result = await uploadLocal(fileName, buffer);
const url = getUrl(fileName, settings, bucketName);
console.log(url);