Use presigned urls when statically pre-rendering

This commit is contained in:
Sam Becker 2026-02-17 18:11:54 -06:00
parent 44550824e7
commit d11cf6f62d
6 changed files with 49 additions and 16 deletions

View File

@ -1,5 +1,5 @@
import { auth } from '@/auth/server'; import { auth } from '@/auth/server';
import { getSignedUrl } from '@/platforms/storage'; import { getSignedUrlForKey } from '@/platforms/storage';
export async function GET( export async function GET(
_: Request, _: Request,
@ -10,7 +10,7 @@ export async function GET(
const session = await auth(); const session = await auth();
if (session?.user && key) { if (session?.user && key) {
const url = await getSignedUrl(key, 'PUT'); const url = await getSignedUrlForKey(key, 'PUT');
return new Response( return new Response(
url, url,
{ headers: { 'content-type': 'text/plain' } }, { headers: { 'content-type': 'text/plain' } },

View File

@ -7,6 +7,8 @@ import {
doAllPhotosHaveOptimizedFiles, doAllPhotosHaveOptimizedFiles,
getOptimizedPhotoUrl, getOptimizedPhotoUrl,
} from '@/photo/storage'; } from '@/photo/storage';
import { fetchBase64ImageFromUrl } from '@/utility/image';
import { getSignedUrlForUrl } from '@/platforms/storage';
export default async function ImagePhotoGrid({ export default async function ImagePhotoGrid({
photos, photos,
@ -53,7 +55,23 @@ export default async function ImagePhotoGrid({
const doOptimizedFilesExist = await doAllPhotosHaveOptimizedFiles(photos); const doOptimizedFilesExist = await doAllPhotosHaveOptimizedFiles(photos);
const renderPhoto = ({ id, url }: Photo, width: number, height: number) => const photoDataUrls = await Promise.all(photos.map(async({ id, url }) => {
const optimizedUrl = getOptimizedPhotoUrl({
imageUrl: url,
size: nextImageWidth,
addBypassSecret: IS_PREVIEW,
compatibilityMode: !doOptimizedFilesExist,
});
const presignedUrl = await getSignedUrlForUrl(optimizedUrl, 'GET');
const data = await fetchBase64ImageFromUrl(presignedUrl);
return { id, data };
}));
const renderPhoto = (
{ id, data }: typeof photoDataUrls[number],
width: number,
height: number,
) =>
<div <div
key={id} key={id}
style={{ style={{
@ -65,12 +83,7 @@ export default async function ImagePhotoGrid({
}} }}
> >
<img {...{ <img {...{
src: getOptimizedPhotoUrl({ src: data,
imageUrl: url,
size: nextImageWidth,
addBypassSecret: IS_PREVIEW,
compatibilityMode: !doOptimizedFilesExist,
}),
style: { style: {
...imageStyle, ...imageStyle,
width: '100%', width: '100%',
@ -100,7 +113,7 @@ export default async function ImagePhotoGrid({
width: cellWidth, width: cellWidth,
height: cellHeight * 2, height: cellHeight * 2,
}}> }}>
{renderPhoto(photos[0], cellWidth, cellHeight * 2)} {renderPhoto(photoDataUrls[0], cellWidth, cellHeight * 2)}
</div> </div>
{/* Small images (R) */} {/* Small images (R) */}
<div style={{ <div style={{
@ -109,12 +122,12 @@ export default async function ImagePhotoGrid({
width: cellWidth, width: cellWidth,
height: cellHeight, height: cellHeight,
}}> }}>
{photos.slice(1).map(photo => {photoDataUrls.slice(1).map(photo =>
renderPhoto(photo, cellWidth, cellHeight), renderPhoto(photo, cellWidth, cellHeight),
)} )}
</div> </div>
</> </>
: photos.slice(0, count).map(photo => : photoDataUrls.slice(0, count).map(photo =>
renderPhoto(photo, cellWidth, cellHeight), renderPhoto(photo, cellWidth, cellHeight),
)} )}
</div> </div>

View File

@ -84,7 +84,7 @@ export const awsS3Delete = async (Key: string) => {
})); }));
}; };
export const awsS3GetSignedUrl = async ( export const awsS3GetSignedUrl = (
Key: string, Key: string,
method: 'GET' | 'PUT', method: 'GET' | 'PUT',
expiresIn: number, expiresIn: number,

View File

@ -104,7 +104,7 @@ export const cloudflareR2Delete = async (Key: string) => {
})); }));
}; };
export const cloudflareR2GetSignedUrl = async ( export const cloudflareR2GetSignedUrl = (
Key: string, Key: string,
method: 'GET' | 'PUT', method: 'GET' | 'PUT',
expiresIn: number, expiresIn: number,

View File

@ -251,7 +251,8 @@ export const getStorageUrlsForPrefix = async (prefix = '') => {
}); });
}; };
export const getSignedUrl = async ( // Used primarily for uploading files
export const getSignedUrlForKey = async (
key: string, key: string,
method: 'GET' | 'PUT', method: 'GET' | 'PUT',
expiresIn = 3600, expiresIn = 3600,
@ -266,5 +267,24 @@ export const getSignedUrl = async (
} }
}; };
// Used for safely fetching files via presigned URLs
export const getSignedUrlForUrl = (
url: string,
method: 'GET' | 'PUT',
expiresIn = 3600,
) => {
const { fileName } = getFileNamePartsFromStorageUrl(url);
switch (storageTypeFromUrl(url)) {
case 'cloudflare-r2':
return cloudflareR2GetSignedUrl(fileName, method, expiresIn);
case 'minio':
return minioGetSignedUrl(fileName, method, expiresIn);
case 'aws-s3':
return awsS3GetSignedUrl(fileName, method, expiresIn);
default:
return url;
}
};
export const testStorageConnection = () => export const testStorageConnection = () =>
getStorageUrlsForPrefix(); getStorageUrlsForPrefix();

View File

@ -93,7 +93,7 @@ export const minioDelete = async (Key: string): Promise<void> => {
await minioClient().send(deleteObjectCommand); await minioClient().send(deleteObjectCommand);
}; };
export const minioGetSignedUrl = async ( export const minioGetSignedUrl = (
Key: string, Key: string,
method: 'GET' | 'PUT', method: 'GET' | 'PUT',
expiresIn: number, expiresIn: number,