UNPKG

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
// 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;