fast-r2
Version:
A Node.js module to simplify Cloudflare R2 interactions, providing an instance-based API for uploads, deletions, and URL generation.
141 lines (126 loc) • 5.74 kB
text/typescript
// fast-r2/src/index.ts
import { S3Client, PutObjectCommand, DeleteObjectCommand } from '@aws-sdk/client-s3';
import R2Error from './utils/errorHandling';
/**
* @typedef {object} FastR2Options
* @property {string} accountId - Your Cloudflare Account ID.
* @property {string} accessKeyId - Your R2 Access Key ID.
* @property {string} secretAccessKey - Your R2 Secret Access Key.
* @property {string} bucketName - The name of your R2 bucket.
* @property {string} [endpoint] - Custom R2 endpoint URL (defaults to 'https://<accountId>.r2.cloudflarestorage.com').
*/
export interface FastR2Options { // EXPORTED INTERFACE FOR TYPESCRIPT
accountId: string;
accessKeyId: string;
secretAccessKey: string;
bucketName: string;
endpoint?: string;
}
/**
* @typedef {object} UploadResult
* @property {string} Key - The object key (file name) in R2.
* @property {string} [ETag] - The entity tag of the uploaded object.
* @property {string} Bucket - The R2 bucket name.
* @property {string} Location - The full public URL of the uploaded file.
* @property {string} ContentType - The MIME type of the uploaded file.
* @property {number} Size - The size of the uploaded file in bytes.
*/
export interface UploadResult { // EXPORTED INTERFACE FOR TYPESCRIPT
Key: string;
ETag?: string;
Bucket: string;
Location: string;
ContentType: string;
Size: number;
}
/**
* Creates an instance of the Cloudflare R2 client with methods for file operations.
* @param {FastR2Options} options - Configuration options for R2.
* @returns {{ uploadFile: (fileBuffer: Buffer, fileName: string, contentType: string) => Promise<UploadResult>, deleteFile: (fileName: string) => Promise<void>, getFileUrl: (fileName: string) => string }} An object containing uploadFile, deleteFile, and getFileUrl methods.
*/
function createR2({ accountId, accessKeyId, secretAccessKey, bucketName, endpoint }: FastR2Options) { // ADDED :FastR2Options
// Validate required options
if (!accountId || !accessKeyId || !secretAccessKey || !bucketName) {
throw new R2Error('Cloudflare R2: accountId, accessKeyId, secretAccessKey, and bucketName are required to create an instance.');
}
// Construct the R2 endpoint URL if not explicitly provided
const r2Endpoint = endpoint || `https://${accountId}.r2.cloudflarestorage.com`;
// Initialize the S3Client configured for Cloudflare R2
const r2 = new S3Client({
region: 'auto',
endpoint: r2Endpoint,
credentials: {
accessKeyId: accessKeyId,
secretAccessKey: secretAccessKey,
},
});
/**
* Uploads a file to the configured R2 bucket.
* @param {Buffer} fileBuffer - The binary data of the file to upload.
* @param {string} fileName - The desired key (path and name) for the file in R2 (e.g., 'users/123/profile.jpg').
* @param {string} contentType - The MIME type of the file (e.g., 'image/jpeg', 'video/mp4').
* @returns {Promise<UploadResult>} A promise that resolves to an object with upload details.
* @throws {R2Error} If the upload operation fails.
*/
const uploadFile = async (fileBuffer: Buffer, fileName: string, contentType: string): Promise<UploadResult> => { // Added types
const params = {
Bucket: bucketName,
Key: fileName,
Body: fileBuffer,
ContentType: contentType,
};
try {
const command = new PutObjectCommand(params);
const data = await r2.send(command);
const publicUrl = `${r2Endpoint}/${bucketName}/${fileName}`;
return {
Key: fileName,
ETag: data.ETag ? data.ETag.replace(/"/g, '') : undefined,
Bucket: bucketName,
Location: publicUrl,
ContentType: contentType,
Size: fileBuffer.length
};
} catch (error: any) {
console.error(`Error uploading file '${fileName}' to R2 bucket '${bucketName}':`, error);
throw new R2Error(`Failed to upload file '${fileName}'.`, error);
}
};
/**
* Deletes a file from the configured R2 bucket.
* @param {string} fileName - The key (path and name) of the file to delete from R2.
* @returns {Promise<void>} A promise that resolves upon successful deletion.
* @throws {R2Error} If the deletion operation fails.
*/
const deleteFile = async (fileName: string): Promise<void> => { // Added types
const params = {
Bucket: bucketName,
Key: fileName,
};
try {
const command = new DeleteObjectCommand(params);
await r2.send(command);
} catch (error: any) {
console.error(`Error deleting file '${fileName}' from R2 bucket '${bucketName}':`, error);
throw new R2Error(`Failed to delete file '${fileName}'.`, error);
}
};
/**
* Generates the public URL for a file in the configured R2 bucket.
* @param {string} fileName - The key (path and name) of the file.
* @returns {string} The public URL of the file.
* @throws {R2Error} If the file name is missing.
*/
const getFileUrl = (fileName: string): string => { // Added types
if (!fileName) {
throw new R2Error('File name is required to generate a URL.');
}
return `${r2Endpoint}/${bucketName}/${fileName}`;
};
return {
uploadFile,
deleteFile,
getFileUrl,
};
}
export default createR2;