75 lines
1.7 KiB
TypeScript
75 lines
1.7 KiB
TypeScript
import {
|
|
S3Client,
|
|
PutObjectCommand,
|
|
DeleteObjectCommand,
|
|
GetObjectCommand,
|
|
} from "@aws-sdk/client-s3";
|
|
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
|
|
|
|
let cachedClient: S3Client | null = null;
|
|
let cachedBucket: string | null = null;
|
|
|
|
function getClient() {
|
|
if (cachedClient && cachedBucket) {
|
|
return { s3: cachedClient, bucket: cachedBucket };
|
|
}
|
|
|
|
const {
|
|
R2_ENDPOINT,
|
|
R2_REGION = "auto",
|
|
R2_ACCESS_KEY_ID,
|
|
R2_SECRET_ACCESS_KEY,
|
|
R2_BUCKET,
|
|
} = process.env;
|
|
|
|
if (!R2_ENDPOINT || !R2_ACCESS_KEY_ID || !R2_SECRET_ACCESS_KEY || !R2_BUCKET) {
|
|
throw new Error("R2 configuration is missing. Check env variables.");
|
|
}
|
|
|
|
cachedBucket = R2_BUCKET;
|
|
cachedClient = new S3Client({
|
|
endpoint: R2_ENDPOINT,
|
|
region: R2_REGION,
|
|
credentials: {
|
|
accessKeyId: R2_ACCESS_KEY_ID,
|
|
secretAccessKey: R2_SECRET_ACCESS_KEY,
|
|
},
|
|
});
|
|
|
|
return { s3: cachedClient, bucket: cachedBucket };
|
|
}
|
|
|
|
export async function uploadToR2(params: {
|
|
key: string;
|
|
contentType: string;
|
|
body: Buffer;
|
|
}) {
|
|
const { s3, bucket } = getClient();
|
|
const command = new PutObjectCommand({
|
|
Bucket: bucket,
|
|
Key: params.key,
|
|
Body: params.body,
|
|
ContentType: params.contentType,
|
|
});
|
|
await s3.send(command);
|
|
}
|
|
|
|
export async function getSignedDownloadUrl(key: string, expiresInSeconds = 300) {
|
|
const { s3, bucket } = getClient();
|
|
const command = new GetObjectCommand({
|
|
Bucket: bucket,
|
|
Key: key,
|
|
});
|
|
return getSignedUrl(s3, command, { expiresIn: expiresInSeconds });
|
|
}
|
|
|
|
export async function deleteFromR2(key: string) {
|
|
const { s3, bucket } = getClient();
|
|
const command = new DeleteObjectCommand({
|
|
Bucket: bucket,
|
|
Key: key,
|
|
});
|
|
await s3.send(command);
|
|
}
|
|
|