HEX
Server: Apache/2.4.52 (Ubuntu)
System: Linux ip-172-31-4-197 6.8.0-1036-aws #38~22.04.1-Ubuntu SMP Fri Aug 22 15:44:33 UTC 2025 x86_64
User: ubuntu (1000)
PHP: 7.4.33
Disabled: pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,pcntl_unshare,
Upload Files
File: /var/www/api-storage/src/modules/storage/api/controllers/file.controller.ts
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
import {
  Body,
  Controller,
  Delete,
  Get,
  HttpCode,
  HttpStatus,
  Param,
  Post,
  Res,
  UploadedFile,
  UploadedFiles,
  UseInterceptors,
} from '@nestjs/common';
import { FileInterceptor, FilesInterceptor } from '@nestjs/platform-express';
import { Response } from 'express';
import { UploadFileUseCase } from '../../application/use-cases/upload-file.usecase';
import { UploadFilesUseCase } from '../../application/use-cases/upload-files.usecase';
import { ResponseEntity } from 'src/shared/core/entities/reponse.entity';
import { FileEntity } from '../../core/entities/file.entity';
import { GetFileUseCase } from '../../application/use-cases/get-file.usecase';
import * as mime from 'mime-types';
import { DeleteFileUseCase } from '../../application/use-cases/delete-file.usecase';

@Controller('files')
export class FileController {
  constructor(
    private readonly uploadFileUseCase: UploadFileUseCase,
    private readonly uploadFilesUseCase: UploadFilesUseCase,
    private readonly getFileUseCase: GetFileUseCase,
    private readonly deleteFileUseCase: DeleteFileUseCase,
  ) {}

  @Post('upload')
  @UseInterceptors(FileInterceptor('file'))
  async upload(
    @UploadedFile() file: Express.Multer.File,
    @Body() body: { bucketPath: string },
  ): Promise<ResponseEntity<FileEntity>> {
    return this.uploadFileUseCase.execute(file, body.bucketPath);
  }

  @Post('upload-multiple')
  @UseInterceptors(FilesInterceptor('files'))
  async uploadMultiple(
    @UploadedFiles() files: Express.Multer.File[],
    @Body() body: { bucketPath: string },
  ): Promise<ResponseEntity<FileEntity[]>> {
    return this.uploadFilesUseCase.execute(files, body.bucketPath);
  }

  @Get(':bucket/*path')
  async getFile(
    @Param('bucket') bucket: string,
    @Param('path') fullPath: string,
    @Res() res: Response,
  ) {
    const decodedPath = decodeURIComponent(fullPath);
    const fileStream = await this.getFileUseCase.execute(bucket, decodedPath);

    // Obtener el tipo MIME basado en la extensión del archivo
    // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
    const mimeType = mime.lookup(decodedPath) ?? 'application/octet-stream';

    // Configurar headers según el tipo de archivo
    res.setHeader('Content-Type', mimeType);

    // Decidir si mostrar inline o forzar descarga
    const disposition = this.shouldDisplayInline(mimeType)
      ? 'inline'
      : 'attachment';
    res.setHeader(
      'Content-Disposition',
      `${disposition}; filename="${decodedPath}"`,
    );

    fileStream.pipe(res);
  }

  private shouldDisplayInline(mimeType: string): boolean {
    // Lista de tipos que pueden mostrarse directamente en el navegador
    const inlineTypes = [
      'image/',
      'application/pdf',
      'text/',
      'application/json',
      'video/',
      'audio/',
    ];

    return inlineTypes.some((type) => mimeType.includes(type));
  }

  @Delete(':bucket/*path')
  @HttpCode(HttpStatus.NO_CONTENT)
  async deleteFile(
    @Param('bucket') bucket: string,
    @Param('path') fullPath: string,
  ): Promise<void> {
    const decodedPath = decodeURIComponent(fullPath);
    await this.deleteFileUseCase.execute(bucket, decodedPath);
  }
}