Compare commits

...

6 Commits

Author SHA1 Message Date
b5dcc131e2 3.0.9 2024-05-27 17:34:27 +02:00
cb0ab2c9db fix(core): update 2024-05-27 17:34:26 +02:00
2a17ee542e 3.0.8 2024-05-27 14:34:13 +02:00
95e9d2f0ff fix(core): update 2024-05-27 14:34:12 +02:00
1a71c76da3 3.0.7 2024-05-27 12:56:26 +02:00
e924511147 fix(s3 paths): pathing differences now correctly handled in a reducePath method. 2024-05-27 12:56:25 +02:00
10 changed files with 206 additions and 62 deletions

View File

@ -8,29 +8,27 @@
"githost": "code.foss.global",
"gitscope": "push.rocks",
"gitrepo": "smartbucket",
"description": "A TypeScript library for cloud-independent object storage, providing features like bucket creation, file and directory management, and data streaming.",
"description": "A TypeScript library offering simple and cloud-agnostic object storage with advanced features like bucket creation, file and directory management, and data streaming.",
"npmPackagename": "@push.rocks/smartbucket",
"license": "MIT",
"keywords": [
"TypeScript",
"cloud storage",
"object storage",
"TypeScript",
"S3",
"minio",
"bucket creation",
"file management",
"directory management",
"bucket creation",
"data streaming",
"multi-cloud",
"API",
"unified storage",
"S3",
"minio",
"file locking",
"metadata",
"buffer handling",
"access key",
"secret key",
"metadata",
"file locking",
"file streaming",
"directory listing",
"cloud agnostic"
]
}

4
package-lock.json generated
View File

