Akshar2325 commited on
Commit
67cf349
·
1 Parent(s): f75a84b

feat(old-minio-storage): add old minio version s3 storage module for file management

Browse files

- Add FileType enum with IMAGE, DOCUMENT, and VIDEO types
- Create OldMinioStorageConfig for environment variable configuration
- Implement OldMinioStorageService with presigned URL generation and file deletion
- Add OldMinioStorageController with endpoints for upload URLs and file deletion
- Create OldMinioStorageModule to integrate the storage provider
- Update .env.example with OldMinio storage credentials and configuration
- Register OldMinioStorageModule in the upload module
- Support S3-compatible MinIO API with presigned URL generation for secure uploads
- Enable file organization by type (images, documents, videos) with automatic folder routing

.env.example CHANGED
@@ -77,3 +77,11 @@ NEWMINIO_S3_ACCESS_KEY="your-access-key"
77
  NEWMINIO_S3_SECRET_KEY="your-secret-key"
78
  NEWMINIO_BUCKET="streamflix"
79
  NEWMINIO_REGION="us-east-1"
 
 
 
 
 
 
 
 
 
77
  NEWMINIO_S3_SECRET_KEY="your-secret-key"
78
  NEWMINIO_BUCKET="streamflix"
79
  NEWMINIO_REGION="us-east-1"
