mirror of
https://github.com/Portabase/portabase.git
synced 2026-07-14 11:16:13 +02:00
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:
@@ -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");
|
||||
};
|
||||
|
||||
|
||||
@@ -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},
|
||||
},
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user