download working
This commit is contained in:
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,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user