@ -1,12 +1,12 @@
{
"name": "@push.rocks/smartbucket",
"version": "3.0.6",
"version": "3.0.9",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@push.rocks/smartbucket",
"version": "3.0.6",
"version": "3.0.9",
"license": "UNLICENSED",
"dependencies": {
"@push.rocks/smartpath": "^5.0.18",

View File

@ -1,7 +1,7 @@
{
"name": "@push.rocks/smartbucket",
"version": "3.0.6",
"description": "A TypeScript library for cloud-independent object storage, providing features like bucket creation, file and directory management, and data streaming.",
"version": "3.0.9",
"description": "A TypeScript library offering simple and cloud-agnostic object storage with advanced features like bucket creation, file and directory management, and data streaming.",
"main": "dist_ts/index.js",
"typings": "dist_ts/index.d.ts",
"type": "module",
@ -44,25 +44,23 @@
"last 1 chrome versions"
],
"keywords": [
"TypeScript",
"cloud storage",
"object storage",
"TypeScript",
"S3",
"minio",
"bucket creation",
"file management",
"directory management",
"bucket creation",
"data streaming",
"multi-cloud",
"API",
"unified storage",
"S3",
"minio",
"file locking",
"metadata",
"buffer handling",
"access key",
"secret key",
"metadata",
"file locking",
"file streaming",
"directory listing",
"cloud agnostic"
]
}

View File

@ -1,10 +1,10 @@
# @push.rocks/smartbucket
A TypeScript library that offers simple, cloud-independent object storage with features like bucket creation, file management, and directory management.
A TypeScript library for cloud-independent object storage, providing features like bucket creation, file and directory management, and data streaming.
## Install
To install `@push.rocks/smartbucket`, you need to have Node.js and npm (Node Package Manager) installed on your system. If you have them installed, you can add `@push.rocks/smartbucket` to your project by running the following command in your project's root directory:
To install `@push.rocks/smartbucket`, you need to have Node.js and npm (Node Package Manager) installed. If they are installed, you can add `@push.rocks/smartbucket` to your project by running the following command in your project's root directory:
```bash
npm install @push.rocks/smartbucket --save
@ -14,7 +14,7 @@ This command will download and install `@push.rocks/smartbucket` along with its
## Usage
`@push.rocks/smartbucket` is a TypeScript module designed to provide simple cloud-independent object storage functionality. It wraps various cloud storage providers such as AWS S3, Google Cloud Storage, and others, offering a unified API to manage storage buckets and objects within those buckets.
`@push.rocks/smartbucket` is a TypeScript module designed to provide simple cloud-independent object storage functionality. It wraps various cloud storage providers such as AWS S3, Google Cloud Storage, and others, offering a unified API to manage storage buckets and objects within those buckets.
In this guide, we will delve into the usage of SmartBucket, covering its full range of features from setting up the library to advanced usage scenarios.
@ -49,7 +49,7 @@ const mySmartBucket = new SmartBucket({
accessKey: "yourAccessKey",
accessSecret: "yourSecretKey",
endpoint: "yourEndpointURL",
port: 443, // Default is 443, could be customized for specific endpoint
port: 443, // Default is 443, can be customized for specific endpoint
useSsl: true // Defaults to true
});
```
@ -190,7 +190,7 @@ async function writeFileStream(bucketName: string, filePath: string, readableStr
// Create a readable stream from a string
const readable = new Readable();
readable.push('Hello world streamed as a file!');
readable.push(null); // Indicates end of the stream
readable.push(null); // End of stream
// Use the function
writeFileStream("exampleBucket", "path/to/streamedObject.txt", readable);
@ -198,9 +198,9 @@ writeFileStream("exampleBucket", "path/to/streamedObject.txt", readable);
### Working with Directories
`@push.rocks/smartbucket` abstracts directories within buckets for easier object management. You can create, list, and delete directories using the `Directory` class.
`@push.rocks/smartbucket` offers abstractions for directories within buckets for easier object management. You can create, list, and delete directories using the `Directory` class.
Here's how to list the contents of a directory:
To list the contents of a directory:
```typescript
async function listDirectoryContents(bucketName: string, directoryPath: string) {
@ -254,11 +254,11 @@ createFileInDirectory("exampleBucket", "some/directory", "newfile.txt", "Hello,
#### Bucket Policies
Manage bucket policies to control access permissions. This feature is dependent on the policies provided by the storage service (e.g., AWS S3, MinIO).
Manage bucket policies to control access permissions. This feature depends on the policies provided by the storage service (e.g., AWS S3, MinIO).
#### Object Metadata
You can retrieve and modify object metadata. Metadata can be useful for storing additional information about an object.
Retrieve and modify object metadata. Metadata can be useful for storing additional information about an object.
To retrieve metadata:
@ -308,8 +308,6 @@ Remember, each cloud provider has specific features and limitations. `@push.rock
This guide covers the basic to advanced scenarios of using `@push.rocks/smartbucket`. For further details, refer to the API documentation and examples.
## License and Legal Information
This repository contains open-source code that is licensed under the MIT License. A copy of the MIT License can be found in the [license](license) file within this repository.

View File

@ -79,7 +79,7 @@ tap.test('prepare for directory style tests', async () => {
contents: 'dir3/dir4/file1.txt content',
});
await myBucket.fastPut({
path: 'file1.txt',
path: '/file1.txt',
contents: 'file1 content',
});
});

View File

@ -3,6 +3,6 @@
*/
export const commitinfo = {
name: '@push.rocks/smartbucket',
version: '3.0.6',
description: 'A TypeScript library for cloud-independent object storage, providing features like bucket creation, file and directory management, and data streaming.'
version: '3.0.9',
description: 'A TypeScript library offering simple and cloud-agnostic object storage with advanced features like bucket creation, file and directory management, and data streaming.'
}

View File

