mirror of
https://github.com/formbricks/formbricks.git
synced 2025-12-30 02:10:12 -06:00
fix: s3-cache (#2362)
This commit is contained in:
@@ -33,6 +33,8 @@ const getFile = async (environmentId: string, accessType: string, fileName: stri
|
||||
status: 302,
|
||||
headers: {
|
||||
Location: signedUrl,
|
||||
// public file, cache for one hour, private file, cache for 10 minutes
|
||||
"Cache-Control": `public, max-age=${accessType === "public" ? 3600 : 600}, s-maxage=3600, stale-while-revalidate=300`,
|
||||
},
|
||||
});
|
||||
} catch (err) {
|
||||
|
||||
@@ -10,10 +10,8 @@ import {
|
||||
import { PresignedPostOptions, createPresignedPost } from "@aws-sdk/s3-presigned-post";
|
||||
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
|
||||
import { randomUUID } from "crypto";
|
||||
import { add, isAfter, parseISO } from "date-fns";
|
||||
import { access, mkdir, readFile, rmdir, unlink, writeFile } from "fs/promises";
|
||||
import { lookup } from "mime-types";
|
||||
import { unstable_cache } from "next/cache";
|
||||
import path, { join } from "path";
|
||||
|
||||
import { TAccessType } from "@formbricks/types/storage";
|
||||
@@ -31,7 +29,6 @@ import {
|
||||
} from "../constants";
|
||||
import { generateLocalSignedUrl } from "../crypto";
|
||||
import { env } from "../env";
|
||||
import { storageCache } from "./cache";
|
||||
|
||||
// S3Client Singleton
|
||||
let s3ClientInstance: S3Client | null = null;
|
||||
@@ -107,72 +104,22 @@ type TGetSignedUrlResponse =
|
||||
const getS3SignedUrl = async (fileKey: string): Promise<string> => {
|
||||
const [_, accessType] = fileKey.split("/");
|
||||
const expiresIn = accessType === "public" ? 60 * 60 : 10 * 60;
|
||||
const revalidateAfter = accessType === "public" ? expiresIn - 60 * 5 : expiresIn - 60 * 2;
|
||||
|
||||
return unstable_cache(
|
||||
async () => {
|
||||
const getObjectCommand = new GetObjectCommand({
|
||||
Bucket: S3_BUCKET_NAME,
|
||||
Key: fileKey,
|
||||
});
|
||||
const getObjectCommand = new GetObjectCommand({
|
||||
Bucket: S3_BUCKET_NAME,
|
||||
Key: fileKey,
|
||||
});
|
||||
|
||||
try {
|
||||
const s3Client = getS3Client();
|
||||
return await getSignedUrl(s3Client, getObjectCommand, { expiresIn });
|
||||
} catch (err) {
|
||||
throw err;
|
||||
}
|
||||
},
|
||||
[`getFileFromS3-${fileKey}`],
|
||||
{
|
||||
revalidate: revalidateAfter,
|
||||
tags: [storageCache.tag.byFileKey(fileKey)],
|
||||
}
|
||||
)();
|
||||
try {
|
||||
const s3Client = getS3Client();
|
||||
return await getSignedUrl(s3Client, getObjectCommand, { expiresIn });
|
||||
} catch (err) {
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
|
||||
export const getS3File = async (fileKey: string): Promise<string> => {
|
||||
const signedUrl = await getS3SignedUrl(fileKey);
|
||||
const signedUrlObject = new URL(signedUrl);
|
||||
|
||||
// The logic below is to check if the signed url has expired.
|
||||
// We do this by parsing the X-Amz-Date and Expires query parameters from the signed url
|
||||
// and checking if the current time is past the expiration time.
|
||||
// If it is, we generate a new signed url and return that instead.
|
||||
// We do this because the time-based revalidation for the signed url is not working as expected. (mayve a bug in next.js caching?)
|
||||
|
||||
const amzDate = signedUrlObject.searchParams.get("X-Amz-Date");
|
||||
const amzExpires = signedUrlObject.searchParams.get("X-Amz-Expires");
|
||||
|
||||
if (amzDate && amzExpires) {
|
||||
const expiresAfterSeconds = parseInt(amzExpires, 10);
|
||||
const currentDate = new Date();
|
||||
|
||||
// Get the UTC components
|
||||
const yearUTC = currentDate.getUTCFullYear();
|
||||
const monthUTC = (currentDate.getUTCMonth() + 1).toString().padStart(2, "0");
|
||||
const dayUTC = currentDate.getUTCDate().toString().padStart(2, "0");
|
||||
const hoursUTC = currentDate.getUTCHours().toString().padStart(2, "0");
|
||||
const minutesUTC = currentDate.getUTCMinutes().toString().padStart(2, "0");
|
||||
const secondsUTC = currentDate.getUTCSeconds().toString().padStart(2, "0");
|
||||
|
||||
// Construct the date-time string in UTC format
|
||||
const currentDateTimeUTC = `${yearUTC}${monthUTC}${dayUTC}T${hoursUTC}${minutesUTC}${secondsUTC}Z`;
|
||||
|
||||
const amzSigningDate = parseISO(amzDate);
|
||||
const amzExpiryDate = add(amzSigningDate, { seconds: expiresAfterSeconds });
|
||||
const currentDateISO = parseISO(currentDateTimeUTC);
|
||||
|
||||
const isExpired = isAfter(currentDateISO, amzExpiryDate);
|
||||
|
||||
if (isExpired) {
|
||||
// generate a new signed url
|
||||
storageCache.revalidate({ fileKey });
|
||||
const signedUrlAfterRefetch = await getS3SignedUrl(fileKey);
|
||||
return signedUrlAfterRefetch;
|
||||
}
|
||||
}
|
||||
|
||||
return signedUrl;
|
||||
};
|
||||
|
||||
|
||||
@@ -51,6 +51,7 @@ const FileInput: React.FC<FileInputProps> = ({
|
||||
}
|
||||
|
||||
const allowedFiles = getAllowedFiles(files, allowedFileExtensions, maxSizeInMB);
|
||||
|
||||
if (allowedFiles.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user