Centralize presigned url commands

This commit is contained in:
Sam Becker 2026-02-17 17:18:04 -06:00
parent 053faae9b6
commit 44550824e7
5 changed files with 65 additions and 44 deletions

View File

@ -1,18 +1,5 @@
import { auth } from '@/auth/server';
import {
awsS3Client,
awsS3PutObjectCommandForKey,
} from '@/platforms/storage/aws-s3';
import {
cloudflareR2Client,
cloudflareR2PutObjectCommandForKey,
} from '@/platforms/storage/cloudflare-r2';
import {
minioClient,
minioPutObjectCommandForKey,
} from '@/platforms/storage/minio';
import { CURRENT_STORAGE } from '@/app/config';
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
import { getSignedUrl } from '@/platforms/storage';
export async function GET(
_: Request,
@ -21,27 +8,9 @@ export async function GET(
const { key } = await params;
const session = await auth();
if (session?.user && key) {
let client;
let command;
switch (CURRENT_STORAGE) {
case 'cloudflare-r2':
client = cloudflareR2Client();
command = cloudflareR2PutObjectCommandForKey(key);
break;
case 'minio':
client = minioClient();
command = minioPutObjectCommandForKey(key);
break;
default:
client = awsS3Client();
command = awsS3PutObjectCommandForKey(key);
break;
}
const url = await getSignedUrl(client, command, { expiresIn: 3600 });
if (session?.user && key) {
const url = await getSignedUrl(key, 'PUT');
return new Response(
url,
{ headers: { 'content-type': 'text/plain' } },

View File

@ -4,7 +4,9 @@ import {
DeleteObjectCommand,
ListObjectsCommand,
PutObjectCommand,
GetObjectCommand,
} from '@aws-sdk/client-s3';
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
import { StorageListResponse, generateStorageId } from '.';
import { formatBytes } from '@/utility/number';
@ -30,9 +32,6 @@ const urlForKey = (key?: string) => `${AWS_S3_BASE_URL}/${key}`;
export const isUrlFromAwsS3 = (url?: string) =>
AWS_S3_BASE_URL && url?.startsWith(AWS_S3_BASE_URL);
export const awsS3PutObjectCommandForKey = (Key: string) =>
new PutObjectCommand({ Bucket: AWS_S3_BUCKET, Key, ACL: 'public-read' });
export const awsS3Put = async (
file: Buffer,
fileName: string,
@ -84,3 +83,15 @@ export const awsS3Delete = async (Key: string) => {
Key,
}));
};
export const awsS3GetSignedUrl = async (
Key: string,
method: 'GET' | 'PUT',
expiresIn: number,
) => {
const client = awsS3Client();
const command = method === 'GET'
? new GetObjectCommand({ Bucket: AWS_S3_BUCKET, Key })
: new PutObjectCommand({ Bucket: AWS_S3_BUCKET, Key, ACL: 'public-read' });
return getSignedUrl(client, command, { expiresIn });
};

View File

@ -4,7 +4,9 @@ import {
PutObjectCommand,
DeleteObjectCommand,
CopyObjectCommand,
GetObjectCommand,
} from '@aws-sdk/client-s3';
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
import { StorageListResponse, generateStorageId } from '.';
import { removeUrlProtocol } from '@/utility/url';
import { formatBytes } from '@/utility/number';
@ -52,9 +54,6 @@ export const isUrlFromCloudflareR2 = (url?: string) => (
url?.startsWith(CLOUDFLARE_R2_BASE_URL_PUBLIC)
);
export const cloudflareR2PutObjectCommandForKey = (Key: string) =>
new PutObjectCommand({ Bucket: CLOUDFLARE_R2_BUCKET, Key });
export const cloudflareR2Put = async (
file: Buffer,
fileName: string,
@ -104,3 +103,16 @@ export const cloudflareR2Delete = async (Key: string) => {
Key,
}));
};
export const cloudflareR2GetSignedUrl = async (
Key: string,
method: 'GET' | 'PUT',
expiresIn: number,
) => {
const client = cloudflareR2Client();
const command = method === 'GET'
? new GetObjectCommand({ Bucket: CLOUDFLARE_R2_BUCKET, Key })
// eslint-disable-next-line max-len
: new PutObjectCommand({ Bucket: CLOUDFLARE_R2_BUCKET, Key, ACL: 'public-read' });
return getSignedUrl(client, command, { expiresIn });
};

View File

@ -10,6 +10,7 @@ import {
AWS_S3_BASE_URL,
awsS3Copy,
awsS3Delete,
awsS3GetSignedUrl,
awsS3List,
awsS3Put,
isUrlFromAwsS3,
@ -26,6 +27,7 @@ import {
CLOUDFLARE_R2_BASE_URL_PUBLIC,
cloudflareR2Copy,
cloudflareR2Delete,
cloudflareR2GetSignedUrl,
cloudflareR2List,
cloudflareR2Put,
isUrlFromCloudflareR2,
@ -37,6 +39,7 @@ import {
minioList,
minioPut,
isUrlFromMinio,
minioGetSignedUrl,
} from './minio';
import { PATH_API_PRESIGNED_URL } from '@/app/path';
@ -248,5 +251,20 @@ export const getStorageUrlsForPrefix = async (prefix = '') => {
});
};
export const getSignedUrl = async (
key: string,
method: 'GET' | 'PUT',
expiresIn = 3600,
) => {
switch (CURRENT_STORAGE) {
case 'cloudflare-r2':
return cloudflareR2GetSignedUrl(key, method, expiresIn);
case 'minio':
return minioGetSignedUrl(key, method, expiresIn);
default:
return awsS3GetSignedUrl(key, method, expiresIn);
}
};
export const testStorageConnection = () =>
getStorageUrlsForPrefix();

View File

@ -4,7 +4,9 @@ import {
ListObjectsCommand,
PutObjectCommand,
DeleteObjectCommand,
GetObjectCommand,
} from '@aws-sdk/client-s3';
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
import { StorageListResponse, generateStorageId } from '.';
import { formatBytes } from '@/utility/number';
@ -39,9 +41,6 @@ const urlForKey = (key?: string) => `${MINIO_BASE_URL}/${key}`;
export const isUrlFromMinio = (url?: string) =>
MINIO_BASE_URL && url?.startsWith(MINIO_BASE_URL);
export const minioPutObjectCommandForKey = (Key: string) =>
new PutObjectCommand({ Bucket: MINIO_BUCKET, Key });
export const minioPut = async (
file: Buffer,
fileName: string,
@ -93,3 +92,15 @@ export const minioDelete = async (Key: string): Promise<void> => {
});
await minioClient().send(deleteObjectCommand);
};
export const minioGetSignedUrl = async (
Key: string,
method: 'GET' | 'PUT',
expiresIn: number,
) => {
const client = minioClient();
const command = method === 'GET'
? new GetObjectCommand({ Bucket: MINIO_BUCKET, Key })
: new PutObjectCommand({ Bucket: MINIO_BUCKET, Key, ACL: 'public-read' });
return getSignedUrl(client, command, { expiresIn });
};