From aaf079038049fabe48b7fbd466f4ae4911f09392 Mon Sep 17 00:00:00 2001 From: vanpana Date: Sun, 3 Nov 2024 11:08:50 +0100 Subject: [PATCH 1/2] add overrides in R2 --- src/Bucket.ts | 7 ++++--- src/R2.ts | 9 ++++++--- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/Bucket.ts b/src/Bucket.ts index 99246ec..0d66176 100644 --- a/src/Bucket.ts +++ b/src/Bucket.ts @@ -10,6 +10,7 @@ import { ListObjectsCommand, PutObjectCommand, type S3Client as R2, + S3ClientConfig } from '@aws-sdk/client-s3'; import { Upload, type Progress } from '@aws-sdk/lib-storage'; import { getSignedUrl } from '@aws-sdk/s3-request-presigner'; @@ -20,7 +21,7 @@ import type { CORSPolicy, HeadObjectResponse, ObjectListResponse, UploadFileResp export class Bucket { private r2: R2; - private endpoint: string; + private endpoint: S3ClientConfig['endpoint']; private bucketPublicUrls: string[] = []; /** @@ -41,10 +42,10 @@ export class Bucket { * @param bucketName Name of the bucket. * @param endpoint Cloudflare R2 base endpoint. */ - constructor(r2: R2, bucketName: string, endpoint: string) { + constructor(r2: R2, bucketName: string, endpoint: S3ClientConfig['endpoint']) { this.r2 = r2; this.name = bucketName; - this.endpoint = new URL(endpoint).origin; + this.endpoint = endpoint; this.uri = `${this.endpoint}/${this.name}`; } diff --git a/src/R2.ts b/src/R2.ts index da631cc..ffcc272 100644 --- a/src/R2.ts +++ b/src/R2.ts @@ -1,4 +1,4 @@ -import { CreateBucketCommand, DeleteBucketCommand, ListBucketsCommand, S3Client } from '@aws-sdk/client-s3'; +import { CreateBucketCommand, DeleteBucketCommand, ListBucketsCommand, S3Client, S3ClientConfig } from '@aws-sdk/client-s3'; import { Bucket } from './Bucket'; import type { BucketList, CORSPolicy, CloudflareR2Config } from './types'; @@ -7,10 +7,12 @@ export class R2 { private r2: S3Client; public endpoint: string; - constructor(config: CloudflareR2Config) { + constructor(config: CloudflareR2Config, overrides?: S3ClientConfig) { this.config = config; - if (this.config.jurisdiction) { + if (overrides?.endpoint) { + this.endpoint = overrides.endpoint + } else if (this.config.jurisdiction) { this.endpoint = `https://${this.config.accountId}.${this.config.jurisdiction}.r2.cloudflarestorage.com`; } else { this.endpoint = `https://${this.config.accountId}.r2.cloudflarestorage.com`; @@ -23,6 +25,7 @@ export class R2 { secretAccessKey: this.config.secretAccessKey, }, region: 'auto', + ...overrides }); } From bdf82b58d82a4dbd7d2f6904787574150ee1c2a9 Mon Sep 17 00:00:00 2001 From: vanpana Date: Thu, 19 Dec 2024 00:34:04 +0100 Subject: [PATCH 2/2] Expose new functions --- README.md | 17 +++++++++++++++-- src/Bucket.ts | 19 ++++++++++++++++--- src/R2.ts | 8 ++++++++ 3 files changed, 39 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index a51dc51..fd6e608 100644 --- a/README.md +++ b/README.md @@ -71,10 +71,11 @@ console.log(upload); */ ``` -### Generate signed URL with expiration time +### Generate GET signed URL with expiration time +[More about signed URLS on R2](https://developers.cloudflare.com/r2/examples/aws/aws-sdk-js/#generate-presigned-urls) ```javascript -// Generate signed link that expires after 3600 seconds. +// Generate GET signed link that expires after 3600 seconds. const signedUrl = await bucket.getObjectSignedUrl('destination_file_name.ext', 3600); console.log(signedUrl); /* @@ -82,6 +83,18 @@ https://bucket-name.cloudflare-account-id.r2.cloudflarestorage.com/destination_f */ ``` +### Generate PUT signed URL with expiration time +[More about signed URLS on R2](https://developers.cloudflare.com/r2/examples/aws/aws-sdk-js/#generate-presigned-urls) + +```javascript +// Generate signed link that expires after 3600 seconds. +const signedUrl = await bucket.putObjectSignedUrl('destination_file_name.ext', 3600); +console.log(signedUrl); +/* +https://bucket-name.cloudflare-account-id.r2.cloudflarestorage.com/destination_file_name.ext?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=...>&X-Amz-Expires=3600&X-Amz-Signature=&X-Amz-SignedHeaders=host +*/ +``` + ### Upload string or binary data or stream ```javascript diff --git a/src/Bucket.ts b/src/Bucket.ts index 5112f4d..e269b73 100644 --- a/src/Bucket.ts +++ b/src/Bucket.ts @@ -102,7 +102,8 @@ export class Bucket { } /** - * Returns the signed URL of an object. This method does not check whether the object exists or not. + * Returns the signed URL of an object used to perform a "GET" action. + * This method does not check whether the object exists or not. * @param objectKey * @param expiresIn Expiration time in seconds. */ @@ -111,8 +112,20 @@ export class Bucket { Bucket: this.name, Key: objectKey, }); - const signedUrl = await getSignedUrl(this.r2, obj, { expiresIn }); - return signedUrl; + return getSignedUrl(this.r2, obj, { expiresIn }); + } + + /** + * Returns the signed URL of an object used to perform a "PUT" action. + * @param objectKey + * @param expiresIn Expiration time in seconds. + */ + public async putObjectSignedUrl(objectKey: string, expiresIn: number): Promise { + const obj = new PutObjectCommand({ + Bucket: this.name, + Key: objectKey, + }); + return getSignedUrl(this.r2, obj, { expiresIn }); } /** diff --git a/src/R2.ts b/src/R2.ts index 5552897..a284dfa 100644 --- a/src/R2.ts +++ b/src/R2.ts @@ -35,6 +35,14 @@ export class R2 { }); } + /** + * Returns the S3 client instance. Should be used as a last resort if you need extra custom functionality. + * @note It is recommended to use other provided methods for specific operations instead of directly accessing the client. + */ + public __unsafe_getClient(): S3Client { + return this.r2; + } + /** * Returns a `Bucket` object that represents the specified storage bucket. * @param bucketName The name of the storage bucket.