80
+
81
+ # OldMinio Storage Credentials
82
+ OLDMINIO_PROJECT_URL="https://your-old-minio-instance.com"
83
+ OLDMINIO_S3_ENDPOINT="https://your-old-minio-instance.com"
84
+ OLDMINIO_S3_ACCESS_KEY="your-access-key"
85
+ OLDMINIO_S3_SECRET_KEY="your-secret-key"
86
+ OLDMINIO_BUCKET="streamflix"
87
+ OLDMINIO_REGION="us-east-1"
src/shared/modules/old-minio-storage/enums/file-type.enum.ts ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ export enum FileType {
2
+ IMAGE = 'IMAGE',
3
+ DOCUMENT = 'DOCUMENT',
4
+ VIDEO = 'VIDEO',
5
+ }
src/shared/modules/old-minio-storage/old-minio-storage.config.ts ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ import { registerAs } from '@nestjs/config';
2
+
3
+ export default registerAs('oldMinioStorage', () => ({
4
+ endpoint: process.env.OLDMINIO_S3_ENDPOINT,
5
+ region: process.env.OLDMINIO_REGION || 'us-east-1',
6
+ accessKeyId: process.env.OLDMINIO_S3_ACCESS_KEY,
7
+ secretAccessKey: process.env.OLDMINIO_S3_SECRET_KEY,
8
+ bucket: process.env.OLDMINIO_BUCKET || 'streamflix',
9
+ projectUrl: process.env.OLDMINIO_PROJECT_URL,
10
+ }));
src/shared/modules/old-minio-storage/old-minio-storage.controller.ts ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import {
2
+ Controller,
3
+ Get,
4
+ Delete,
5
+ Query,
6
+ HttpCode,
7
+ HttpStatus,
8
+ } from '@nestjs/common';
9
+ import { ApiTags, ApiOperation, ApiQuery } from '@nestjs/swagger';
10
+ import { OldMinioStorageService } from './old-minio-storage.service';
11
+ import { FileType } from './enums/file-type.enum';
12
+
13
+ @ApiTags('OldMinio Storage')
14
+ @Controller('oldminio')
15
+ export class OldMinioStorageController {
16
+ constructor(
17
+ private readonly oldMinioStorageService: OldMinioStorageService,
18
+ ) {}
19
+
20
+ @Get('presigned-upload')
21
+ @ApiOperation({ summary: 'Get presigned upload URL' })
22
+ @ApiQuery({ name: 'fileName', required: true })
23
+ @ApiQuery({ name: 'fileType', enum: FileType, required: false })
24
+ async getPresignedUploadUrl(
25
+ @Query('fileName') fileName: string,
26
+ @Query('fileType') fileType?: FileType,
27
+ ) {
28
+ return this.oldMinioStorageService.getPresignedUploadUrl(
29
+ fileName,
30
+ fileType,
31
+ );
32
+ }
33
+
34
+ @Delete('by-url')
35
+ @HttpCode(HttpStatus.OK)
36
+ @ApiOperation({ summary: 'Delete file by URL' })
37
+ async deleteFileByUrl(@Query('url') url: string) {
38
+ const result = await this.oldMinioStorageService.deleteFileByUrl(url);
39
+ return {
40
+ message: 'File deleted successfully',
41
+ ...result,
42
+ };
43
+ }
44
+ }
src/shared/modules/old-minio-storage/old-minio-storage.module.ts ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Module } from '@nestjs/common';
2
+ import { ConfigModule } from '@nestjs/config';
3
+ import { OldMinioStorageController } from './old-minio-storage.controller';
4
+ import { OldMinioStorageService } from './old-minio-storage.service';
5
+ import oldMinioStorageConfig from './old-minio-storage.config';
6
+
7
+ @Module({
8
+ imports: [ConfigModule.forFeature(oldMinioStorageConfig)],
9
+ controllers: [OldMinioStorageController],
10
+ providers: [OldMinioStorageService],
11
+ exports: [OldMinioStorageService],
12
+ })
13
+ export class OldMinioStorageModule {}
src/shared/modules/old-minio-storage/old-minio-storage.service.ts ADDED
@@ -0,0 +1,173 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Injectable, Logger } from '@nestjs/common';
2
+ import { ConfigService } from '@nestjs/config';
3
+ import {
4
+ S3Client,
5
+ PutObjectCommand,
6
+ DeleteObjectCommand,
7
+ } from '@aws-sdk/client-s3';
8
+ import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
9
+ import { FileType } from './enums/file-type.enum';
10
+
11
+ @Injectable()
12
+ export class OldMinioStorageService {
13
+ private readonly logger = new Logger(OldMinioStorageService.name);
14
+ private s3Client: S3Client;
15
+ private bucket: string;
16
+ private region: string;
17
+ private endpoint: string;
18
+ private projectUrl: string;
19
+
20
+ constructor(private configService: ConfigService) {
21
+ this.endpoint = this.configService.get<string>('oldMinioStorage.endpoint')!;
22
+ this.region = this.configService.get<string>('oldMinioStorage.region')!;
23
+ this.bucket = this.configService.get<string>('oldMinioStorage.bucket')!;
24
+ this.projectUrl = this.configService.get<string>(
25
+ 'oldMinioStorage.projectUrl',
26
+ )!;
27
+
28
+ const accessKeyId = this.configService.get<string>(
29
+ 'oldMinioStorage.accessKeyId',
30
+ )!;
31
+ const secretAccessKey = this.configService.get<string>(
32
+ 'oldMinioStorage.secretAccessKey',
33
+ )!;
34
+
35
+ this.s3Client = new S3Client({
36
+ region: this.region,
37
+ endpoint: this.endpoint,
38
+ credentials: {
39
+ accessKeyId,
40
+ secretAccessKey,
41
+ },
42
+ forcePathStyle: true, // Required for MinIO
43
+ });
44
+
45
+ this.logger.log('Old MinIO Storage Service initialized');
46
+ }
47
+
48
+ /**
49
+ * Get folder path based on file type
50
+ * @param fileType - The file type (IMAGE, DOCUMENT, VIDEO)
51
+ * @returns Folder path for the file type
52
+ */
53
+ private getFolderPath(fileType: string): string {
54
+ switch (fileType?.toUpperCase()) {
55
+ case FileType.IMAGE:
56
+ return 'images';
57
+ case FileType.DOCUMENT:
58
+ return 'documents';
59
+ case FileType.VIDEO:
60
+ return 'videos';
61
+ default:
62
+ return 'files';
63
+ }
64
+ }
65
+
66
+ /**
67
+ * Generate presigned upload URL using S3
68
+ * @param fileName - The file name
69
+ * @param fileType - The file type (IMAGE, DOCUMENT, VIDEO)
70
+ * @returns Presigned upload URL and file path
71
+ */
72
+ async getPresignedUploadUrl(fileName: string, fileType?: string) {
73
+ try {
74
+ const folder = this.getFolderPath(fileType || '');
75
+ const timestamp = Date.now();
76
+ const sanitizedFileName = fileName.replace(/[^a-zA-Z0-9.-]/g, '_');
77
+ const key = `${folder}/${timestamp}-${sanitizedFileName}`;
78
+
79
+ const command = new PutObjectCommand({
80
+ Bucket: this.bucket,
81
+ Key: key,
82
+ });
83
+
84
+ const uploadUrl = await getSignedUrl(this.s3Client, command, {
85
+ expiresIn: 3600, // 1 hour
86
+ });
87
+
88
+ const fileUrl = `${this.projectUrl}/${this.bucket}/${key}`;
89
+
90
+ this.logger.log(`Generated presigned upload URL for: ${key}`);
91
+
92
+ return {
93
+ uploadUrl,
94
+ fileUrl,
95
+ key,
96
+ bucket: this.bucket,
97
+ expiresIn: 3600,
98
+ };
99
+ } catch (error) {
100
+ this.logger.error('Error generating presigned URL:', error);
101
+ throw error;
102
+ }
103
+ }
104
+
105
+ /**
106
+ * Delete a file from Old MinIO Storage using S3
107
+ * @param filePath - The file path in the bucket
108
+ * @returns Deletion result
109
+ */
110
+ async deleteFile(filePath: string): Promise<any> {
111
+ try {
112
+ const command = new DeleteObjectCommand({
113
+ Bucket: this.bucket,
114
+ Key: filePath,
115
+ });
116
+
117
+ await this.s3Client.send(command);
118
+
119
+ this.logger.log(`File deleted successfully: ${filePath}`);
120
+
121
+ return {
122
+ success: true,
123
+ filePath,
124
+ };
125
+ } catch (error) {
126
+ this.logger.error(`Error deleting file: ${filePath}`, error);
127
+ throw error;
128
+ }
129
+ }
130
+
131
+ /**
132
+ * Delete a file by URL
133
+ * @param url - The Old MinIO Storage file URL
134
+ * @returns Deletion result
135
+ */
136
+ async deleteFileByUrl(url: string): Promise<any> {
137
+ try {
138
+ const filePath = this.extractFilePathFromUrl(url);
139
+
140
+ if (!filePath) {
141
+ throw new Error('Invalid Old MinIO Storage URL');
142
+ }
143
+
144
+ return await this.deleteFile(filePath);
145
+ } catch (error) {
146
+ this.logger.error('Error deleting file by URL:', error);
147
+ throw error;
148
+ }
149
+ }
150
+
151
+ /**
152
+ * Extract file path from Old MinIO Storage URL
153
+ * @param url - The Old MinIO Storage URL
154
+ * @returns File path
155
+ */
156
+ private extractFilePathFromUrl(url: string): string | null {
157
+ try {
158
+ // URL format: https://lucifer925-minio-classic-oss-storage.hf.space/streamflix/images/1701234567890-avatar.jpg
159
+ const urlObj = new URL(url);
160
+ const pathParts = urlObj.pathname.split('/').filter(Boolean);
161
+
162
+ // Remove bucket name from path
163
+ if (pathParts[0] === this.bucket) {
164
+ pathParts.shift();
165
+ }
166
+
167
+ return pathParts.join('/');
168
+ } catch (error) {
169
+ this.logger.error('Error extracting file path from URL:', error);
170
+ return null;
171
+ }
172
+ }
173
+ }
src/shared/modules/upload/upload.module.ts CHANGED
@@ -4,6 +4,7 @@ import { CloudinaryModule } from './cloudinary/cloudinary.module';
4
  import { SupabaseStorageModule } from './supabase-storage/supabase-storage.module';
5
  import { BackblazeStorageModule } from './backblaze-storage/backblaze-storage.module';
6
  import { NewMinioStorageModule } from './new-minio-storage/new-minio-storage.module';
 
7
  import { FilestackModule } from './filestack/filestack.module';
8
 
9
  @Module({
@@ -14,6 +15,7 @@ import { FilestackModule } from './filestack/filestack.module';
14
  BackblazeStorageModule,
15
  FilestackModule,
16
  NewMinioStorageModule,
 
17
  ],
18
  })
19
  export class UploadModule {}
 
4
  import { SupabaseStorageModule } from './supabase-storage/supabase-storage.module';
5
  import { BackblazeStorageModule } from './backblaze-storage/backblaze-storage.module';
6
  import { NewMinioStorageModule } from './new-minio-storage/new-minio-storage.module';
7
+ import { OldMinioStorageModule } from '../old-minio-storage/old-minio-storage.module';
8
  import { FilestackModule } from './filestack/filestack.module';
9
 
10
  @Module({
 
15
  BackblazeStorageModule,
16
  FilestackModule,
17
  NewMinioStorageModule,
18
+ OldMinioStorageModule,
19
  ],
20
  })
21
  export class UploadModule {}