fix: s3-cache (#2362)

This commit is contained in:
Anshuman Pandey
2024-03-28 21:08:25 +05:30
committed by GitHub
parent a1e9425a3e
commit f15a4b237d
3 changed files with 13 additions and 63 deletions

View File

@@ -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) {

View File

@@ -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;
};

View File

@@ -51,6 +51,7 @@ const FileInput: React.FC<FileInputProps> = ({
}
const allowedFiles = getAllowedFiles(files, allowedFileExtensions, maxSizeInMB);
if (allowedFiles.length === 0) {
return;
}