127 lines
3.5 KiB
TypeScript
127 lines
3.5 KiB
TypeScript
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
|
|
});
|
|
}
|
|
}
|
|
}
|