download working

This commit is contained in:
dimitar committed 2024-11-02 00:08:04 +01:00
1 parent ed1a580b09
commit bc47e1d39a
11 files changed
+544 -355

No files matched your search

@@ -1,19 +1,33 @@
import { Controller, Get, Param, Req, UseGuards } from '@nestjs/common';
import { Controller, Get, Param, Req, Res, UseGuards } from '@nestjs/common';
import { Response } from 'express';
import { DocumentsService } from './documents.service';
import { JwtAuthGuard } from 'src/auth/jwt-auth.guard';
@Controller('documents')
export class DocumentsController {
constructor(private readonly documentsService: DocumentsService) {}
@Get('shared/:userId')
async getSharedDocuments(@Param('userId') userId: string) {
console.log('userId', userId);
return this.documentsService.getClientDocuments(parseInt(userId));
}
@Get('shared/download/:key')
@UseGuards(JwtAuthGuard)
async downloadDocument(@Param('key') key: string, @Req() req) {
return this.documentsService.downloadDocument(key, req.user.id);
async downloadDocument(
@Param('key') key: string,
@Req() req,
@Res() res: Response
) {
const file = await this.documentsService.downloadDocument(key, req.user.id);
res.set({
'Content-Type': file.contentType,
'Content-Length': file.contentLength,
'Content-Disposition': `attachment; filename="${file.fileName}"`,
});
res.send(file.buffer);
}
}
}
@@ -1,99 +1,199 @@
// import { Injectable } from '@nestjs/common';
// // import { Injectable } from '@nestjs/common';
// // import { PrismaService } from '../prisma/prisma.service';
// // //import { Document } from '@prisma/client';
// // @Injectable()
// // export class DocumentsService {
// // downloadDocument(key: string, id: any) {
// // throw new Error('Method not implemented.');
// // }
// // constructor(private readonly prisma: PrismaService) {}
// // async getClientDocuments(clientId: number) {
// // // return this.prisma.document.findMany({
// // // where: {
// // // sharedWithId: clientId,
// // // },
// // // include: {
// // // sharedWith: {
// // // select: {
// // // id: true,
// // // name: true,
// // // email: true,
// // // },
// // // },
// // // },
// // // });
// // return this.prisma.document.findMany({
// // where: {
// // sharedWithId: clientId,
// // },
// // orderBy: {
// // createdAt: 'desc',
// // },
// // });
// // }
// // }
// import { Injectable, NotFoundException, UnauthorizedException } from '@nestjs/common';
// import { PrismaService } from '../prisma/prisma.service';
// //import { Document } from '@prisma/client';
// import { S3Service } from '../s3/s3.service';
// import { Document, User } from '@prisma/client';
// @Injectable()
// export class DocumentsService {
// downloadDocument(key: string, id: any) {
// throw new Error('Method not implemented.');
// }
// constructor(private readonly prisma: PrismaService) {}
// constructor(
// private prisma: PrismaService,
// private s3Service: S3Service,
// ) {}
// async getClientDocuments(clientId: number) {
// // return this.prisma.document.findMany({
// // where: {
// // sharedWithId: clientId,
// // },
// // include: {
// // sharedWith: {
// // select: {
// // id: true,
// // name: true,
// // email: true,
// // },
// // },
// // },
// // });
// return this.prisma.document.findMany({
// async findAllByClient(userId: number): Promise<Document[]> {
// const documents = await this.prisma.document.findMany({
// where: {
// sharedWithId: clientId,
// sharedWithId: userId,
// },
// include: {
// sharedWith: {
// select: {
// id: true,
// name: true,
// email: true,
// },
// },
// },
// orderBy: {
// createdAt: 'desc',
// },
// });
// return documents;
// }
// async getClientDocuments(clientId: number) {
// // return this.prisma.document.findMany({
// // where: {
// // sharedWithId: clientId,
// // },
// // include: {
// // sharedWith: {
// // select: {
// // id: true,
// // name: true,
// // email: true,
// // },
// // },
// // },
// // });
// return this.prisma.document.findMany({
// where: {
// sharedWithId: clientId,
// },
// orderBy: {
// createdAt: 'desc',
// },
// });
// }
// async downloadDocument(s3Key: string, userId: number): Promise<any> {
// // Verify document exists and user has access
// const document = await this.prisma.document.findFirst({
// where: {
// s3Key: s3Key,
// sharedWithId: userId,
// },
// });
// if (!document) {
// throw new NotFoundException('Document not found or access denied');
// }
// try {
// // Get document stream from S3
// const fileStream = await this.s3Service.getObject(s3Key).createReadStream();
// return fileStream;
// } catch (error) {
// console.error('Error downloading document:', error);
// throw new NotFoundException('Document file not found in storage');
// }
// }
// async getAllDocuments(): Promise<Document[]> {
// return this.prisma.document.findMany({
// include: {
// sharedWith: {
// select: {
// id: true,
// name: true,
// email: true,
// },
// },
// },
// orderBy: {
// createdAt: 'desc',
// },
// });
// }
// async shareDocument(documentId: number, userId: number): Promise<Document> {
// const document = await this.prisma.document.update({
// where: { id: documentId },
// data: {
// sharedWithId: userId,
// },
// include: {
// sharedWith: {
// select: {
// id: true,
// name: true,
// email: true,
// },
// },
// },
// });
// return document;
// }
// async updateStatus(documentId: number, status: string): Promise<Document> {
// return this.prisma.document.update({
// where: { id: documentId },
// data: { status },
// include: {
// sharedWith: {
// select: {
// id: true,
// name: true,
// email: true,
// },
// },
// },
// });
// }
// }
import { Injectable, NotFoundException, UnauthorizedException } from '@nestjs/common';
import { PrismaService } from '../prisma/prisma.service';
import { S3Service } from '../s3/s3.service';
import { Document, User } from '@prisma/client';
import { Readable } from 'stream';
@Injectable()
export class DocumentsService {
constructor(
private prisma: PrismaService,
private s3Service: S3Service,
private readonly prisma: PrismaService,
private readonly s3Service: S3Service,
) {}
async findAllByClient(userId: number): Promise<Document[]> {
const documents = await this.prisma.document.findMany({
async getClientDocuments(clientId: number) {
return this.prisma.document.findMany({
where: {
sharedWithId: userId,
},
include: {
sharedWith: {
select: {
id: true,
name: true,
email: true,
},
},
sharedWithId: clientId,
},
orderBy: {
createdAt: 'desc',
},
});
return documents;
}
async getClientDocuments(clientId: number) {
// return this.prisma.document.findMany({
// where: {
// sharedWithId: clientId,
// },
// include: {
// sharedWith: {
// select: {
// id: true,
// name: true,
// email: true,
// },
// },
// },
// });
return this.prisma.document.findMany({
where: {
sharedWithId: clientId,
},
orderBy: {
createdAt: 'desc',
},
});
}
async downloadDocument(s3Key: string, userId: number): Promise<any> {
// Verify document exists and user has access
async downloadDocument(s3Key: string, userId: number) {
// Verify document access
const document = await this.prisma.document.findFirst({
where: {
s3Key: s3Key,
@@ -106,65 +206,31 @@ export class DocumentsService {
}
try {
// Get document stream from S3
const fileStream = await this.s3Service.getObject(s3Key).createReadStream();
return fileStream;
const s3Response = await this.s3Service.getObject(s3Key);
if (!s3Response.Body) {
throw new Error('No file content received from S3');
}
// Convert the response body to a buffer
const streamBody = s3Response.Body as Readable;
const chunks: Buffer[] = [];
for await (const chunk of streamBody) {
chunks.push(Buffer.from(chunk));
}
const fileBuffer = Buffer.concat(chunks);
return {
buffer: fileBuffer,
contentType: s3Response.ContentType || 'application/octet-stream',
contentLength: s3Response.ContentLength,
fileName: s3Key.split('/').pop() || 'download'
};
} catch (error) {
console.error('Error downloading document:', error);
throw new NotFoundException('Document file not found in storage');
console.error('Error downloading from S3:', error);
throw new NotFoundException('Failed to download file');
}
}
async getAllDocuments(): Promise<Document[]> {
return this.prisma.document.findMany({
include: {
sharedWith: {
select: {
id: true,
name: true,
email: true,
},
},
},
orderBy: {
createdAt: 'desc',
},
});
}
async shareDocument(documentId: number, userId: number): Promise<Document> {
const document = await this.prisma.document.update({
where: { id: documentId },
data: {
sharedWithId: userId,
},
include: {
sharedWith: {
select: {
id: true,
name: true,
email: true,
},
},
},
});
return document;
}
async updateStatus(documentId: number, status: string): Promise<Document> {
return this.prisma.document.update({
where: { id: documentId },
data: { status },
include: {
sharedWith: {
select: {
id: true,
name: true,
email: true,
},
},
},
});
}
}
+45 -37
View File
@@ -5,6 +5,7 @@ import {
DeleteObjectCommand,
GetObjectCommand,
ListObjectsCommand,
PutObjectCommand,
} from '@aws-sdk/client-s3';
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
import { Upload } from '@aws-sdk/lib-storage';
@@ -14,7 +15,6 @@ import { ConfigService } from '@nestjs/config';
export class S3Service {
private s3Client: S3Client;
private readonly logger = new Logger(S3Service.name);
s3: any;
constructor(private configService: ConfigService) {
this.s3Client = new S3Client({
@@ -28,45 +28,54 @@ export class S3Service {
});
}
async uploadFile(file: Express.Multer.File, key: string): Promise<string> {
async getObject(key: string) {
try {
const uniqueKey = `${Date.now()}-${key}`; // Ensure unique keys
const upload = new Upload({
client: this.s3Client,
params: {
Bucket: process.env.AWS_S3_BUCKET_NAME,
Key: uniqueKey,
Body: file.buffer,
},
const command = new GetObjectCommand({
Bucket: this.configService.get('AWS_S3_BUCKET_NAME'),
Key: key,
});
const result = await upload.done();
console.log(`File uploaded successfully: ${uniqueKey}`);
console.log(result);
return uniqueKey;
const response = await this.s3Client.send(command);
return response;
} catch (error) {
console.error(`Error uploading file: ${error.message}`);
this.logger.error(`Error getting object from S3: ${error.message}`);
throw error;
}
}
async uploadFile(file: Express.Multer.File, folder: string): Promise<string> {
try {
const key = `${folder}/${Date.now()}-${file.originalname}`;
const command = new PutObjectCommand({
Bucket: this.configService.get('AWS_S3_BUCKET_NAME'),
Key: key,
Body: file.buffer,
ContentType: file.mimetype,
});
await this.s3Client.send(command);
return key;
} catch (error) {
this.logger.error(`Error uploading file to S3: ${error.message}`);
throw error;
}
}
async deleteFile(key: string): Promise<void> {
const command = new DeleteObjectCommand({
Bucket: this.configService.get('AWS_S3_BUCKET_NAME'),
Key: key,
});
try {
const command = new DeleteObjectCommand({
Bucket: this.configService.get('AWS_S3_BUCKET_NAME'),
Key: key,
});
await this.s3Client.send(command);
}
getObject(key: string) {
return this.s3.getObject({
Bucket: process.env.AWS_S3_BUCKET,
Key: key,
});
await this.s3Client.send(command);
} catch (error) {
this.logger.error(`Error deleting file from S3: ${error.message}`);
throw error;
}
}
async getFileUrl(key: string): Promise<string> {
async getFileUrl(key: string): Promise<string> {
const command = new GetObjectCommand({
Bucket: this.configService.get('AWS_S3_BUCKET_NAME'),
Key: key,
@@ -75,21 +84,20 @@ export class S3Service {
const url = await getSignedUrl(this.s3Client, command, { expiresIn: 3600 }); // URL expires in 1 hour
return url;
}
async testConnection(): Promise<boolean> {
try {
const command = new ListObjectsCommand({
Bucket: this.configService.get('AWS_S3_BUCKET_NAME'),
MaxKeys: 1,
});
const response = await this.s3Client.send(command);
this.logger.log(
`Successfully connected to S3. Bucket contains ${response.Contents?.length || 0} objects.`,
);
await this.getObject('test-connection');
return true;
} catch (error) {
if (error.name === 'NoSuchKey') {
// This is expected as we're just testing connection
return true;
}
this.logger.error('Failed to connect to S3', error);
return false;
}
}
}