import { Controller, Get, Param, Req, Res, UseGuards, Logger, Request, Post, UseInterceptors, UploadedFile, Body } from '@nestjs/common'; import { Response } from 'express'; import { DocumentsService } from './documents.service'; import { JwtAuthGuard } from '../auth/jwt-auth.guard'; import { S3Service } from '../s3/s3.service'; import { FileInterceptor } from '@nestjs/platform-express'; interface S3File { buffer: Buffer; contentType: string; contentLength: number; fileName: string; } @Controller('documents') export class DocumentsController { private readonly logger = new Logger(DocumentsController.name); constructor( private readonly documentsService: DocumentsService, private readonly s3Service: S3Service ) { this.logger.log('DocumentsController initialized'); } @Post('upload') @UseGuards(JwtAuthGuard) @UseInterceptors(FileInterceptor('file')) async uploadDocument( @UploadedFile() file: Express.Multer.File, @Body('title') title: string, @Body('sharedWithId') sharedWithId: string, @Request() req, ) { this.logger.log('=== Document upload endpoint hit ==='); this.logger.debug('Upload request received:', { fileName: file?.originalname, fileSize: file?.size, title, sharedWithId, uploadedById: req.user.id, }); try { const result = await this.documentsService.uploadDocument( file, title, Number(sharedWithId), req.user.id, ); this.logger.debug('Document upload successful:', { documentId: result.id, title: result.title, s3Key: result.s3Key, }); return result; } catch (error) { this.logger.error('Document upload failed:', { error: error.message, stack: error.stack, }); throw error; } } @Get('shared/:userId') @UseGuards(JwtAuthGuard) async getSharedDocuments(@Param('userId') userId: string) { return this.documentsService.getClientDocuments(Number(userId)); } @Get('download/:key') @UseGuards(JwtAuthGuard) async downloadDocument( @Param('key') key: string, @Request() req, @Res() res: Response ) { try { this.logger.debug(`Download request for key: ${key}`); const decodedKey = decodeURIComponent(key); this.logger.debug(`Decoded key: ${decodedKey}`); // Get document from database first to verify access const document = await this.documentsService.findDocumentByS3Key(decodedKey); if (!document) { return res.status(404).json({ message: 'Document not found' }); } // Verify user has access to this document const hasAccess = await this.documentsService.verifyDocumentAccess( document.id, req.user.id ); if (!hasAccess) { return res.status(403).json({ message: 'Access denied' }); } // Get the file from S3 const file = await this.s3Service.getFile(decodedKey); if (!file || !file.buffer) { return res.status(404).json({ message: 'File not found in storage' }); } res.set({ 'Content-Type': file.contentType || 'application/octet-stream', 'Content-Length': file.contentLength, 'Content-Disposition': `attachment; filename="${encodeURIComponent(file.fileName)}"`, }); return res.send(file.buffer); } catch (error) { this.logger.error('Download error:', error); return res.status(500).json({ message: 'Failed to download file', error: error.message }); } } }