UNPKG

@push.rocks/smartbucket

Version:

A TypeScript library providing a cloud-agnostic interface for managing object storage with functionalities like bucket management, file and directory operations, and advanced features such as metadata handling and file locking.

413 lines 35.2 kB
// classes.bucket.ts import * as plugins from './plugins.js'; import * as helpers from './helpers.js'; import * as interfaces from './interfaces.js'; import { SmartBucket } from './classes.smartbucket.js'; import { Directory } from './classes.directory.js'; import { File } from './classes.file.js'; import { Trash } from './classes.trash.js'; /** * The bucket class exposes the basic functionality of a bucket. * The functions of the bucket alone are enough to * operate in S3 basic fashion on blobs of data. */ export class Bucket { static async getBucketByName(smartbucketRef, bucketNameArg) { const command = new plugins.s3.ListBucketsCommand({}); const buckets = await smartbucketRef.s3Client.send(command); const foundBucket = buckets.Buckets.find((bucket) => bucket.Name === bucketNameArg); if (foundBucket) { console.log(`bucket with name ${bucketNameArg} exists.`); console.log(`Taking this as base for new Bucket instance`); return new this(smartbucketRef, bucketNameArg); } else { console.log(`did not find bucket by name: ${bucketNameArg}`); return null; } } static async createBucketByName(smartbucketRef, bucketName) { const command = new plugins.s3.CreateBucketCommand({ Bucket: bucketName }); await smartbucketRef.s3Client.send(command).catch((e) => console.log(e)); return new Bucket(smartbucketRef, bucketName); } static async removeBucketByName(smartbucketRef, bucketName) { const command = new plugins.s3.DeleteBucketCommand({ Bucket: bucketName }); await smartbucketRef.s3Client.send(command).catch((e) => console.log(e)); } constructor(smartbucketRef, bucketName) { this.smartbucketRef = smartbucketRef; this.name = bucketName; } /** * gets the base directory of the bucket */ async getBaseDirectory() { return new Directory(this, null, ''); } /** * gets the trash directory */ async getTrash() { const trash = new Trash(this); return trash; } async getDirectoryFromPath(pathDescriptorArg) { if (!pathDescriptorArg.path && !pathDescriptorArg.directory) { return this.getBaseDirectory(); } const checkPath = await helpers.reducePathDescriptorToPath(pathDescriptorArg); const baseDirectory = await this.getBaseDirectory(); return await baseDirectory.getSubDirectoryByNameStrict(checkPath, { getEmptyDirectory: true, }); } // =============== // Fast Operations // =============== /** * store file */ async fastPut(optionsArg) { try { const reducedPath = await helpers.reducePathDescriptorToPath(optionsArg); const exists = await this.fastExists({ path: reducedPath }); if (exists && !optionsArg.overwrite) { const errorText = `Object already exists at path '${reducedPath}' in bucket '${this.name}'.`; console.error(errorText); return null; } else if (exists && optionsArg.overwrite) { console.log(`Overwriting existing object at path '${reducedPath}' in bucket '${this.name}'.`); } else { console.log(`Creating new object at path '${reducedPath}' in bucket '${this.name}'.`); } const command = new plugins.s3.PutObjectCommand({ Bucket: this.name, Key: reducedPath, Body: optionsArg.contents, }); await this.smartbucketRef.s3Client.send(command); console.log(`Object '${reducedPath}' has been successfully stored in bucket '${this.name}'.`); const parsedPath = plugins.path.parse(reducedPath); return new File({ directoryRefArg: await this.getDirectoryFromPath({ path: parsedPath.dir, }), fileName: parsedPath.base, }); } catch (error) { console.error(`Error storing object at path '${optionsArg.path}' in bucket '${this.name}':`, error); throw error; } } async fastPutStrict(...args) { const file = await this.fastPut(...args); if (!file) { throw new Error(`File not stored at path '${args[0].path}'`); } return file; } /** * get file */ async fastGet(optionsArg) { const done = plugins.smartpromise.defer(); let completeFile; const replaySubject = await this.fastGetReplaySubject(optionsArg); const subscription = replaySubject.subscribe({ next: (chunk) => { if (completeFile) { completeFile = Buffer.concat([completeFile, chunk]); } else { completeFile = chunk; } }, complete: () => { done.resolve(); subscription.unsubscribe(); }, error: (err) => { console.log(err); }, }); await done.promise; return completeFile; } /** * good when time to first byte is important * and multiple subscribers are expected * @param optionsArg * @returns */ async fastGetReplaySubject(optionsArg) { const command = new plugins.s3.GetObjectCommand({ Bucket: this.name, Key: optionsArg.path, }); const response = await this.smartbucketRef.s3Client.send(command); const replaySubject = new plugins.smartrx.rxjs.ReplaySubject(); // Convert the stream to a format that supports piping const stream = response.Body; // SdkStreamMixin includes readable stream if (typeof stream.pipe === 'function') { const duplexStream = new plugins.smartstream.SmartDuplex({ writeFunction: async (chunk) => { replaySubject.next(chunk); return; }, finalFunction: async (cb) => { replaySubject.complete(); return; }, }); stream.pipe(duplexStream); } return replaySubject; } async fastGetStream(optionsArg, typeArg = 'nodestream') { const command = new plugins.s3.GetObjectCommand({ Bucket: this.name, Key: optionsArg.path, }); const response = await this.smartbucketRef.s3Client.send(command); const stream = response.Body; // SdkStreamMixin includes readable stream const duplexStream = new plugins.smartstream.SmartDuplex({ writeFunction: async (chunk) => { return chunk; }, finalFunction: async (cb) => { return null; }, }); if (typeof stream.pipe === 'function') { stream.pipe(duplexStream); } if (typeArg === 'nodestream') { return duplexStream; } if (typeArg === 'webstream') { return (await duplexStream.getWebStreams()).readable; } throw new Error('unknown typeArg'); } /** * store file as stream */ async fastPutStream(optionsArg) { try { const exists = await this.fastExists({ path: optionsArg.path }); if (exists && !optionsArg.overwrite) { console.error(`Object already exists at path '${optionsArg.path}' in bucket '${this.name}'.`); return; } else if (exists && optionsArg.overwrite) { console.log(`Overwriting existing object at path '${optionsArg.path}' in bucket '${this.name}'.`); } else { console.log(`Creating new object at path '${optionsArg.path}' in bucket '${this.name}'.`); } const command = new plugins.s3.PutObjectCommand({ Bucket: this.name, Key: optionsArg.path, Body: optionsArg.readableStream, Metadata: optionsArg.nativeMetadata, }); await this.smartbucketRef.s3Client.send(command); console.log(`Object '${optionsArg.path}' has been successfully stored in bucket '${this.name}'.`); } catch (error) { console.error(`Error storing object at path '${optionsArg.path}' in bucket '${this.name}':`, error); throw error; } } async fastCopy(optionsArg) { try { const targetBucketName = optionsArg.targetBucket ? optionsArg.targetBucket.name : this.name; // Retrieve current object information to use in copy conditions const currentObjInfo = await this.smartbucketRef.s3Client.send(new plugins.s3.HeadObjectCommand({ Bucket: this.name, Key: optionsArg.sourcePath, })); // Prepare new metadata const newNativeMetadata = { ...(optionsArg.deleteExistingNativeMetadata ? {} : currentObjInfo.Metadata), ...optionsArg.nativeMetadata, }; // Define the copy operation const copySource = `${this.name}/${optionsArg.sourcePath}`; const command = new plugins.s3.CopyObjectCommand({ Bucket: targetBucketName, CopySource: copySource, Key: optionsArg.destinationPath || optionsArg.sourcePath, Metadata: newNativeMetadata, MetadataDirective: optionsArg.deleteExistingNativeMetadata ? 'REPLACE' : 'COPY', }); await this.smartbucketRef.s3Client.send(command); } catch (err) { console.error('Error updating metadata:', err); throw err; // rethrow to allow caller to handle } } /** * Move object from one path to another within the same bucket or to another bucket */ async fastMove(optionsArg) { try { const destinationBucket = optionsArg.targetBucket || this; const exists = await destinationBucket.fastExists({ path: optionsArg.destinationPath, }); if (exists && !optionsArg.overwrite) { console.error(`Object already exists at destination path '${optionsArg.destinationPath}' in bucket '${destinationBucket.name}'.`); return; } else if (exists && optionsArg.overwrite) { console.log(`Overwriting existing object at destination path '${optionsArg.destinationPath}' in bucket '${destinationBucket.name}'.`); } else { console.log(`Moving object to path '${optionsArg.destinationPath}' in bucket '${destinationBucket.name}'.`); } await this.fastCopy(optionsArg); await this.fastRemove({ path: optionsArg.sourcePath }); console.log(`Object '${optionsArg.sourcePath}' has been successfully moved to '${optionsArg.destinationPath}' in bucket '${destinationBucket.name}'.`); } catch (error) { console.error(`Error moving object from '${optionsArg.sourcePath}' to '${optionsArg.destinationPath}':`, error); throw error; } } /** * removeObject */ async fastRemove(optionsArg) { const command = new plugins.s3.DeleteObjectCommand({ Bucket: this.name, Key: optionsArg.path, }); await this.smartbucketRef.s3Client.send(command); } /** * check whether file exists * @param optionsArg * @returns */ async fastExists(optionsArg) { try { const command = new plugins.s3.HeadObjectCommand({ Bucket: this.name, Key: optionsArg.path, }); await this.smartbucketRef.s3Client.send(command); console.log(`Object '${optionsArg.path}' exists in bucket '${this.name}'.`); return true; } catch (error) { if (error?.name === 'NotFound') { console.log(`Object '${optionsArg.path}' does not exist in bucket '${this.name}'.`); return false; } else { console.error('Error checking object existence:', error); throw error; // Rethrow if it's not a NotFound error to handle unexpected issues } } } /** * deletes this bucket */ async delete() { await this.smartbucketRef.s3Client.send(new plugins.s3.DeleteBucketCommand({ Bucket: this.name })); } async fastStat(pathDescriptor) { const checkPath = await helpers.reducePathDescriptorToPath(pathDescriptor); const command = new plugins.s3.HeadObjectCommand({ Bucket: this.name, Key: checkPath, }); return this.smartbucketRef.s3Client.send(command); } async isDirectory(pathDescriptor) { const checkPath = await helpers.reducePathDescriptorToPath(pathDescriptor); const command = new plugins.s3.ListObjectsV2Command({ Bucket: this.name, Prefix: checkPath, Delimiter: '/', }); const { CommonPrefixes } = await this.smartbucketRef.s3Client.send(command); return !!CommonPrefixes && CommonPrefixes.length > 0; } async isFile(pathDescriptor) { const checkPath = await helpers.reducePathDescriptorToPath(pathDescriptor); const command = new plugins.s3.ListObjectsV2Command({ Bucket: this.name, Prefix: checkPath, Delimiter: '/', }); const { Contents } = await this.smartbucketRef.s3Client.send(command); return !!Contents && Contents.length > 0; } async getMagicBytes(optionsArg) { try { const command = new plugins.s3.GetObjectCommand({ Bucket: this.name, Key: optionsArg.path, Range: `bytes=0-${optionsArg.length - 1}`, }); const response = await this.smartbucketRef.s3Client.send(command); const chunks = []; const stream = response.Body; // SdkStreamMixin includes readable stream for await (const chunk of stream) { chunks.push(chunk); } return Buffer.concat(chunks); } catch (error) { console.error(`Error retrieving magic bytes from object at path '${optionsArg.path}' in bucket '${this.name}':`, error); throw error; } } async cleanAllContents() { try { // Define the command type explicitly const listCommandInput = { Bucket: this.name, }; let isTruncated = true; let continuationToken = undefined; while (isTruncated) { // Add the continuation token to the input if present const listCommand = new plugins.s3.ListObjectsV2Command({ ...listCommandInput, ContinuationToken: continuationToken, }); // Explicitly type the response const response = await this.smartbucketRef.s3Client.send(listCommand); console.log(`Cleaning contents of bucket '${this.name}': Now deleting ${response.Contents?.length} items...`); if (response.Contents && response.Contents.length > 0) { // Delete objects in batches, mapping each item to { Key: string } const deleteCommand = new plugins.s3.DeleteObjectsCommand({ Bucket: this.name, Delete: { Objects: response.Contents.map((item) => ({ Key: item.Key })), Quiet: true, }, }); await this.smartbucketRef.s3Client.send(deleteCommand); } // Update continuation token and truncation status isTruncated = response.IsTruncated || false; continuationToken = response.NextContinuationToken; } console.log(`All contents in bucket '${this.name}' have been deleted.`); } catch (error) { console.error(`Error cleaning contents of bucket '${this.name}':`, error); throw error; } } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5idWNrZXQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9jbGFzc2VzLmJ1Y2tldC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxvQkFBb0I7QUFFcEIsT0FBTyxLQUFLLE9BQU8sTUFBTSxjQUFjLENBQUM7QUFDeEMsT0FBTyxLQUFLLE9BQU8sTUFBTSxjQUFjLENBQUM7QUFDeEMsT0FBTyxLQUFLLFVBQVUsTUFBTSxpQkFBaUIsQ0FBQztBQUM5QyxPQUFPLEVBQUUsV0FBVyxFQUFFLE1BQU0sMEJBQTBCLENBQUM7QUFDdkQsT0FBTyxFQUFFLFNBQVMsRUFBRSxNQUFNLHdCQUF3QixDQUFDO0FBQ25ELE9BQU8sRUFBRSxJQUFJLEVBQUUsTUFBTSxtQkFBbUIsQ0FBQztBQUN6QyxPQUFPLEVBQUUsS0FBSyxFQUFFLE1BQU0sb0JBQW9CLENBQUM7QUFFM0M7Ozs7R0FJRztBQUNILE1BQU0sT0FBTyxNQUFNO0lBQ1YsTUFBTSxDQUFDLEtBQUssQ0FBQyxlQUFlLENBQUMsY0FBMkIsRUFBRSxhQUFxQjtRQUNwRixNQUFNLE9BQU8sR0FBRyxJQUFJLE9BQU8sQ0FBQyxFQUFFLENBQUMsa0JBQWtCLENBQUMsRUFBRSxDQUFDLENBQUM7UUFDdEQsTUFBTSxPQUFPLEdBQUcsTUFBTSxjQUFjLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsQ0FBQztRQUM1RCxNQUFNLFdBQVcsR0FBRyxPQUFPLENBQUMsT0FBUSxDQUFDLElBQUksQ0FBQyxDQUFDLE1BQU0sRUFBRSxFQUFFLENBQUMsTUFBTSxDQUFDLElBQUksS0FBSyxhQUFhLENBQUMsQ0FBQztRQUVyRixJQUFJLFdBQVcsRUFBRSxDQUFDO1lBQ2hCLE9BQU8sQ0FBQyxHQUFHLENBQUMsb0JBQW9CLGFBQWEsVUFBVSxDQUFDLENBQUM7WUFDekQsT0FBTyxDQUFDLEdBQUcsQ0FBQyw2Q0FBNkMsQ0FBQyxDQUFDO1lBQzNELE9BQU8sSUFBSSxJQUFJLENBQUMs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