fix: use overwrite to make metadata files work #2
| @@ -1,5 +1,14 @@ | ||||
| # Changelog | ||||
|  | ||||
| ## 2024-11-24 - 3.2.0 - feat(bucket) | ||||
| Enhanced SmartBucket with trash management and metadata handling | ||||
|  | ||||
| - Added functionality to move files to a trash directory. | ||||
| - Introduced methods to handle file metadata more robustly. | ||||
| - Implemented a method to clean all contents from a bucket. | ||||
| - Enhanced directory retrieval to handle non-existent directories with options. | ||||
| - Improved handling of file paths and metadata within the storage system. | ||||
|  | ||||
| ## 2024-11-18 - 3.1.0 - feat(file) | ||||
| Added functionality to retrieve magic bytes from files and detect file types using magic bytes. | ||||
|  | ||||
|   | ||||
							
								
								
									
										4
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										4
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							| @@ -1,12 +1,12 @@ | ||||
| { | ||||
|   "name": "@push.rocks/smartbucket", | ||||
|   "version": "3.1.0", | ||||
|   "version": "3.2.0", | ||||
|   "lockfileVersion": 3, | ||||
|   "requires": true, | ||||
|   "packages": { | ||||
|     "": { | ||||
|       "name": "@push.rocks/smartbucket", | ||||
|       "version": "3.1.0", | ||||
|       "version": "3.2.0", | ||||
|       "license": "UNLICENSED", | ||||
|       "dependencies": { | ||||
|         "@push.rocks/smartpath": "^5.0.18", | ||||
|   | ||||
							
								
								
									
										10
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								package.json
									
									
									
									
									
								
							| @@ -1,6 +1,6 @@ | ||||
| { | ||||
|   "name": "@push.rocks/smartbucket", | ||||
|   "version": "3.1.0", | ||||
|   "version": "3.2.0", | ||||
|   "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", | ||||
| @@ -15,16 +15,16 @@ | ||||
|     "@git.zone/tsbuild": "^2.1.84", | ||||
|     "@git.zone/tsrun": "^1.2.49", | ||||
|     "@git.zone/tstest": "^1.0.90", | ||||
|     "@push.rocks/qenv": "^6.0.5", | ||||
|     "@push.rocks/tapbundle": "^5.3.0" | ||||
|     "@push.rocks/qenv": "^6.1.0", | ||||
|     "@push.rocks/tapbundle": "^5.5.3" | ||||
|   }, | ||||
|   "dependencies": { | ||||
|     "@aws-sdk/client-s3": "^3.693.0", | ||||
|     "@aws-sdk/client-s3": "^3.699.0", | ||||
|     "@push.rocks/smartmime": "^2.0.4", | ||||
|     "@push.rocks/smartpath": "^5.0.18", | ||||
|     "@push.rocks/smartpromise": "^4.0.4", | ||||
|     "@push.rocks/smartrx": "^3.0.7", | ||||
|     "@push.rocks/smartstream": "^3.2.4", | ||||
|     "@push.rocks/smartstream": "^3.2.5", | ||||
|     "@push.rocks/smartstring": "^4.0.15", | ||||
|     "@push.rocks/smartunique": "^3.0.9", | ||||
|     "@tsclass/tsclass": "^4.1.2" | ||||
|   | ||||
							
								
								
									
										1542
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										1542
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										0
									
								
								test/helpers/prepare.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								test/helpers/prepare.ts
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										7
									
								
								test/test.metadata.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								test/test.metadata.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | ||||
| import { tap, expect } from '@push.rocks/tapbundle'; | ||||
|  | ||||
| tap.test('test metadata functionality', async () => { | ||||
|  | ||||
| }) | ||||
|  | ||||
| tap.start(); | ||||
| @@ -1,4 +1,5 @@ | ||||
| import { expect, expectAsync, tap } from '@push.rocks/tapbundle'; | ||||
| import { jestExpect } from '@push.rocks/tapbundle/node'; | ||||
| import { Qenv } from '@push.rocks/qenv'; | ||||
|  | ||||
| import * as smartbucket from '../ts/index.js'; | ||||
| @@ -11,18 +12,52 @@ let baseDirectory: smartbucket.Directory; | ||||
|  | ||||
| tap.test('should create a valid smartbucket', async () => { | ||||
|   testSmartbucket = new smartbucket.SmartBucket({ | ||||
|     accessKey: await testQenv.getEnvVarOnDemand('S3_KEY'), | ||||
|     accessSecret: await testQenv.getEnvVarOnDemand('S3_SECRET'), | ||||
|     endpoint: await testQenv.getEnvVarOnDemand('S3_ENDPOINT'), | ||||
|     accessKey: await testQenv.getEnvVarOnDemandStrict('S3_ACCESSKEY'), | ||||
|     accessSecret: await testQenv.getEnvVarOnDemandStrict('S3_ACCESSSECRET'), | ||||
|     endpoint: await testQenv.getEnvVarOnDemandStrict('S3_ENDPOINT'), | ||||
|   }); | ||||
|   expect(testSmartbucket).toBeInstanceOf(smartbucket.SmartBucket); | ||||
|   myBucket = await testSmartbucket.getBucketByName('testzone'); | ||||
|   myBucket = await testSmartbucket.getBucketByNameStrict(await testQenv.getEnvVarOnDemandStrict('S3_BUCKET'),); | ||||
|   expect(myBucket).toBeInstanceOf(smartbucket.Bucket); | ||||
|   expect(myBucket.name).toEqual('testzone'); | ||||
|   expect(myBucket.name).toEqual('test-pushrocks-smartbucket'); | ||||
| }); | ||||
|  | ||||
| tap.test('', async () => { | ||||
|    | ||||
| }) | ||||
| tap.test('should clean all contents', async () => { | ||||
|   await myBucket.cleanAllContents(); | ||||
|   expect(await myBucket.fastExists({ path: 'hithere/socool.txt' })).toBeFalse(); | ||||
|   expect(await myBucket.fastExists({ path: 'trashtest/trashme.txt' })).toBeFalse(); | ||||
| }); | ||||
|  | ||||
| export default tap.start(); | ||||
| tap.test('should delete a file into the normally', async () => { | ||||
|   const path = 'trashtest/trashme.txt'; | ||||
|   const file = await myBucket.fastPut({ | ||||
|     path, | ||||
|     contents: 'I\'m in the trash test content!', | ||||
|   }); | ||||
|   const fileMetadata = await (await file.getMetaData()).metadataFile.getContents(); | ||||
|   console.log(fileMetadata.toString()); | ||||
|   expect(await file.getMetaData().then((meta) => meta.metadataFile.getJsonData())).toEqual({}); | ||||
|   await file.delete({ mode: 'permanent' }); | ||||
|   expect((await (await myBucket.getBaseDirectory()).listFiles()).length).toEqual(0);   | ||||
|   expect((await (await myBucket.getBaseDirectory()).listDirectories()).length).toEqual(0);   | ||||
| }); | ||||
|  | ||||
| tap.test('should put a file into the trash', async () => { | ||||
|   const path = 'trashtest/trashme.txt'; | ||||
|   const file = await myBucket.fastPut({ | ||||
|     path, | ||||
|     contents: 'I\'m in the trash test content!', | ||||
|   }); | ||||
|   const fileMetadata = await (await file.getMetaData()).metadataFile.getContents(); | ||||
|   console.log(fileMetadata.toString()); | ||||
|   expect(await file.getMetaData().then((meta) => meta.metadataFile.getJsonData())).toEqual({}); | ||||
|   await file.delete({ mode: 'trash' }); | ||||
|   jestExpect(await file.getMetaData().then((meta) => meta.metadataFile.getJsonData())).toEqual({ | ||||
|     custom_recycle: { | ||||
|       deletedAt: jestExpect.any(Number), | ||||
|       originalPath: "trashtest/trashme.txt", | ||||
|     }, | ||||
|   }); | ||||
| }); | ||||
|  | ||||
| export default tap.start(); | ||||
|   | ||||
							
								
								
									
										28
									
								
								test/test.ts
									
									
									
									
									
								
							
							
						
						
									
										28
									
								
								test/test.ts
									
									
									
									
									
								
							| @@ -11,14 +11,20 @@ let baseDirectory: smartbucket.Directory; | ||||
|  | ||||
| tap.test('should create a valid smartbucket', async () => { | ||||
|   testSmartbucket = new smartbucket.SmartBucket({ | ||||
|     accessKey: await testQenv.getEnvVarOnDemand('S3_KEY'), | ||||
|     accessSecret: await testQenv.getEnvVarOnDemand('S3_SECRET'), | ||||
|     endpoint: await testQenv.getEnvVarOnDemand('S3_ENDPOINT'), | ||||
|     accessKey: await testQenv.getEnvVarOnDemandStrict('S3_ACCESSKEY'), | ||||
|     accessSecret: await testQenv.getEnvVarOnDemandStrict('S3_ACCESSSECRET'), | ||||
|     endpoint: await testQenv.getEnvVarOnDemandStrict('S3_ENDPOINT'), | ||||
|   }); | ||||
|   expect(testSmartbucket).toBeInstanceOf(smartbucket.SmartBucket); | ||||
|   myBucket = await testSmartbucket.getBucketByName('testzone'); | ||||
|   myBucket = await testSmartbucket.getBucketByNameStrict(await testQenv.getEnvVarOnDemandStrict('S3_BUCKET'),); | ||||
|   expect(myBucket).toBeInstanceOf(smartbucket.Bucket); | ||||
|   expect(myBucket.name).toEqual('testzone'); | ||||
|   expect(myBucket.name).toEqual('test-pushrocks-smartbucket'); | ||||
| }); | ||||
|  | ||||
| tap.test('should clean all contents', async () => { | ||||
|   await myBucket.cleanAllContents(); | ||||
|   expect(await myBucket.fastExists({ path: 'hithere/socool.txt' })).toBeFalse(); | ||||
|   expect(await myBucket.fastExists({ path: 'trashtest/trashme.txt' })).toBeFalse(); | ||||
| }); | ||||
|  | ||||
| tap.skip.test('should create testbucket', async () => { | ||||
| @@ -41,9 +47,12 @@ tap.test('should get data in bucket', async () => { | ||||
|   const fileString = await myBucket.fastGet({ | ||||
|     path: 'hithere/socool.txt', | ||||
|   }); | ||||
|   const fileStringStream = await myBucket.fastGetStream({ | ||||
|     path: 'hithere/socool.txt', | ||||
|   }, 'nodestream'); | ||||
|   const fileStringStream = await myBucket.fastGetStream( | ||||
|     { | ||||
|       path: 'hithere/socool.txt', | ||||
|     }, | ||||
|     'nodestream' | ||||
|   ); | ||||
|   console.log(fileString); | ||||
| }); | ||||
|  | ||||
| @@ -97,8 +106,9 @@ tap.test('should get base directory', async () => { | ||||
| tap.test('should correctly build paths for sub directories', async () => { | ||||
|   const dir4 = await baseDirectory.getSubDirectoryByName('dir3/dir4'); | ||||
|   expect(dir4).toBeInstanceOf(smartbucket.Directory); | ||||
|   const dir4BasePath = dir4.getBasePath(); | ||||
|   const dir4BasePath = dir4?.getBasePath(); | ||||
|   console.log(dir4BasePath); | ||||
|   expect(dir4BasePath).toEqual('dir3/dir4/'); | ||||
| }); | ||||
|  | ||||
| tap.test('clean up directory style tests', async () => { | ||||
|   | ||||
| @@ -3,6 +3,6 @@ | ||||
|  */ | ||||
| export const commitinfo = { | ||||
|   name: '@push.rocks/smartbucket', | ||||
|   version: '3.1.0', | ||||
|   version: '3.2.0', | ||||
|   description: 'A TypeScript library offering simple and cloud-agnostic object storage with advanced features like bucket creation, file and directory management, and data streaming.' | ||||
| } | ||||
|   | ||||
| @@ -52,7 +52,7 @@ export class Bucket { | ||||
|    * gets the base directory of the bucket | ||||
|    */ | ||||
|   public async getBaseDirectory(): Promise<Directory> { | ||||
|     return new Directory(this, null, ''); | ||||
|     return new Directory(this, null!, ''); | ||||
|   } | ||||
|  | ||||
|   /** | ||||
| @@ -71,7 +71,9 @@ export class Bucket { | ||||
|     } | ||||
|     const checkPath = await helpers.reducePathDescriptorToPath(pathDescriptorArg); | ||||
|     const baseDirectory = await this.getBaseDirectory(); | ||||
|     return await baseDirectory.getSubDirectoryByName(checkPath); | ||||
|     return await baseDirectory.getSubDirectoryByNameStrict(checkPath, { | ||||
|       getEmptyDirectory: true, | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   // =============== | ||||
| @@ -331,7 +333,9 @@ export class Bucket { | ||||
|   }): Promise<void> { | ||||
|     try { | ||||
|       const destinationBucket = optionsArg.targetBucket || this; | ||||
|       const exists = await destinationBucket.fastExists({ path: optionsArg.destinationPath }); | ||||
|       const exists = await destinationBucket.fastExists({ | ||||
|         path: optionsArg.destinationPath, | ||||
|       }); | ||||
|  | ||||
|       if (exists && !optionsArg.overwrite) { | ||||
|         console.error( | ||||
| @@ -424,8 +428,8 @@ export class Bucket { | ||||
|       Prefix: checkPath, | ||||
|       Delimiter: '/', | ||||
|     }); | ||||
|     const response = await this.smartbucketRef.s3Client.send(command); | ||||
|     return response.CommonPrefixes.length > 0; | ||||
|     const { CommonPrefixes } = await this.smartbucketRef.s3Client.send(command); | ||||
|     return !!CommonPrefixes && CommonPrefixes.length > 0; | ||||
|   } | ||||
|  | ||||
|   public async isFile(pathDescriptor: interfaces.IPathDecriptor): Promise<boolean> { | ||||
| @@ -435,8 +439,8 @@ export class Bucket { | ||||
|       Prefix: checkPath, | ||||
|       Delimiter: '/', | ||||
|     }); | ||||
|     const response = await this.smartbucketRef.s3Client.send(command); | ||||
|     return response.Contents.length > 0; | ||||
|     const { Contents } = await this.smartbucketRef.s3Client.send(command); | ||||
|     return !!Contents && Contents.length > 0; | ||||
|   } | ||||
|  | ||||
|   public async getMagicBytes(optionsArg: { path: string; length: number }): Promise<Buffer> { | ||||
| @@ -449,7 +453,7 @@ export class Bucket { | ||||
|       const response = await this.smartbucketRef.s3Client.send(command); | ||||
|       const chunks = []; | ||||
|       const stream = response.Body as any; // SdkStreamMixin includes readable stream | ||||
|    | ||||
|  | ||||
|       for await (const chunk of stream) { | ||||
|         chunks.push(chunk); | ||||
|       } | ||||
| @@ -462,4 +466,52 @@ export class Bucket { | ||||
|       throw error; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   public async cleanAllContents(): Promise<void> { | ||||
|     try { | ||||
|       // Define the command type explicitly | ||||
|       const listCommandInput: plugins.s3.ListObjectsV2CommandInput = { | ||||
|         Bucket: this.name, | ||||
|       }; | ||||
|    | ||||
|       let isTruncated = true; | ||||
|       let continuationToken: string | undefined = 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: plugins.s3.ListObjectsV2Output = | ||||
|           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; | ||||
|     } | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -69,7 +69,7 @@ export class Directory { | ||||
|     path: string; | ||||
|     createWithContents?: string | Buffer; | ||||
|     getFromTrash?: boolean; | ||||
|   }): Promise<File> { | ||||
|   }): Promise<File | null> { | ||||
|     const pathDescriptor = { | ||||
|       directory: this, | ||||
|       path: optionsArg.path, | ||||
| @@ -98,6 +98,19 @@ export class Directory { | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * gets a file strictly | ||||
|    * @param args  | ||||
|    * @returns  | ||||
|    */ | ||||
|   public async getFileStrict(...args: Parameters<Directory['getFile']>) { | ||||
|     const file = await this.getFile(...args); | ||||
|     if (!file) { | ||||
|       throw new Error(`File not found at path '${args[0].path}'`); | ||||
|     } | ||||
|     return file; | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * lists all files | ||||
|    */ | ||||
| @@ -110,7 +123,7 @@ export class Directory { | ||||
|     const response = await this.bucketRef.smartbucketRef.s3Client.send(command); | ||||
|     const fileArray: File[] = []; | ||||
|  | ||||
|     response.Contents.forEach((item) => { | ||||
|     response.Contents?.forEach((item) => { | ||||
|       if (item.Key && !item.Key.endsWith('/')) { | ||||
|         const subtractedPath = item.Key.replace(this.getBasePath(), ''); | ||||
|         if (!subtractedPath.includes('/')) { | ||||
| @@ -178,23 +191,53 @@ export class Directory { | ||||
|   /** | ||||
|    * gets a sub directory by name | ||||
|    */ | ||||
|   public async getSubDirectoryByName(dirNameArg: string): Promise<Directory> { | ||||
|     const dirNameArray = dirNameArg.split('/'); | ||||
|   public async getSubDirectoryByName(dirNameArg: string, optionsArg: { | ||||
|     getEmptyDirectory?: boolean; | ||||
|     createWithInitializerFile?: boolean; | ||||
|   } = {}): Promise<Directory | null> { | ||||
|     const dirNameArray = dirNameArg.split('/').filter(str => str.trim() !== ""); | ||||
|  | ||||
|     const getDirectory = async (directoryArg: Directory, dirNameToSearch: string) => { | ||||
|       const directories = await directoryArg.listDirectories(); | ||||
|       return directories.find((directory) => { | ||||
|         return directory.name === dirNameToSearch; | ||||
|       }); | ||||
|     }; | ||||
|  | ||||
|     let wantedDirectory: Directory; | ||||
|     for (const dirNameToSearch of dirNameArray) { | ||||
|       const directoryToSearchIn = wantedDirectory ? wantedDirectory : this; | ||||
|       wantedDirectory = await getDirectory(directoryToSearchIn, dirNameToSearch); | ||||
|     optionsArg = { | ||||
|       getEmptyDirectory: false, | ||||
|       createWithInitializerFile: false, | ||||
|       ...optionsArg, | ||||
|     } | ||||
|  | ||||
|     return wantedDirectory; | ||||
|  | ||||
|     const getDirectory = async (directoryArg: Directory, dirNameToSearch: string, isFinalDirectory: boolean) => { | ||||
|       const directories = await directoryArg.listDirectories(); | ||||
|       let returnDirectory = directories.find((directory) => { | ||||
|         return directory.name === dirNameToSearch; | ||||
|       }); | ||||
|       if (returnDirectory) { | ||||
|         return returnDirectory; | ||||
|       } | ||||
|       if (optionsArg.getEmptyDirectory || optionsArg.createWithInitializerFile) { | ||||
|         returnDirectory = new Directory(this.bucketRef, this, dirNameToSearch); | ||||
|       } | ||||
|       if (isFinalDirectory && optionsArg.createWithInitializerFile) { | ||||
|         returnDirectory?.createEmptyFile('00init.txt'); | ||||
|       } | ||||
|       return returnDirectory || null; | ||||
|     }; | ||||
|  | ||||
|     let wantedDirectory: Directory | null = null; | ||||
|     let counter = 0;  | ||||
|     for (const dirNameToSearch of dirNameArray) { | ||||
|       counter++; | ||||
|       const directoryToSearchIn = wantedDirectory ? wantedDirectory : this; | ||||
|       wantedDirectory = await getDirectory(directoryToSearchIn, dirNameToSearch, counter === dirNameArray.length); | ||||
|     } | ||||
|  | ||||
|     return wantedDirectory || null; | ||||
|   } | ||||
|  | ||||
|   public async getSubDirectoryByNameStrict(...args: Parameters<Directory['getSubDirectoryByName']>) { | ||||
|     const directory = await this.getSubDirectoryByName(...args); | ||||
|     if (!directory) { | ||||
|       throw new Error(`Directory not found at path '${args[0]}'`); | ||||
|     } | ||||
|     return directory; | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|   | ||||
| @@ -92,24 +92,23 @@ export class File { | ||||
|   /** | ||||
|    * deletes this file | ||||
|    */ | ||||
|   public async delete(optionsArg?: { | ||||
|     mode: 'trash' | 'permanent'; | ||||
|   }) { | ||||
|  | ||||
|   public async delete(optionsArg?: { mode: 'trash' | 'permanent' }) { | ||||
|     optionsArg = { | ||||
|       ... { | ||||
|       ...{ | ||||
|         mode: 'permanent', | ||||
|       }, | ||||
|       ...optionsArg, | ||||
|     } | ||||
|     }; | ||||
|  | ||||
|     if (optionsArg.mode === 'permanent') { | ||||
|       await this.parentDirectoryRef.bucketRef.fastRemove({ | ||||
|         path: this.getBasePath(), | ||||
|       }); | ||||
|       if (!this.name.endsWith('.metadata')) { | ||||
|         const metadata = await this.getMetaData(); | ||||
|         await metadata.metadataFile.delete(optionsArg); | ||||
|         if (await this.hasMetaData()) { | ||||
|           const metadata = await this.getMetaData(); | ||||
|           await metadata.metadataFile.delete(optionsArg); | ||||
|         } | ||||
|       } | ||||
|     } else if (optionsArg.mode === 'trash') { | ||||
|       const metadata = await this.getMetaData(); | ||||
| @@ -121,12 +120,13 @@ export class File { | ||||
|         }, | ||||
|       }); | ||||
|       const trash = await this.parentDirectoryRef.bucketRef.getTrash(); | ||||
|       const trashDir = await trash.getTrashDir(); | ||||
|       await this.move({ | ||||
|         directory: await trash.getTrashDir(), | ||||
|         directory: trashDir, | ||||
|         path: await trash.getTrashKeyByOriginalBasePath(this.getBasePath()), | ||||
|       }); | ||||
|     } | ||||
|      | ||||
|  | ||||
|     await this.parentDirectoryRef.listFiles(); | ||||
|   } | ||||
|  | ||||
| @@ -169,16 +169,19 @@ export class File { | ||||
|       await this.parentDirectoryRef.bucketRef.fastPutStream({ | ||||
|         path: this.getBasePath(), | ||||
|         readableStream: optionsArg.contents, | ||||
|         overwrite: true, | ||||
|       }); | ||||
|     } else if (Buffer.isBuffer(optionsArg.contents)) { | ||||
|       await this.parentDirectoryRef.bucketRef.fastPut({ | ||||
|         path: this.getBasePath(), | ||||
|         contents: optionsArg.contents, | ||||
|         overwrite: true, | ||||
|       }); | ||||
|     } else if (typeof optionsArg.contents === 'string') { | ||||
|       await this.parentDirectoryRef.bucketRef.fastPut({ | ||||
|         path: this.getBasePath(), | ||||
|         contents: Buffer.from(optionsArg.contents, optionsArg.encoding), | ||||
|         overwrite: true, | ||||
|       }); | ||||
|     } | ||||
|   } | ||||
| @@ -187,23 +190,49 @@ export class File { | ||||
|    * moves the file to another directory | ||||
|    */ | ||||
|   public async move(pathDescriptorArg: interfaces.IPathDecriptor) { | ||||
|     let moveToPath = ''; | ||||
|     let moveToPath: string = ''; | ||||
|     const isDirectory = await this.parentDirectoryRef.bucketRef.isDirectory(pathDescriptorArg); | ||||
|     if (isDirectory) { | ||||
|       moveToPath = await helpers.reducePathDescriptorToPath({ | ||||
|         ...pathDescriptorArg, | ||||
|         path: plugins.path.join(pathDescriptorArg.path!, this.name), | ||||
|       }); | ||||
|     } else { | ||||
|       moveToPath = await helpers.reducePathDescriptorToPath(pathDescriptorArg); | ||||
|     } | ||||
|     // lets move the file | ||||
|     await this.parentDirectoryRef.bucketRef.fastMove({ | ||||
|       sourcePath: this.getBasePath(), | ||||
|       destinationPath: moveToPath, | ||||
|       overwrite: true, | ||||
|     }); | ||||
|  | ||||
|     // lets move the metadatafile | ||||
|     const metadata = await this.getMetaData(); | ||||
|     await metadata.metadataFile.move(pathDescriptorArg); | ||||
|     if (!this.name.endsWith('.metadata')) { | ||||
|       const metadata = await this.getMetaData(); | ||||
|       await this.parentDirectoryRef.bucketRef.fastMove({ | ||||
|         sourcePath: metadata.metadataFile.getBasePath(), | ||||
|         destinationPath: moveToPath + '.metadata', | ||||
|         overwrite: true, | ||||
|       }); | ||||
|     } | ||||
|  | ||||
|     // lets update references of this | ||||
|     const baseDirectory = await this.parentDirectoryRef.bucketRef.getBaseDirectory(); | ||||
|     this.parentDirectoryRef = await baseDirectory.getSubDirectoryByNameStrict( | ||||
|       pathDescriptorArg.directory?.getBasePath()! | ||||
|     ); | ||||
|     this.name = pathDescriptorArg.path!; | ||||
|   } | ||||
|  | ||||
|   public async hasMetaData(): Promise<boolean> { | ||||
|     if (!this.name.endsWith('.metadata')) { | ||||
|       const hasMetadataBool = MetaData.hasMetaData({ | ||||
|         file: this, | ||||
|       }); | ||||
|       return hasMetadataBool; | ||||
|     } else { | ||||
|       return false; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   /** | ||||
| @@ -238,7 +267,7 @@ export class File { | ||||
|   public async getMagicBytes(optionsArg: { length: number }): Promise<Buffer> { | ||||
|     return this.parentDirectoryRef.bucketRef.getMagicBytes({ | ||||
|       path: this.getBasePath(), | ||||
|       length: optionsArg.length | ||||
|     }) | ||||
|       length: optionsArg.length, | ||||
|     }); | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -3,13 +3,21 @@ import * as plugins from './plugins.js'; | ||||
| import { File } from './classes.file.js'; | ||||
|  | ||||
| export class MetaData { | ||||
|   public static async hasMetaData(optionsArg: { file: File }) { | ||||
|     // lets find the existing metadata file | ||||
|     const existingFile = await optionsArg.file.parentDirectoryRef.getFile({ | ||||
|       path: optionsArg.file.name + '.metadata', | ||||
|     }); | ||||
|     return !!existingFile; | ||||
|   } | ||||
|  | ||||
|   // static | ||||
|   public static async createForFile(optionsArg: { file: File }) { | ||||
|     const metaData = new MetaData(); | ||||
|     metaData.fileRef = optionsArg.file; | ||||
|  | ||||
|     // lets find the existing metadata file | ||||
|     metaData.metadataFile = await metaData.fileRef.parentDirectoryRef.getFile({ | ||||
|     metaData.metadataFile = await metaData.fileRef.parentDirectoryRef.getFileStrict({ | ||||
|       path: metaData.fileRef.name + '.metadata', | ||||
|       createWithContents: '{}', | ||||
|     }); | ||||
|   | ||||
| @@ -41,7 +41,15 @@ export class SmartBucket { | ||||
|     await Bucket.removeBucketByName(this, bucketName); | ||||
|   } | ||||
|  | ||||
|   public async getBucketByName(bucketName: string) { | ||||
|     return Bucket.getBucketByName(this, bucketName); | ||||
|   public async getBucketByName(bucketNameArg: string) { | ||||
|     return Bucket.getBucketByName(this, bucketNameArg); | ||||
|   } | ||||
|  | ||||
|   public async getBucketByNameStrict(...args: Parameters<SmartBucket['getBucketByName']>) { | ||||
|     const bucket = await this.getBucketByName(...args); | ||||
|     if (!bucket) { | ||||
|       throw new Error(`Bucket ${args[0]} does not exist.`); | ||||
|     } | ||||
|     return bucket; | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -21,7 +21,7 @@ export class Trash { | ||||
|     const trashDir = await this.getTrashDir(); | ||||
|     const originalPath = await helpers.reducePathDescriptorToPath(pathDescriptor); | ||||
|     const trashKey = await this.getTrashKeyByOriginalBasePath(originalPath); | ||||
|     return trashDir.getFile({ path: trashKey }); | ||||
|     return trashDir.getFileStrict({ path: trashKey }); | ||||
|   } | ||||
|  | ||||
|   public async getTrashKeyByOriginalBasePath (originalPath: string): Promise<string> { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user