@ -1,6 +1,9 @@
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';
export class Bucket {
public static async getBucketByName(smartbucketRef: SmartBucket, bucketNameArg: string) {
@ -38,10 +41,19 @@ export class Bucket {
/**
* gets the base directory of the bucket
*/
public async getBaseDirectory() {
public async getBaseDirectory(): Promise<Directory> {
return new Directory(this, null, '');
}
public async getDirectoryFromPath(pathDescriptorArg: interfaces.IPathDecriptor): Promise<Directory> {
if (!pathDescriptorArg.path && !pathDescriptorArg.directory) {
return this.getBaseDirectory();
}
let checkPath = await helpers.reducePathDescriptorToPath(pathDescriptorArg);
const baseDirectory = await this.getBaseDirectory();
return await baseDirectory.getSubDirectoryByName(checkPath);
}
// ===============
// Fast Operations
// ===============
@ -53,28 +65,38 @@ export class Bucket {
path: string;
contents: string | Buffer;
overwrite?: boolean;
}): Promise<void> {
}): Promise<File> {
try {
const reducedPath = await helpers.reducePathDescriptorToPath({
path: optionsArg.path,
})
// Check if the object already exists
const exists = await this.fastExists({ path: optionsArg.path });
const exists = await this.fastExists({ path: reducedPath });
if (exists && !optionsArg.overwrite) {
console.error(`Object already exists at path '${optionsArg.path}' in bucket '${this.name}'.`);
console.error(`Object already exists at path '${reducedPath}' in bucket '${this.name}'.`);
return;
} else if (exists && optionsArg.overwrite) {
console.log(`Overwriting existing object at path '${optionsArg.path}' in bucket '${this.name}'.`);
console.log(`Overwriting existing object at path '${reducedPath}' in bucket '${this.name}'.`);
} else {
console.log(`Creating new object at path '${optionsArg.path}' in bucket '${this.name}'.`);
console.log(`Creating new object at path '${reducedPath}' in bucket '${this.name}'.`);
}
// Proceed with putting the object
const streamIntake = new plugins.smartstream.StreamIntake();
const putPromise = this.smartbucketRef.minioClient.putObject(this.name, optionsArg.path, streamIntake);
const putPromise = this.smartbucketRef.minioClient.putObject(this.name, reducedPath, streamIntake);
streamIntake.pushData(optionsArg.contents);
streamIntake.signalEnd();
await putPromise;
console.log(`Object '${optionsArg.path}' has been successfully stored in bucket '${this.name}'.`);
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;
@ -183,19 +205,10 @@ export class Bucket {
}
public async copyObject(optionsArg: {
/**
* the
*/
objectKey: string;
/**
* in case you want to copy to another bucket specify it here
*/
public async fastCopy(optionsArg: {
sourcePath: string;
destinationPath?: string;
targetBucket?: Bucket;
targetBucketKey?: string;
/**
* metadata will be merged with existing metadata
*/
nativeMetadata?: { [key: string]: string };
deleteExistingNativeMetadata?: boolean;
}): Promise<void> {
@ -205,7 +218,7 @@ export class Bucket {
// Retrieve current object information to use in copy conditions
const currentObjInfo = await this.smartbucketRef.minioClient.statObject(
targetBucketName,
optionsArg.objectKey
optionsArg.sourcePath
);
// Setting up copy conditions
@ -221,8 +234,8 @@ export class Bucket {
// TODO: check on issue here: https://github.com/minio/minio-js/issues/1286
await this.smartbucketRef.minioClient.copyObject(
this.name,
optionsArg.objectKey,
`/${targetBucketName}/${optionsArg.objectKey}`,
optionsArg.sourcePath,
`/${targetBucketName}/${optionsArg.destinationPath || optionsArg.sourcePath}`,
copyConditions
);
} catch (err) {
@ -231,6 +244,43 @@ export class Bucket {
}
}
/**
* Move object from one path to another within the same bucket or to another bucket
*/
public async fastMove(optionsArg: {
sourcePath: string;
destinationPath: string;
targetBucket?: Bucket;
overwrite?: boolean;
}): Promise<void> {
try {
// Check if the destination object already exists
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}'.`);
}
// Proceed with copying the object to the new path
await this.fastCopy(optionsArg);
// Remove the original object after successful copy
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
*/
@ -263,9 +313,56 @@ export class Bucket {
}
}
public async fastStat(optionsArg: {
path: string;
}) {
return this.smartbucketRef.minioClient.statObject(this.name, optionsArg.path);
public async fastStat(pathDescriptor: interfaces.IPathDecriptor) {
let checkPath = await helpers.reducePathDescriptorToPath(pathDescriptor);
return this.smartbucketRef.minioClient.statObject(this.name, checkPath);
}
public async isDirectory(pathDescriptor: interfaces.IPathDecriptor): Promise<boolean> {
let checkPath = await helpers.reducePathDescriptorToPath(pathDescriptor);
// lets check if the checkPath is a directory
const stream = this.smartbucketRef.minioClient.listObjectsV2(this.name, checkPath, true);
const done = plugins.smartpromise.defer<boolean>();
stream.on('data', (dataArg) => {
stream.destroy(); // Stop the stream early if we find at least one object
if (dataArg.prefix.startsWith(checkPath + '/')) {
done.resolve(true);
}
});
stream.on('end', () => {
done.resolve(false);
});
stream.on('error', (err) => {
done.reject(err);
});
return done.promise;
};
public async isFile(pathDescriptor: interfaces.IPathDecriptor): Promise<boolean> {
let checkPath = await helpers.reducePathDescriptorToPath(pathDescriptor);
// lets check if the checkPath is a directory
const stream = this.smartbucketRef.minioClient.listObjectsV2(this.name, checkPath, true);
const done = plugins.smartpromise.defer<boolean>();
stream.on('data', (dataArg) => {
stream.destroy(); // Stop the stream early if we find at least one object
if (dataArg.prefix === checkPath) {
done.resolve(true);
}
});
stream.on('end', () => {
done.resolve(false);
});
stream.on('error', (err) => {
done.reject(err);
});
return done.promise;
}
}

View File

@ -1,4 +1,6 @@
import * as plugins from './plugins.js';
import * as helpers from './helpers.js';
import * as interfaces from './interfaces.js';
import { Directory } from './classes.directory.js';
import { MetaData } from './classes.metadata.js';
@ -144,6 +146,29 @@ export class File {
}
}
/**
* moves the file to another directory
*/
public async move(pathDescriptorArg: interfaces.IPathDecriptor) {
let moveToPath = '';
const isDirectory = await this.parentDirectoryRef.bucketRef.isDirectory(pathDescriptorArg);
if (isDirectory) {
moveToPath = await helpers.reducePathDescriptorToPath({
...pathDescriptorArg,
path: plugins.path.join(pathDescriptorArg.path, this.name),
});
}
// lets move the file
await this.parentDirectoryRef.bucketRef.fastMove({
sourcePath: this.getBasePath(),
destinationPath: moveToPath,
});
// lets move the metadatafile
const metadata = await this.getMetaData();
await metadata.metadataFile.move(pathDescriptorArg);
}
/**
* allows updating the metadata of a file
* @param updatedMetadata

22
ts/helpers.ts Normal file
View File

@ -0,0 +1,22 @@
import * as plugins from './plugins.js';
import * as interfaces from './interfaces.js';
export const reducePathDescriptorToPath = async (pathDescriptorArg: interfaces.IPathDecriptor): Promise<string> => {
let returnPath = ``
if (pathDescriptorArg.directory) {
if (pathDescriptorArg.path && plugins.path.isAbsolute(pathDescriptorArg.path)) {
console.warn('Directory is being ignored when path is absolute.');
returnPath = pathDescriptorArg.path;
} else if (pathDescriptorArg.path) {
returnPath = plugins.path.join(pathDescriptorArg.directory.getBasePath(), pathDescriptorArg.path);
}
} else if (pathDescriptorArg.path) {
returnPath = pathDescriptorArg.path;
} else {
throw new Error('You must specify either a path or a directory.');
}
if (returnPath.startsWith('/')) {
returnPath = returnPath.substring(1);
}
return returnPath;
}

6
ts/interfaces.ts Normal file
View File

@ -0,0 +1,6 @@
import type { Directory } from "./classes.directory.js";
export interface IPathDecriptor {
path?: string;
directory?: Directory;
}