Spaces:
Runtime error
Runtime error
Akshar2325 commited on
Commit ·
3b3ab98
1
Parent(s): 2d96a3f
feat(imagekit): add folder management and cdn cache purging
Browse files- Organize uploads into type-specific folders (images, documents, etc.).
- Automatically purge cdn cache upon file deletion for immediate effect.
refactor(imagekit): streamline api endpoints and service logic
- Rename endpoints for clarity (`/auth` to `/signed-upload`, `/by-url`).
- Remove unused endpoints and service methods for file details and deletion by id.
- Simplify swagger documentation for controller endpoints.
fix(imagekit): make file lookup by url more robust
- Clean urls by removing query parameters before searching.
- Prevents lookup failures for urls with versioning or tracking params.
src/shared/modules/imagekitio/imagekitio.controller.ts
CHANGED
|
@@ -2,18 +2,11 @@ import {
|
|
| 2 |
Controller,
|
| 3 |
Get,
|
| 4 |
Delete,
|
| 5 |
-
Param,
|
| 6 |
Query,
|
| 7 |
HttpCode,
|
| 8 |
HttpStatus,
|
| 9 |
} from '@nestjs/common';
|
| 10 |
-
import {
|
| 11 |
-
ApiTags,
|
| 12 |
-
ApiOperation,
|
| 13 |
-
ApiResponse,
|
| 14 |
-
ApiParam,
|
| 15 |
-
ApiQuery,
|
| 16 |
-
} from '@nestjs/swagger';
|
| 17 |
import { ImagekitioService } from './imagekitio.service';
|
| 18 |
import { FileType } from './enums/file-type.enum';
|
| 19 |
|
|
@@ -22,97 +15,22 @@ import { FileType } from './enums/file-type.enum';
|
|
| 22 |
export class ImagekitioController {
|
| 23 |
constructor(private readonly imagekitioService: ImagekitioService) {}
|
| 24 |
|
| 25 |
-
@Get('
|
| 26 |
-
@ApiOperation({
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
'Returns token, expire, and signature required for direct upload to ImageKit from frontend. Optionally restrict by file type.',
|
| 30 |
-
})
|
| 31 |
-
@ApiQuery({
|
| 32 |
-
name: 'type',
|
| 33 |
-
required: false,
|
| 34 |
-
enum: FileType,
|
| 35 |
-
description: 'File type restriction',
|
| 36 |
-
example: FileType.IMAGE,
|
| 37 |
-
})
|
| 38 |
-
@ApiResponse({
|
| 39 |
-
status: 200,
|
| 40 |
-
description: 'Authentication parameters generated successfully',
|
| 41 |
-
schema: {
|
| 42 |
-
example: {
|
| 43 |
-
token: 'unique-token-string',
|
| 44 |
-
expire: 1234567890,
|
| 45 |
-
signature: 'generated-signature',
|
| 46 |
-
fileType: 'image',
|
| 47 |
-
},
|
| 48 |
-
},
|
| 49 |
-
})
|
| 50 |
-
getAuthParams(@Query('type') type?: FileType) {
|
| 51 |
return this.imagekitioService.getAuthenticationParameters(type);
|
| 52 |
}
|
| 53 |
|
| 54 |
-
@Get('
|
| 55 |
-
@ApiOperation({
|
| 56 |
-
|
| 57 |
-
description:
|
| 58 |
-
'Retrieves the file ID and details from an ImageKit URL. Useful before deleting a file.',
|
| 59 |
-
})
|
| 60 |
-
@ApiQuery({
|
| 61 |
-
name: 'url',
|
| 62 |
-
required: true,
|
| 63 |
-
description: 'The ImageKit file URL',
|
| 64 |
-
example:
|
| 65 |
-
'https://ik.imagekit.io/eihvofxmgsdfb/my-images/krishna/krishna_KvmHprPX7.jpg',
|
| 66 |
-
})
|
| 67 |
-
@ApiResponse({
|
| 68 |
-
status: 200,
|
| 69 |
-
description: 'File ID retrieved successfully',
|
| 70 |
-
schema: {
|
| 71 |
-
example: {
|
| 72 |
-
fileId: '66c91e205ae88a1234d90abc',
|
| 73 |
-
name: 'krishna.jpg',
|
| 74 |
-
url: 'https://ik.imagekit.io/eihvofxmgsdfb/my-images/krishna/krishna_KvmHprPX7.jpg',
|
| 75 |
-
},
|
| 76 |
-
},
|
| 77 |
-
})
|
| 78 |
-
@ApiResponse({
|
| 79 |
-
status: 404,
|
| 80 |
-
description: 'File not found or already deleted',
|
| 81 |
-
})
|
| 82 |
-
async getFileIdByUrl(@Query('url') url: string): Promise<any> {
|
| 83 |
return this.imagekitioService.getFileIdByUrl(url);
|
| 84 |
}
|
| 85 |
|
| 86 |
@Delete('by-url')
|
| 87 |
@HttpCode(HttpStatus.OK)
|
| 88 |
-
@ApiOperation({
|
| 89 |
-
summary: 'Delete a file from ImageKit by URL',
|
| 90 |
-
description:
|
| 91 |
-
'Deletes a file from ImageKit storage using the file URL. This endpoint first retrieves the file ID from the URL, then deletes the file.',
|
| 92 |
-
})
|
| 93 |
-
@ApiQuery({
|
| 94 |
-
name: 'url',
|
| 95 |
-
required: true,
|
| 96 |
-
description: 'The ImageKit file URL to delete',
|
| 97 |
-
example:
|
| 98 |
-
'https://ik.imagekit.io/eihvofxmgsdfb/my-images/krishna/krishna_KvmHprPX7.jpg',
|
| 99 |
-
})
|
| 100 |
-
@ApiResponse({
|
| 101 |
-
status: 200,
|
| 102 |
-
description: 'File deleted successfully',
|
| 103 |
-
schema: {
|
| 104 |
-
example: {
|
| 105 |
-
message: 'File deleted successfully',
|
| 106 |
-
deletedUrl:
|
| 107 |
-
'https://ik.imagekit.io/eihvofxmgsdfb/my-images/krishna/krishna_KvmHprPX7.jpg',
|
| 108 |
-
fileId: '66c91e205ae88a1234d90abc',
|
| 109 |
-
},
|
| 110 |
-
},
|
| 111 |
-
})
|
| 112 |
-
@ApiResponse({
|
| 113 |
-
status: 404,
|
| 114 |
-
description: 'File not found or already deleted',
|
| 115 |
-
})
|
| 116 |
async deleteFileByUrl(@Query('url') url: string) {
|
| 117 |
const result = await this.imagekitioService.deleteFileByUrl(url);
|
| 118 |
return {
|
|
@@ -120,49 +38,4 @@ export class ImagekitioController {
|
|
| 120 |
...result,
|
| 121 |
};
|
| 122 |
}
|
| 123 |
-
|
| 124 |
-
@Delete(':fileId')
|
| 125 |
-
@HttpCode(HttpStatus.OK)
|
| 126 |
-
@ApiOperation({
|
| 127 |
-
summary: 'Delete a file from ImageKit by file ID',
|
| 128 |
-
description: 'Deletes a file from ImageKit storage using the file ID',
|
| 129 |
-
})
|
| 130 |
-
@ApiParam({
|
| 131 |
-
name: 'fileId',
|
| 132 |
-
description: 'The ImageKit file ID to delete',
|
| 133 |
-
example: '5f8e7d6c5b4a3f2e1d0c9b8a',
|
| 134 |
-
})
|
| 135 |
-
@ApiResponse({
|
| 136 |
-
status: 200,
|
| 137 |
-
description: 'File deleted successfully',
|
| 138 |
-
})
|
| 139 |
-
@ApiResponse({
|
| 140 |
-
status: 404,
|
| 141 |
-
description: 'File not found',
|
| 142 |
-
})
|
| 143 |
-
async deleteFile(@Param('fileId') fileId: string) {
|
| 144 |
-
await this.imagekitioService.deleteFile(fileId);
|
| 145 |
-
return {
|
| 146 |
-
message: 'File deleted successfully',
|
| 147 |
-
fileId,
|
| 148 |
-
};
|
| 149 |
-
}
|
| 150 |
-
|
| 151 |
-
@Get('file/:fileId')
|
| 152 |
-
@ApiOperation({
|
| 153 |
-
summary: 'Get file details by file ID',
|
| 154 |
-
description: 'Retrieves details of a specific file from ImageKit',
|
| 155 |
-
})
|
| 156 |
-
@ApiParam({
|
| 157 |
-
name: 'fileId',
|
| 158 |
-
description: 'The ImageKit file ID',
|
| 159 |
-
example: '5f8e7d6c5b4a3f2e1d0c9b8a',
|
| 160 |
-
})
|
| 161 |
-
@ApiResponse({
|
| 162 |
-
status: 200,
|
| 163 |
-
description: 'File details retrieved successfully',
|
| 164 |
-
})
|
| 165 |
-
async getFileDetails(@Param('fileId') fileId: string): Promise<any> {
|
| 166 |
-
return this.imagekitioService.getFileDetails(fileId);
|
| 167 |
-
}
|
| 168 |
}
|
|
|
|
| 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 { ImagekitioService } from './imagekitio.service';
|
| 11 |
import { FileType } from './enums/file-type.enum';
|
| 12 |
|
|
|
|
| 15 |
export class ImagekitioController {
|
| 16 |
constructor(private readonly imagekitioService: ImagekitioService) {}
|
| 17 |
|
| 18 |
+
@Get('signed-upload')
|
| 19 |
+
@ApiOperation({ summary: 'Get signed upload parameters' })
|
| 20 |
+
@ApiQuery({ name: 'type', enum: FileType, required: false })
|
| 21 |
+
getSignedUploadParams(@Query('type') type?: FileType) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 22 |
return this.imagekitioService.getAuthenticationParameters(type);
|
| 23 |
}
|
| 24 |
|
| 25 |
+
@Get('by-url')
|
| 26 |
+
@ApiOperation({ summary: 'Get file details by URL' })
|
| 27 |
+
async getFileByUrl(@Query('url') url: string): Promise<any> {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 28 |
return this.imagekitioService.getFileIdByUrl(url);
|
| 29 |
}
|
| 30 |
|
| 31 |
@Delete('by-url')
|
| 32 |
@HttpCode(HttpStatus.OK)
|
| 33 |
+
@ApiOperation({ summary: 'Delete file by URL' })
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 34 |
async deleteFileByUrl(@Query('url') url: string) {
|
| 35 |
const result = await this.imagekitioService.deleteFileByUrl(url);
|
| 36 |
return {
|
|
|
|
| 38 |
...result,
|
| 39 |
};
|
| 40 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 41 |
}
|
src/shared/modules/imagekitio/imagekitio.service.ts
CHANGED
|
@@ -16,20 +16,41 @@ export class ImagekitioService {
|
|
| 16 |
});
|
| 17 |
}
|
| 18 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 19 |
/**
|
| 20 |
* Get authentication parameters for client-side upload
|
| 21 |
-
* @param fileType - Optional file type restriction (
|
| 22 |
-
* @returns Authentication parameters (token, expire, signature)
|
| 23 |
*/
|
| 24 |
getAuthenticationParameters(fileType?: string) {
|
| 25 |
try {
|
|
|
|
|
|
|
|
|
|
|
|
|
| 26 |
const authParams = this.imagekit.getAuthenticationParameters();
|
|
|
|
| 27 |
this.logger.log(
|
| 28 |
-
`Generated authentication parameters for ImageKit upload${fileType
|
| 29 |
);
|
|
|
|
| 30 |
return {
|
| 31 |
...authParams,
|
| 32 |
-
|
|
|
|
| 33 |
};
|
| 34 |
} catch (error) {
|
| 35 |
this.logger.error('Failed to generate authentication parameters', error);
|
|
@@ -37,6 +58,15 @@ export class ImagekitioService {
|
|
| 37 |
}
|
| 38 |
}
|
| 39 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 40 |
/**
|
| 41 |
* Get file ID from ImageKit URL
|
| 42 |
* @param url - The ImageKit file URL
|
|
@@ -44,21 +74,22 @@ export class ImagekitioService {
|
|
| 44 |
*/
|
| 45 |
async getFileIdByUrl(url: string): Promise<any> {
|
| 46 |
try {
|
| 47 |
-
//
|
| 48 |
-
|
| 49 |
-
|
| 50 |
const urlEndpoint = this.configService.get<string>(
|
| 51 |
'imagekitio.urlEndpoint',
|
| 52 |
);
|
| 53 |
-
if (!
|
| 54 |
throw new NotFoundException(
|
| 55 |
'Invalid ImageKit URL - does not match your URL endpoint',
|
| 56 |
);
|
| 57 |
}
|
| 58 |
|
| 59 |
-
|
|
|
|
| 60 |
|
| 61 |
-
// Extract filename from path
|
| 62 |
const fileName = filePath.split('/').pop() || '';
|
| 63 |
|
| 64 |
if (!fileName) {
|
|
@@ -71,24 +102,21 @@ export class ImagekitioService {
|
|
| 71 |
});
|
| 72 |
|
| 73 |
if (!files || files.length === 0) {
|
| 74 |
-
this.logger.warn(
|
| 75 |
-
`File not found with URL: ${url} - it may have been already deleted`,
|
| 76 |
-
);
|
| 77 |
throw new NotFoundException('File not found or already deleted');
|
| 78 |
}
|
| 79 |
|
| 80 |
-
// Find
|
| 81 |
-
const exactMatch = files.find(
|
|
|
|
|
|
|
| 82 |
|
| 83 |
if (!exactMatch) {
|
| 84 |
-
|
| 85 |
-
this.logger.warn(
|
| 86 |
-
`No exact URL match found for: ${url} - file may have been already deleted`,
|
| 87 |
-
);
|
| 88 |
throw new NotFoundException('File not found or already deleted');
|
| 89 |
}
|
| 90 |
|
| 91 |
-
this.logger.log(`Found file
|
| 92 |
return exactMatch;
|
| 93 |
} catch (error) {
|
| 94 |
if (error instanceof NotFoundException) {
|
|
@@ -100,7 +128,24 @@ export class ImagekitioService {
|
|
| 100 |
}
|
| 101 |
|
| 102 |
/**
|
| 103 |
-
*
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 104 |
* @param url - The ImageKit file URL
|
| 105 |
* @returns Deletion result
|
| 106 |
*/
|
|
@@ -112,9 +157,20 @@ export class ImagekitioService {
|
|
| 112 |
throw new NotFoundException('Could not retrieve file ID from URL');
|
| 113 |
}
|
| 114 |
|
| 115 |
-
//
|
| 116 |
const result = await this.deleteFile(fileDetails.fileId);
|
| 117 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 118 |
|
| 119 |
return {
|
| 120 |
...result,
|
|
@@ -124,11 +180,11 @@ export class ImagekitioService {
|
|
| 124 |
}
|
| 125 |
|
| 126 |
/**
|
| 127 |
-
* Delete a file from ImageKit
|
| 128 |
* @param fileId - The file ID to delete
|
| 129 |
* @returns Deletion result
|
| 130 |
*/
|
| 131 |
-
async deleteFile(fileId: string): Promise<any> {
|
| 132 |
try {
|
| 133 |
const result = await this.imagekit.deleteFile(fileId);
|
| 134 |
this.logger.log(`Successfully deleted file with ID: ${fileId}`);
|
|
@@ -138,36 +194,4 @@ export class ImagekitioService {
|
|
| 138 |
throw error;
|
| 139 |
}
|
| 140 |
}
|
| 141 |
-
|
| 142 |
-
/**
|
| 143 |
-
* Get file details from ImageKit
|
| 144 |
-
* @param fileId - The file ID
|
| 145 |
-
* @returns File details
|
| 146 |
-
*/
|
| 147 |
-
async getFileDetails(fileId: string): Promise<any> {
|
| 148 |
-
try {
|
| 149 |
-
const fileDetails = await this.imagekit.getFileDetails(fileId);
|
| 150 |
-
return fileDetails;
|
| 151 |
-
} catch (error) {
|
| 152 |
-
this.logger.error(`Failed to get file details for ID: ${fileId}`, error);
|
| 153 |
-
throw error;
|
| 154 |
-
}
|
| 155 |
-
}
|
| 156 |
-
|
| 157 |
-
/**
|
| 158 |
-
* List files from ImageKit
|
| 159 |
-
* @param options - List options (skip, limit, etc.)
|
| 160 |
-
* @returns List of files
|
| 161 |
-
*/
|
| 162 |
-
async listFiles(
|
| 163 |
-
options: { skip?: number; limit?: number } = {},
|
| 164 |
-
): Promise<any> {
|
| 165 |
-
try {
|
| 166 |
-
const files = await this.imagekit.listFiles(options);
|
| 167 |
-
return files;
|
| 168 |
-
} catch (error) {
|
| 169 |
-
this.logger.error('Failed to list files', error);
|
| 170 |
-
throw error;
|
| 171 |
-
}
|
| 172 |
-
}
|
| 173 |
}
|
|
|
|
| 16 |
});
|
| 17 |
}
|
| 18 |
|
| 19 |
+
/**
|
| 20 |
+
* Get folder path based on file type
|
| 21 |
+
* @param fileType - The file type (IMAGE, DOCUMENT, VIDEO)
|
| 22 |
+
* @returns Folder path for the file type
|
| 23 |
+
*/
|
| 24 |
+
private getFolderPath(fileType: string): string {
|
| 25 |
+
const folderMap: Record<string, string> = {
|
| 26 |
+
IMAGE: 'images',
|
| 27 |
+
DOCUMENT: 'documents',
|
| 28 |
+
VIDEO: 'videos',
|
| 29 |
+
};
|
| 30 |
+
return folderMap[fileType.toUpperCase()] || 'others';
|
| 31 |
+
}
|
| 32 |
+
|
| 33 |
/**
|
| 34 |
* Get authentication parameters for client-side upload
|
| 35 |
+
* @param fileType - Optional file type restriction (IMAGE, DOCUMENT, VIDEO)
|
| 36 |
+
* @returns Authentication parameters (token, expire, signature, folder)
|
| 37 |
*/
|
| 38 |
getAuthenticationParameters(fileType?: string) {
|
| 39 |
try {
|
| 40 |
+
// Determine folder based on file type
|
| 41 |
+
const folder = fileType ? this.getFolderPath(fileType) : 'others';
|
| 42 |
+
|
| 43 |
+
// Generate auth params
|
| 44 |
const authParams = this.imagekit.getAuthenticationParameters();
|
| 45 |
+
|
| 46 |
this.logger.log(
|
| 47 |
+
`Generated authentication parameters for ImageKit upload${fileType ? ` (type: ${fileType}, folder: ${folder})` : ''}`,
|
| 48 |
);
|
| 49 |
+
|
| 50 |
return {
|
| 51 |
...authParams,
|
| 52 |
+
folder, // Folder where file should be uploaded
|
| 53 |
+
...(fileType && { fileType }),
|
| 54 |
};
|
| 55 |
} catch (error) {
|
| 56 |
this.logger.error('Failed to generate authentication parameters', error);
|
|
|
|
| 58 |
}
|
| 59 |
}
|
| 60 |
|
| 61 |
+
/**
|
| 62 |
+
* Clean URL by removing query parameters
|
| 63 |
+
* @param url - The URL to clean
|
| 64 |
+
* @returns Clean URL without query parameters
|
| 65 |
+
*/
|
| 66 |
+
private cleanUrl(url: string): string {
|
| 67 |
+
return url.split('?')[0];
|
| 68 |
+
}
|
| 69 |
+
|
| 70 |
/**
|
| 71 |
* Get file ID from ImageKit URL
|
| 72 |
* @param url - The ImageKit file URL
|
|
|
|
| 74 |
*/
|
| 75 |
async getFileIdByUrl(url: string): Promise<any> {
|
| 76 |
try {
|
| 77 |
+
// Clean URL (remove query parameters like ?updatedAt=...)
|
| 78 |
+
const cleanUrl = this.cleanUrl(url);
|
| 79 |
+
|
| 80 |
const urlEndpoint = this.configService.get<string>(
|
| 81 |
'imagekitio.urlEndpoint',
|
| 82 |
);
|
| 83 |
+
if (!cleanUrl.startsWith(urlEndpoint || '')) {
|
| 84 |
throw new NotFoundException(
|
| 85 |
'Invalid ImageKit URL - does not match your URL endpoint',
|
| 86 |
);
|
| 87 |
}
|
| 88 |
|
| 89 |
+
// Extract file path from URL
|
| 90 |
+
const filePath = cleanUrl.replace(urlEndpoint || '', '');
|
| 91 |
|
| 92 |
+
// Extract filename from path (last part after /)
|
| 93 |
const fileName = filePath.split('/').pop() || '';
|
| 94 |
|
| 95 |
if (!fileName) {
|
|
|
|
| 102 |
});
|
| 103 |
|
| 104 |
if (!files || files.length === 0) {
|
| 105 |
+
this.logger.warn(`File not found: ${fileName}`);
|
|
|
|
|
|
|
| 106 |
throw new NotFoundException('File not found or already deleted');
|
| 107 |
}
|
| 108 |
|
| 109 |
+
// Find exact match by comparing clean URLs
|
| 110 |
+
const exactMatch = files.find(
|
| 111 |
+
(file: any) => this.cleanUrl(file.url) === cleanUrl,
|
| 112 |
+
);
|
| 113 |
|
| 114 |
if (!exactMatch) {
|
| 115 |
+
this.logger.warn(`No exact match found for: ${fileName}`);
|
|
|
|
|
|
|
|
|
|
| 116 |
throw new NotFoundException('File not found or already deleted');
|
| 117 |
}
|
| 118 |
|
| 119 |
+
this.logger.log(`Found file: ${fileName}`);
|
| 120 |
return exactMatch;
|
| 121 |
} catch (error) {
|
| 122 |
if (error instanceof NotFoundException) {
|
|
|
|
| 128 |
}
|
| 129 |
|
| 130 |
/**
|
| 131 |
+
* Purge CDN cache for a URL
|
| 132 |
+
* @param url - The ImageKit file URL to purge from cache
|
| 133 |
+
* @returns Purge result
|
| 134 |
+
*/
|
| 135 |
+
async purgeCdnCache(url: string): Promise<any> {
|
| 136 |
+
try {
|
| 137 |
+
const cleanUrl = this.cleanUrl(url);
|
| 138 |
+
const result = await this.imagekit.purgeCache(cleanUrl);
|
| 139 |
+
this.logger.log(`Successfully purged CDN cache for: ${cleanUrl}`);
|
| 140 |
+
return result;
|
| 141 |
+
} catch (error) {
|
| 142 |
+
this.logger.error(`Failed to purge CDN cache for URL: ${url}`, error);
|
| 143 |
+
throw error;
|
| 144 |
+
}
|
| 145 |
+
}
|
| 146 |
+
|
| 147 |
+
/**
|
| 148 |
+
* Delete a file from ImageKit by URL and purge CDN cache
|
| 149 |
* @param url - The ImageKit file URL
|
| 150 |
* @returns Deletion result
|
| 151 |
*/
|
|
|
|
| 157 |
throw new NotFoundException('Could not retrieve file ID from URL');
|
| 158 |
}
|
| 159 |
|
| 160 |
+
// Delete the file using the file ID
|
| 161 |
const result = await this.deleteFile(fileDetails.fileId);
|
| 162 |
+
|
| 163 |
+
// Purge CDN cache to make deletion immediate
|
| 164 |
+
try {
|
| 165 |
+
await this.purgeCdnCache(url);
|
| 166 |
+
this.logger.log(
|
| 167 |
+
`Successfully deleted file and purged cache for URL: ${url}`,
|
| 168 |
+
);
|
| 169 |
+
} catch {
|
| 170 |
+
this.logger.warn(
|
| 171 |
+
`File deleted but CDN cache purge failed for: ${url}. Cache will expire naturally.`,
|
| 172 |
+
);
|
| 173 |
+
}
|
| 174 |
|
| 175 |
return {
|
| 176 |
...result,
|
|
|
|
| 180 |
}
|
| 181 |
|
| 182 |
/**
|
| 183 |
+
* Delete a file from ImageKit (private method used internally)
|
| 184 |
* @param fileId - The file ID to delete
|
| 185 |
* @returns Deletion result
|
| 186 |
*/
|
| 187 |
+
private async deleteFile(fileId: string): Promise<any> {
|
| 188 |
try {
|
| 189 |
const result = await this.imagekit.deleteFile(fileId);
|
| 190 |
this.logger.log(`Successfully deleted file with ID: ${fileId}`);
|
|
|
|
| 194 |
throw error;
|
| 195 |
}
|
| 196 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 197 |
}
|