55 GetObjectCommand ,
66 DeleteObjectCommand ,
77 ListBucketsCommand ,
8+ ListObjectsV2Command ,
9+ DeleteObjectsCommand ,
810} from '@aws-sdk/client-s3' ;
911import { getSignedUrl } from '@aws-sdk/s3-request-presigner' ;
1012import { FileNotExistError , Defaults } from '@impler/shared' ;
@@ -28,6 +30,7 @@ export abstract class StorageService {
2830 abstract getFileStream ( key : string ) : Promise < Readable > ;
2931 abstract writeStream ( key : string , stream : Readable , contentType : string ) : Promise < void > ;
3032 abstract deleteFile ( key : string ) : Promise < void > ;
33+ abstract deleteFolder ( key : string ) : Promise < void > ;
3134 abstract isConnected ( ) : boolean ;
3235 abstract getSignedUrl ( key : string ) : Promise < string > ;
3336}
@@ -64,6 +67,46 @@ export class S3StorageService implements StorageService {
6467 this . isS3Connected = false ;
6568 } ) ;
6669 }
70+ async deleteFolder ( prefix : string ) : Promise < void > {
71+ try {
72+ // Ensure prefix ends with '/' if it's not empty
73+ const folderPrefix = prefix && ! prefix . endsWith ( '/' ) ? `${ prefix } /` : prefix ;
74+
75+ let continuationToken : string | undefined ;
76+
77+ do {
78+ // List objects with the given prefix
79+ const listCommand = new ListObjectsV2Command ( {
80+ Bucket : process . env . S3_BUCKET_NAME ,
81+ Prefix : folderPrefix ,
82+ ContinuationToken : continuationToken ,
83+ } ) ;
84+
85+ const listResponse = await this . s3 . send ( listCommand ) ;
86+
87+ if ( listResponse . Contents && listResponse . Contents . length > 0 ) {
88+ // Prepare objects for deletion (max 1000 objects per delete request)
89+ const objectsToDelete = listResponse . Contents . map ( ( obj ) => ( {
90+ Key : obj . Key ,
91+ } ) ) ;
92+
93+ // Delete the objects
94+ const deleteCommand = new DeleteObjectsCommand ( {
95+ Bucket : process . env . S3_BUCKET_NAME ,
96+ Delete : {
97+ Objects : objectsToDelete ,
98+ Quiet : true , // Don't return details about deleted objects
99+ } ,
100+ } ) ;
101+
102+ await this . s3 . send ( deleteCommand ) ;
103+ }
104+
105+ // Check if there are more objects to delete
106+ continuationToken = listResponse . NextContinuationToken ;
107+ } while ( continuationToken ) ;
108+ } catch ( error ) { }
109+ }
67110
68111 async uploadFile ( key : string , file : Buffer | string | Readable , contentType : string ) : Promise < IStorageResponse > {
69112 const command = new PutObjectCommand ( {
@@ -275,6 +318,13 @@ export class AzureStorageService implements StorageService {
275318 await blockBlobClient . delete ( ) ;
276319 }
277320
321+ async deleteFolder ( prefix : string ) : Promise < void > {
322+ for await ( const blob of this . containerClient . listBlobsFlat ( { prefix } ) ) {
323+ const blockBlobClient = this . containerClient . getBlockBlobClient ( blob . name ) ;
324+ await blockBlobClient . deleteIfExists ( ) ;
325+ }
326+ }
327+
278328 isConnected ( ) : boolean {
279329 return this . isAzureConnected ;
280330 }
0 commit comments