Skip to content

Commit 4b30cb1

Browse files
committed
fix(upload): resolve lint warnings in cloudinary service
- Add eslint-disable for buffer check (can be undefined with disk storage) - Add type assertion for cloudinary destroy result - Fix object injection warning by extracting file to variable
1 parent 45be7a1 commit 4b30cb1

3 files changed

Lines changed: 30 additions & 18 deletions

File tree

backend/src/upload/cloudinary.service.ts

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { Readable } from 'stream';
22

33
import { Injectable, BadRequestException } from '@nestjs/common';
44
import { ConfigService } from '@nestjs/config';
5+
56
import {
67
v2 as cloudinary,
78
UploadApiResponse,
@@ -28,7 +29,7 @@ export class CloudinaryService {
2829
const apiKey = this.configService.get<string>('CLOUDINARY_API_KEY');
2930
const apiSecret = this.configService.get<string>('CLOUDINARY_API_SECRET');
3031

31-
this.isConfigured = !!(cloudName && apiKey && apiSecret);
32+
this.isConfigured = Boolean(cloudName && apiKey && apiSecret);
3233

3334
if (this.isConfigured) {
3435
cloudinary.config({
@@ -120,8 +121,9 @@ export class CloudinaryService {
120121
): Promise<CloudinaryUploadResult> {
121122
// For memory storage, file.buffer is available
122123
// For disk storage, we'd need to read the file
123-
const buffer = file.buffer;
124+
const { buffer } = file;
124125

126+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- buffer can be undefined with disk storage
125127
if (!buffer) {
126128
throw new BadRequestException(
127129
'File buffer not available. Ensure memory storage is used.',
@@ -145,7 +147,9 @@ export class CloudinaryService {
145147
/**
146148
* Upload an avatar image with automatic optimization
147149
*/
148-
async uploadAvatar(file: Express.Multer.File): Promise<CloudinaryUploadResult> {
150+
async uploadAvatar(
151+
file: Express.Multer.File,
152+
): Promise<CloudinaryUploadResult> {
149153
return this.uploadFile(file, {
150154
folder: 'learnix/avatars',
151155
transformation: {
@@ -189,9 +193,9 @@ export class CloudinaryService {
189193
}
190194

191195
try {
192-
const result = await cloudinary.uploader.destroy(publicId, {
196+
const result = (await cloudinary.uploader.destroy(publicId, {
193197
resource_type: resourceType,
194-
});
198+
})) as { result: string };
195199
return result.result === 'ok';
196200
} catch {
197201
return false;
@@ -210,7 +214,9 @@ export class CloudinaryService {
210214

211215
// Find the index of 'upload' and get everything after version
212216
const uploadIndex = pathParts.indexOf('upload');
213-
if (uploadIndex === -1) return null;
217+
if (uploadIndex === -1) {
218+
return null;
219+
}
214220

215221
// Skip 'upload' and version (vXXXXXXX)
216222
const publicIdParts = pathParts.slice(uploadIndex + 2);

backend/src/upload/upload.controller.ts

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ import {
1414
import { ConfigService } from '@nestjs/config';
1515
import { FileInterceptor, FilesInterceptor } from '@nestjs/platform-express';
1616

17-
import { UploadService } from './upload.service';
1817
import { CloudinaryService } from './cloudinary.service';
18+
import { UploadService } from './upload.service';
1919
import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard';
2020

2121
import type { FileUploadResult } from './upload.service';
@@ -188,13 +188,17 @@ export class UploadController {
188188
const results = await Promise.all(
189189
files.map((file) => this.cloudinaryService.uploadCourseImage(file)),
190190
);
191-
return results.map((result, index) => ({
192-
filename: result.publicId,
193-
originalName: files[index].originalname,
194-
mimetype: files[index].mimetype,
195-
size: result.bytes,
196-
url: result.secureUrl,
197-
}));
191+
return results.map((result, idx) => {
192+
// eslint-disable-next-line security/detect-object-injection -- idx is from array iteration, not user input
193+
const file = files[idx];
194+
return {
195+
filename: result.publicId,
196+
originalName: file.originalname,
197+
mimetype: file.mimetype,
198+
size: result.bytes,
199+
url: result.secureUrl,
200+
};
201+
});
198202
}
199203

200204
return this.uploadService.processUploadedFiles(files);
@@ -244,7 +248,9 @@ export class UploadController {
244248
// Try to delete from Cloudinary first if it looks like a Cloudinary public ID
245249
if (this.useCloudinary || filename.includes('/')) {
246250
const deleted = await this.cloudinaryService.deleteFile(filename);
247-
if (deleted) return;
251+
if (deleted) {
252+
return;
253+
}
248254
}
249255

250256
// Fall back to local file deletion

backend/src/upload/upload.module.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,20 @@ import { MulterModule } from '@nestjs/platform-express';
88

99
import { diskStorage, memoryStorage } from 'multer';
1010

11+
import { CloudinaryService } from './cloudinary.service';
1112
import { UploadController } from './upload.controller';
1213
import { UploadService } from './upload.service';
13-
import { CloudinaryService } from './cloudinary.service';
1414

1515
@Module({
1616
imports: [
1717
MulterModule.registerAsync({
1818
imports: [ConfigModule],
1919
useFactory: (configService: ConfigService) => {
2020
// Check if Cloudinary is configured
21-
const cloudinaryConfigured = !!(
21+
const cloudinaryConfigured = Boolean(
2222
configService.get<string>('CLOUDINARY_CLOUD_NAME') &&
2323
configService.get<string>('CLOUDINARY_API_KEY') &&
24-
configService.get<string>('CLOUDINARY_API_SECRET')
24+
configService.get<string>('CLOUDINARY_API_SECRET'),
2525
);
2626

2727
// Use memory storage if Cloudinary is configured (for cloud upload)

0 commit comments

Comments
 (0)