Some fix on the s3 and local methods for the files saving, we have to improve testing. Working on table actions.

This commit is contained in:
charlesgauthereau
2025-08-03 12:17:10 +02:00
parent 449c9c2cfe
commit baeec9f7a0
6 changed files with 422 additions and 271 deletions
+19 -10
View File
@@ -14,7 +14,6 @@ import {Download, MoreHorizontal, Trash2} from "lucide-react";
import {ReloadIcon} from "@radix-ui/react-icons";
import {
getFileUrlPresignedLocal,
getFileUrlPresignedS3,
getFileUrlPreSignedS3Action
} from "@/features/upload/private/upload.action";
import {useMutation} from "@tanstack/react-query";
@@ -26,6 +25,9 @@ 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";
import {SafeActionResult} from "next-safe-action";
import {ZodString} from "zod";
import {ServerActionResult} from "@/types/action-type";
export function backupColumns(isAlreadyRestore: boolean, settings: Setting, database: DatabaseWith): ColumnDef<Backup>[] {
@@ -103,19 +105,26 @@ export function backupColumns(isAlreadyRestore: boolean, settings: Setting, data
};
const handleDownload = async (fileName: string) => {
let url: string = "";
let data: SafeActionResult<string, ZodString, readonly [], {
_errors?: string[] | undefined;
}, readonly [], ServerActionResult<string>, object> | undefined
if (settings.storage == "local") {
url = await getFileUrlPresignedLocal(fileName);
data = 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);
}
data = await getFileUrlPreSignedS3Action(`backups/${database.project?.slug}/${fileName}`);
}
console.log(data)
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");
};
+99 -47
View File
@@ -7,7 +7,7 @@ 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 {action, userAction} from "@/safe-actions";
import {z} from "zod";
import {ServerActionResult} from "@/types/action-type";
import {Backup} from "@/db/schema/06_database";
@@ -54,25 +54,25 @@ export async function uploadS3Private(fileName: string, buffer: any, bucketName:
}
export async function getFileUrlPresignedLocal(fileName: string) {
try {
const filePath = path.join(privateLocalDir, fileName);
await mkdir(path.join(process.cwd(), privateLocalDir), {recursive: true});
if (!fs.existsSync(filePath)) {
console.error("File not found at:", filePath);
return `File not found at: ${filePath}`;
}
const crypto = require("crypto");
const baseUrl = getServerUrl();
const expiresAt = Date.now() + 60 * 1000; // expires in 1 minute
const token = crypto.createHash("sha256").update(`${fileName}${expiresAt}`).digest("hex");
return `${baseUrl}/api/files/${fileName}?token=${token}&expires=${expiresAt}`;
} catch (error) {
throw error;
}
}
// export async function getFileUrlPresignedLocal(fileName: string) {
// try {
// const filePath = path.join(privateLocalDir, fileName);
// await mkdir(path.join(process.cwd(), privateLocalDir), {recursive: true});
//
// if (!fs.existsSync(filePath)) {
// console.error("File not found at:", filePath);
// return `File not found at: ${filePath}`;
// }
// const crypto = require("crypto");
// const baseUrl = getServerUrl();
//
// const expiresAt = Date.now() + 60 * 1000; // expires in 1 minute
// const token = crypto.createHash("sha256").update(`${fileName}${expiresAt}`).digest("hex");
// return `${baseUrl}/api/files/${fileName}?token=${token}&expires=${expiresAt}`;
// } catch (error) {
// throw error;
// }
// }
export async function getFileUrlPresignedS3(fileName: string) {
try {
@@ -86,33 +86,85 @@ export async function getFileUrlPresignedS3(fileName: string) {
}
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},
},
};
}
});
export const getFileUrlPresignedLocal = action
.schema(z.string())
.action(async ({parsedInput}): Promise<ServerActionResult<string>> => {
try {
const filePath = path.join(privateLocalDir, parsedInput);
await mkdir(path.join(process.cwd(), privateLocalDir), {recursive: true});
if (!fs.existsSync(filePath)) {
console.error("File not found at:", filePath);
throw new Error(`File not found at: ${filePath}`);
}
const crypto = require("crypto");
const baseUrl = getServerUrl();
const expiresAt = Date.now() + 60 * 1000; // expires in 1 minute
const token = crypto.createHash("sha256").update(`${parsedInput}${expiresAt}`).digest("hex");
return {
success: true,
value: `${baseUrl}/api/files/${parsedInput}?token=${token}&expires=${expiresAt}`,
actionSuccess: {
message: "Successfully retrieved presigned URL Local",
messageParams: {fileName: parsedInput},
},
};
} catch (error) {
return {
success: false,
actionError: {
message: "Failed to generate presigned URL",
status: 500,
cause: error instanceof Error ? error.message : "Unknown error",
messageParams: {fileName: parsedInput},
},
};
}
});
export const getFileUrlPreSignedS3Action = action
.schema(z.string())
.action(async ({parsedInput}): Promise<ServerActionResult<string>> => {
try {
const data = await createPresignedUrlToDownload({
bucketName: env.S3_BUCKET_NAME!,
fileName: parsedInput,
});
return {
success: true,
value: data.url,
actionSuccess: {
message: "Successfully retrieved presigned URL",
messageParams: {fileName: parsedInput},
},
};
} catch (error) {
const isNotFound = error instanceof Error && error.message.includes("File does not exist");
const logContext = {
file: parsedInput,
reason: error instanceof Error ? error.message : "Unknown",
};
console.error("Presigned URL generation failed:", logContext);
return {
success: false,
actionError: {
message: isNotFound
? "File not found in S3 bucket"
: "Failed to generate presigned URL",
status: isNotFound ? 404 : 500,
cause: error instanceof Error ? error.message : "Unknown error",
messageParams: {fileName: parsedInput},
},
};
}
});