Spaces:
Runtime error
Runtime error
File size: 6,134 Bytes
9f0826d 3b3ab98 9f0826d 3b3ab98 9f0826d 3b3ab98 9f0826d 3b3ab98 9f0826d 3b3ab98 9f0826d 3b3ab98 9f0826d 3b3ab98 9f0826d 3b3ab98 9f0826d 3b3ab98 9f0826d 3b3ab98 9f0826d 3b3ab98 9f0826d 3b3ab98 9f0826d 3b3ab98 9f0826d 3b3ab98 9f0826d 3b3ab98 9f0826d 3b3ab98 9f0826d 3b3ab98 9f0826d 3b3ab98 9f0826d 3b3ab98 9f0826d 3b3ab98 9f0826d 3b3ab98 9f0826d | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 | import { Injectable, Logger, NotFoundException } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import ImageKit from 'imagekit';
@Injectable()
export class ImagekitioService {
private readonly logger = new Logger(ImagekitioService.name);
private imagekit: ImageKit;
constructor(private configService: ConfigService) {
this.imagekit = new ImageKit({
publicKey: this.configService.get<string>('imagekitio.publicKey') || '',
privateKey: this.configService.get<string>('imagekitio.privateKey') || '',
urlEndpoint:
this.configService.get<string>('imagekitio.urlEndpoint') || '',
});
}
/**
* Get folder path based on file type
* @param fileType - The file type (IMAGE, DOCUMENT, VIDEO)
* @returns Folder path for the file type
*/
private getFolderPath(fileType: string): string {
const folderMap: Record<string, string> = {
IMAGE: 'images',
DOCUMENT: 'documents',
VIDEO: 'videos',
};
return folderMap[fileType.toUpperCase()] || 'others';
}
/**
* Get authentication parameters for client-side upload
* @param fileType - Optional file type restriction (IMAGE, DOCUMENT, VIDEO)
* @returns Authentication parameters (token, expire, signature, folder)
*/
getAuthenticationParameters(fileType?: string) {
try {
// Determine folder based on file type
const folder = fileType ? this.getFolderPath(fileType) : 'others';
// Generate auth params
const authParams = this.imagekit.getAuthenticationParameters();
this.logger.log(
`Generated authentication parameters for ImageKit upload${fileType ? ` (type: ${fileType}, folder: ${folder})` : ''}`,
);
return {
...authParams,
folder, // Folder where file should be uploaded
...(fileType && { fileType }),
};
} catch (error) {
this.logger.error('Failed to generate authentication parameters', error);
throw error;
}
}
/**
* Clean URL by removing query parameters
* @param url - The URL to clean
* @returns Clean URL without query parameters
*/
private cleanUrl(url: string): string {
return url.split('?')[0];
}
/**
* Get file ID from ImageKit URL
* @param url - The ImageKit file URL
* @returns File details including fileId
*/
async getFileIdByUrl(url: string): Promise<any> {
try {
// Clean URL (remove query parameters like ?updatedAt=...)
const cleanUrl = this.cleanUrl(url);
const urlEndpoint = this.configService.get<string>(
'imagekitio.urlEndpoint',
);
if (!cleanUrl.startsWith(urlEndpoint || '')) {
throw new NotFoundException(
'Invalid ImageKit URL - does not match your URL endpoint',
);
}
// Extract file path from URL
const filePath = cleanUrl.replace(urlEndpoint || '', '');
// Extract filename from path (last part after /)
const fileName = filePath.split('/').pop() || '';
if (!fileName) {
throw new NotFoundException('Could not extract filename from URL');
}
// Search by filename
const files = await this.imagekit.listFiles({
searchQuery: `name="${fileName}"`,
});
if (!files || files.length === 0) {
this.logger.warn(`File not found: ${fileName}`);
throw new NotFoundException('File not found or already deleted');
}
// Find exact match by comparing clean URLs
const exactMatch = files.find(
(file: any) => this.cleanUrl(file.url) === cleanUrl,
);
if (!exactMatch) {
this.logger.warn(`No exact match found for: ${fileName}`);
throw new NotFoundException('File not found or already deleted');
}
this.logger.log(`Found file: ${fileName}`);
return exactMatch;
} catch (error) {
if (error instanceof NotFoundException) {
throw error;
}
this.logger.error(`Failed to get file ID for URL: ${url}`, error);
throw new NotFoundException('File not found or already deleted');
}
}
/**
* Purge CDN cache for a URL
* @param url - The ImageKit file URL to purge from cache
* @returns Purge result
*/
async purgeCdnCache(url: string): Promise<any> {
try {
const cleanUrl = this.cleanUrl(url);
const result = await this.imagekit.purgeCache(cleanUrl);
this.logger.log(`Successfully purged CDN cache for: ${cleanUrl}`);
return result;
} catch (error) {
this.logger.error(`Failed to purge CDN cache for URL: ${url}`, error);
throw error;
}
}
/**
* Delete a file from ImageKit by URL and purge CDN cache
* @param url - The ImageKit file URL
* @returns Deletion result
*/
async deleteFileByUrl(url: string): Promise<any> {
// First, get the file ID from the URL (this will throw NotFoundException if not found)
const fileDetails = await this.getFileIdByUrl(url);
if (!fileDetails || !fileDetails.fileId) {
throw new NotFoundException('Could not retrieve file ID from URL');
}
// Delete the file using the file ID
const result = await this.deleteFile(fileDetails.fileId);
// Purge CDN cache to make deletion immediate
try {
await this.purgeCdnCache(url);
this.logger.log(
`Successfully deleted file and purged cache for URL: ${url}`,
);
} catch {
this.logger.warn(
`File deleted but CDN cache purge failed for: ${url}. Cache will expire naturally.`,
);
}
return {
...result,
deletedUrl: url,
fileId: fileDetails.fileId,
};
}
/**
* Delete a file from ImageKit (private method used internally)
* @param fileId - The file ID to delete
* @returns Deletion result
*/
private async deleteFile(fileId: string): Promise<any> {
try {
const result = await this.imagekit.deleteFile(fileId);
this.logger.log(`Successfully deleted file with ID: ${fileId}`);
return result;
} catch (error) {
this.logger.error(`Failed to delete file with ID: ${fileId}`, error);
throw error;
}
}
}
|