everything works!
This commit is contained in:
1 parent
01017bb36e
commit
a32992b17b
19 files changed
+1554
-727
No files matched your search
@@ -5,6 +5,7 @@
|
||||
# See the documentation for all the connection string options: https://pris.ly/d/connection-strings
|
||||
|
||||
DATABASE_URL="postgresql://root:admin@localhost:5432/imk?schema=public"
|
||||
JWT_SECRET=some-secret
|
||||
AWS_REGION=EU2
|
||||
AWS_ACCESS_KEY_ID=4d2f5655369a02100375e3247d7e1fe6
|
||||
AWS_SECRET_ACCESS_KEY=6d4723e14c0d799b89948c24dbe983e4
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
Warnings:
|
||||
|
||||
- You are about to drop the column `content` on the `Document` table. All the data in the column will be lost.
|
||||
- You are about to drop the column `sharedWithId` on the `Document` table. All the data in the column will be lost.
|
||||
- Added the required column `uploadedById` to the `Document` table without a default value. This is not possible if the table is not empty.
|
||||
- Added the required column `documentId` to the `Notification` table without a default value. This is not possible if the table is not empty.
|
||||
|
||||
*/
|
||||
-- DropForeignKey
|
||||
ALTER TABLE "Document" DROP CONSTRAINT "Document_sharedWithId_fkey";
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "Document" DROP COLUMN "content",
|
||||
DROP COLUMN "sharedWithId",
|
||||
ADD COLUMN "uploadedById" INTEGER NOT NULL,
|
||||
ALTER COLUMN "status" DROP DEFAULT;
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "Notification" ADD COLUMN "documentId" INTEGER NOT NULL;
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "_SharedDocuments" (
|
||||
"A" INTEGER NOT NULL,
|
||||
"B" INTEGER NOT NULL
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "_SharedDocuments_AB_unique" ON "_SharedDocuments"("A", "B");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "_SharedDocuments_B_index" ON "_SharedDocuments"("B");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Document" ADD CONSTRAINT "Document_uploadedById_fkey" FOREIGN KEY ("uploadedById") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Notification" ADD CONSTRAINT "Notification_documentId_fkey" FOREIGN KEY ("documentId") REFERENCES "Document"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "_SharedDocuments" ADD CONSTRAINT "_SharedDocuments_A_fkey" FOREIGN KEY ("A") REFERENCES "Document"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "_SharedDocuments" ADD CONSTRAINT "_SharedDocuments_B_fkey" FOREIGN KEY ("B") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
@@ -8,32 +8,36 @@ datasource db {
|
||||
}
|
||||
|
||||
model Document {
|
||||
id Int @id @default(autoincrement())
|
||||
id Int @id @default(autoincrement())
|
||||
title String
|
||||
content String?
|
||||
s3Key String
|
||||
status String @default("pending")
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
sharedWithId Int
|
||||
sharedWith User @relation("SharedDocuments", fields: [sharedWithId], references: [id])
|
||||
status String
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
uploadedById Int
|
||||
uploadedBy User @relation("UploadedDocuments", fields: [uploadedById], references: [id])
|
||||
Notification Notification[]
|
||||
sharedWith User[] @relation("SharedDocuments")
|
||||
}
|
||||
|
||||
model User {
|
||||
id Int @id @default(autoincrement())
|
||||
email String @unique
|
||||
name String?
|
||||
password String
|
||||
isAdmin Boolean @default(false)
|
||||
sharedDocuments Document[] @relation("SharedDocuments")
|
||||
notifications Notification[]
|
||||
id Int @id @default(autoincrement())
|
||||
email String @unique
|
||||
name String?
|
||||
password String
|
||||
isAdmin Boolean @default(false)
|
||||
uploadedDocuments Document[] @relation("UploadedDocuments")
|
||||
Notification Notification[]
|
||||
sharedDocuments Document[] @relation("SharedDocuments")
|
||||
}
|
||||
|
||||
model Notification {
|
||||
id Int @id @default(autoincrement())
|
||||
message String
|
||||
read Boolean @default(false)
|
||||
userId Int
|
||||
createdAt DateTime @default(now())
|
||||
user User @relation(fields: [userId], references: [id])
|
||||
id Int @id @default(autoincrement())
|
||||
message String
|
||||
read Boolean @default(false)
|
||||
userId Int
|
||||
createdAt DateTime @default(now())
|
||||
documentId Int
|
||||
document Document @relation(fields: [documentId], references: [id])
|
||||
user User @relation(fields: [userId], references: [id])
|
||||
}
|
||||
@@ -1,3 +1,118 @@
|
||||
// import {
|
||||
// Controller,
|
||||
// Get,
|
||||
// Post,
|
||||
// Body,
|
||||
// Param,
|
||||
// Put,
|
||||
// UseInterceptors,
|
||||
// UploadedFile,
|
||||
// ParseIntPipe,
|
||||
// UseGuards,
|
||||
// } from '@nestjs/common';
|
||||
// import { FileInterceptor } from '@nestjs/platform-express';
|
||||
// import { AdminService } from './admin.service';
|
||||
// //import { CreateDocumentDto } from '../dto/create-document.dto';
|
||||
// import { UpdateDocumentDto } from '../dto/update-document.dto';
|
||||
// import { AdminGuard } from '../auth/admin.guard';
|
||||
// import { JwtAuthGuard } from '../auth/jwt-auth.guard';
|
||||
// import { CreateUserDto } from '../dto/create-user.dto';
|
||||
// import { S3Service } from 'src/s3/s3.service';
|
||||
// import { PrismaService } from 'src/prisma/prisma.service';
|
||||
|
||||
// @Controller('admin')
|
||||
// @UseGuards(JwtAuthGuard, AdminGuard)
|
||||
// export class AdminController {
|
||||
// constructor(
|
||||
// private readonly adminService: AdminService,
|
||||
// private readonly s3Service: S3Service,
|
||||
// private readonly prisma: PrismaService,
|
||||
// ) {}
|
||||
|
||||
// @Get('documents')
|
||||
// getAllDocuments() {
|
||||
// return this.adminService.getAllDocuments();
|
||||
// }
|
||||
|
||||
// @Put('documents/:id')
|
||||
// @UseInterceptors(FileInterceptor('file'))
|
||||
// updateDocument(
|
||||
// @Param('id', ParseIntPipe) id: number,
|
||||
// @Body() updateDocumentDto: UpdateDocumentDto,
|
||||
// @UploadedFile() file?: Express.Multer.File,
|
||||
// ) {
|
||||
// return this.adminService.updateDocument(id, updateDocumentDto, file);
|
||||
// }
|
||||
|
||||
// @Get('users')
|
||||
// getAllUsers() {
|
||||
// return this.adminService.getAllUsers();
|
||||
// }
|
||||
// @Post('test-document')
|
||||
// async testDocumentCreation() {
|
||||
// try {
|
||||
// const document = await this.prisma.document.create({
|
||||
// data: {
|
||||
// title: 'Test Document',
|
||||
// s3Key: 'test-key',
|
||||
// status: 'completed',
|
||||
// sharedWithId: 2, // ID of 'pero' user
|
||||
// },
|
||||
// });
|
||||
// return document;
|
||||
// } catch (error) {
|
||||
// console.error('Test document creation error:', error);
|
||||
// throw error;
|
||||
// }
|
||||
// }
|
||||
|
||||
// @Post('users')
|
||||
// async createUser(@Body() createUserDto: CreateUserDto) {
|
||||
// return this.adminService.createUser(createUserDto);
|
||||
// }
|
||||
|
||||
// @Post('documents/:id/share')
|
||||
// async shareDocument(
|
||||
// @Param('id') id: string,
|
||||
// @Body() { userId }: { userId: number },
|
||||
// ) {
|
||||
// return this.adminService.shareDocument(+id, userId);
|
||||
// }
|
||||
|
||||
// @Put('documents/:id/status')
|
||||
// async updateDocumentStatus(
|
||||
// @Param('id') id: string,
|
||||
// @Body() { status }: { status: string },
|
||||
// ) {
|
||||
// return this.adminService.updateDocumentStatus(+id, status);
|
||||
// }
|
||||
|
||||
// @Post('documents')
|
||||
// @UseInterceptors(FileInterceptor('file'))
|
||||
// async uploadDocument(
|
||||
// @UploadedFile() file: Express.Multer.File,
|
||||
// @Body('title') title: string,
|
||||
// @Body('sharedWithId') sharedWithId: number,
|
||||
// @Body('uploadedById') uploadedById: number
|
||||
// ) {
|
||||
// const document = await this.adminService.uploadDocument(
|
||||
// file,
|
||||
// title,
|
||||
// sharedWithId,
|
||||
// uploadedById // Add this missing parameter
|
||||
// );
|
||||
// return document;
|
||||
// }
|
||||
// @Get('test-s3-connection')
|
||||
// async testS3Connection() {
|
||||
// const isConnected = await this.s3Service.testConnection();
|
||||
// if (isConnected) {
|
||||
// return { message: 'Successfully connected to S3' };
|
||||
// } else {
|
||||
// return { message: 'Failed to connect to S3' };
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
import {
|
||||
Controller,
|
||||
Get,
|
||||
@@ -9,10 +124,10 @@ import {
|
||||
UploadedFile,
|
||||
ParseIntPipe,
|
||||
UseGuards,
|
||||
BadRequestException,
|
||||
} from '@nestjs/common';
|
||||
import { FileInterceptor } from '@nestjs/platform-express';
|
||||
import { AdminService } from './admin.service';
|
||||
//import { CreateDocumentDto } from '../dto/create-document.dto';
|
||||
import { UpdateDocumentDto } from '../dto/update-document.dto';
|
||||
import { AdminGuard } from '../auth/admin.guard';
|
||||
import { JwtAuthGuard } from '../auth/jwt-auth.guard';
|
||||
@@ -44,27 +159,60 @@ export class AdminController {
|
||||
return this.adminService.updateDocument(id, updateDocumentDto, file);
|
||||
}
|
||||
|
||||
@Post('documents')
|
||||
@UseInterceptors(FileInterceptor('file'))
|
||||
async uploadDocument(
|
||||
@UploadedFile() file: Express.Multer.File,
|
||||
@Body('title') title: string,
|
||||
@Body('sharedWithId') sharedWithId: string, // Accept as string first
|
||||
@Body('uploadedById') uploadedById: string // Accept as string first
|
||||
) {
|
||||
if (!sharedWithId || !uploadedById) {
|
||||
throw new BadRequestException('sharedWithId and uploadedById are required');
|
||||
}
|
||||
|
||||
// Parse the string values to numbers
|
||||
const parsedSharedWithId = parseInt(sharedWithId, 10);
|
||||
const parsedUploadedById = parseInt(uploadedById, 10);
|
||||
|
||||
// Validate that the parsing was successful
|
||||
if (isNaN(parsedSharedWithId) || isNaN(parsedUploadedById)) {
|
||||
throw new BadRequestException('sharedWithId and uploadedById must be valid numbers');
|
||||
}
|
||||
|
||||
const document = await this.adminService.uploadDocument(
|
||||
file,
|
||||
title,
|
||||
parsedSharedWithId,
|
||||
parsedUploadedById
|
||||
);
|
||||
return document;
|
||||
}
|
||||
|
||||
@Get('users')
|
||||
getAllUsers() {
|
||||
return this.adminService.getAllUsers();
|
||||
}
|
||||
@Post('test-document')
|
||||
async testDocumentCreation() {
|
||||
try {
|
||||
const document = await this.prisma.document.create({
|
||||
data: {
|
||||
title: 'Test Document',
|
||||
s3Key: 'test-key',
|
||||
status: 'completed',
|
||||
sharedWithId: 2, // ID of 'pero' user
|
||||
},
|
||||
});
|
||||
return document;
|
||||
} catch (error) {
|
||||
console.error('Test document creation error:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// @Post('test-document')
|
||||
// async testDocumentCreation() {
|
||||
// try {
|
||||
// const document = await this.prisma.document.create({
|
||||
// data: {
|
||||
// title: 'Test Document',
|
||||
// s3Key: 'test-key',
|
||||
// status: 'completed',
|
||||
// sharedWith: {
|
||||
// connect: { id: 2 } // ID of 'pero' user
|
||||
// }
|
||||
// },
|
||||
// });
|
||||
// return document;
|
||||
// } catch (error) {
|
||||
// console.error('Test document creation error:', error);
|
||||
// throw error;
|
||||
// }
|
||||
// }
|
||||
|
||||
@Post('users')
|
||||
async createUser(@Body() createUserDto: CreateUserDto) {
|
||||
@@ -87,41 +235,14 @@ export class AdminController {
|
||||
return this.adminService.updateDocumentStatus(+id, status);
|
||||
}
|
||||
|
||||
@Post('documents')
|
||||
@UseInterceptors(FileInterceptor('file'))
|
||||
async uploadDocument(
|
||||
@UploadedFile() file: Express.Multer.File,
|
||||
@Body('title') title: string,
|
||||
@Body('sharedWith') sharedWithId: string,
|
||||
) {
|
||||
console.log('Received upload request:', {
|
||||
fileExists: !!file,
|
||||
fileSize: file?.size,
|
||||
title,
|
||||
sharedWithId,
|
||||
});
|
||||
|
||||
try {
|
||||
const document = await this.adminService.uploadDocument(
|
||||
file,
|
||||
title,
|
||||
Number(sharedWithId),
|
||||
);
|
||||
|
||||
console.log('Document created:', document);
|
||||
return document;
|
||||
} catch (error) {
|
||||
console.error('Upload error:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
@Get('test-s3-connection')
|
||||
async testS3Connection() {
|
||||
const isConnected = await this.s3Service.testConnection();
|
||||
if (isConnected) {
|
||||
return { message: 'Successfully connected to S3' };
|
||||
} else {
|
||||
return { message: 'Failed to connect to S3' };
|
||||
}
|
||||
}
|
||||
}
|
||||
// @Get('test-s3-connection')
|
||||
// async testS3Connection() {
|
||||
// const isConnected = await this.s3Service.testConnection();
|
||||
// if (isConnected) {
|
||||
// return { message: 'Successfully connected to S3' };
|
||||
// } else {
|
||||
// return { message: 'Failed to connect to S3' };
|
||||
// }
|
||||
// }
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Injectable, NotFoundException } from '@nestjs/common';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
import { S3Service } from '../s3/s3.service';
|
||||
//import { CreateDocumentDto } from '../dto/create-document.dto';
|
||||
import { UpdateDocumentDto } from '../dto/update-document.dto';
|
||||
import { CreateUserDto } from '../dto/create-user.dto';
|
||||
import * as bcrypt from 'bcrypt';
|
||||
@@ -9,93 +8,44 @@ import * as bcrypt from 'bcrypt';
|
||||
@Injectable()
|
||||
export class AdminService {
|
||||
constructor(
|
||||
private prisma: PrismaService,
|
||||
private s3Service: S3Service,
|
||||
private readonly prisma: PrismaService,
|
||||
private readonly s3Service: S3Service,
|
||||
) {}
|
||||
|
||||
async uploadDocument(
|
||||
file: Express.Multer.File,
|
||||
title: string,
|
||||
sharedWithId: number,
|
||||
) {
|
||||
try {
|
||||
// First verify the user exists
|
||||
console.log('Verifying user:', sharedWithId);
|
||||
const user = await this.prisma.user.findUnique({
|
||||
where: { id: sharedWithId },
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
throw new NotFoundException(`User with ID ${sharedWithId} not found`);
|
||||
}
|
||||
|
||||
console.log('Found user:', user);
|
||||
|
||||
// Upload file to S3
|
||||
console.log('Uploading file to S3...');
|
||||
const s3Key = await this.s3Service.uploadFile(file, 'documents');
|
||||
console.log('File uploaded to S3:', s3Key);
|
||||
|
||||
console.log('Creating document record with data:', {
|
||||
title,
|
||||
s3Key,
|
||||
sharedWithId,
|
||||
});
|
||||
|
||||
const document = await this.prisma.document.create({
|
||||
data: {
|
||||
title,
|
||||
s3Key,
|
||||
status: 'completed',
|
||||
sharedWithId,
|
||||
},
|
||||
include: {
|
||||
sharedWith: {
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
email: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
console.log('Created document:', document);
|
||||
return document;
|
||||
} catch (error) {
|
||||
console.error('Error in uploadDocument:', error);
|
||||
if (error.code === 'P2002') {
|
||||
console.error('Unique constraint violation');
|
||||
}
|
||||
if (error.code === 'P2003') {
|
||||
console.error('Foreign key constraint violation');
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
async getAllDocuments() {
|
||||
try {
|
||||
const documents = await this.prisma.document.findMany({
|
||||
include: {
|
||||
sharedWith: {
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
email: true,
|
||||
},
|
||||
return this.prisma.document.findMany({
|
||||
include: {
|
||||
uploadedBy: {
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
email: true,
|
||||
},
|
||||
},
|
||||
orderBy: {
|
||||
createdAt: 'desc',
|
||||
sharedWith: {
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
email: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
console.log('Retrieved documents with shared users:', documents);
|
||||
return documents;
|
||||
} catch (error) {
|
||||
console.error('Error fetching documents:', error);
|
||||
throw error;
|
||||
}
|
||||
async getAllUsers() {
|
||||
return this.prisma.user.findMany();
|
||||
}
|
||||
|
||||
async createUser(createUserDto: CreateUserDto) {
|
||||
const hashedPassword = await bcrypt.hash(createUserDto.password, 10);
|
||||
|
||||
return this.prisma.user.create({
|
||||
data: {
|
||||
...createUserDto,
|
||||
password: hashedPassword,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async updateDocument(
|
||||
@@ -103,76 +53,26 @@ export class AdminService {
|
||||
updateDocumentDto: UpdateDocumentDto,
|
||||
file?: Express.Multer.File,
|
||||
) {
|
||||
const { ...documentData } = updateDocumentDto;
|
||||
let s3Key: string | undefined;
|
||||
|
||||
let s3Key = undefined;
|
||||
if (file) {
|
||||
const oldDocument = await this.prisma.document.findUnique({
|
||||
where: { id },
|
||||
select: { s3Key: true },
|
||||
});
|
||||
if (oldDocument?.s3Key) {
|
||||
await this.s3Service.deleteFile(oldDocument.s3Key);
|
||||
}
|
||||
s3Key = await this.s3Service.uploadFile(file, 'documents');
|
||||
}
|
||||
|
||||
return this.prisma.document.update({
|
||||
where: { id },
|
||||
data: {
|
||||
...documentData,
|
||||
...updateDocumentDto,
|
||||
...(s3Key && { s3Key }),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// async createUser(createUserDto: CreateUserDto) {
|
||||
// const hashedPassword = await bcrypt.hash(createUserDto.password, 10);
|
||||
// return this.prisma.user.create({
|
||||
// data: {
|
||||
// ...createUserDto,
|
||||
// password: hashedPassword,
|
||||
// },
|
||||
// });
|
||||
// }
|
||||
|
||||
async createUser(userData: {
|
||||
name: string;
|
||||
email: string;
|
||||
password: string;
|
||||
isAdmin: boolean;
|
||||
}) {
|
||||
const hashedPassword = await bcrypt.hash(userData.password, 10);
|
||||
|
||||
return this.prisma.user.create({
|
||||
data: {
|
||||
name: userData.name,
|
||||
email: userData.email,
|
||||
password: hashedPassword,
|
||||
isAdmin: userData.isAdmin,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
email: true,
|
||||
isAdmin: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async shareDocument(documentId: number, userId: number) {
|
||||
return this.prisma.document.update({
|
||||
where: { id: documentId },
|
||||
data: {
|
||||
sharedWithId: userId,
|
||||
},
|
||||
include: {
|
||||
sharedWith: {
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
email: true,
|
||||
},
|
||||
connect: { id: userId },
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -185,43 +85,42 @@ export class AdminService {
|
||||
});
|
||||
}
|
||||
|
||||
async getDocumentUrl(documentId: number) {
|
||||
const document = await this.prisma.document.findUnique({
|
||||
where: { id: documentId },
|
||||
});
|
||||
if (!document) {
|
||||
throw new NotFoundException('Document not found');
|
||||
}
|
||||
return this.s3Service.getFileUrl(document.s3Key);
|
||||
}
|
||||
async getUserWithDocuments(userId: number) {
|
||||
return this.prisma.user.findUnique({
|
||||
where: { id: userId },
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
email: true,
|
||||
sharedDocuments: true,
|
||||
async uploadDocument(
|
||||
file: Express.Multer.File,
|
||||
title: string,
|
||||
sharedWithId: number,
|
||||
uploadedById: number
|
||||
) {
|
||||
const s3Key = await this.s3Service.uploadFile(file, 'documents');
|
||||
|
||||
return this.prisma.document.create({
|
||||
data: {
|
||||
title,
|
||||
s3Key,
|
||||
status: 'pending',
|
||||
sharedWith: {
|
||||
connect: { id: sharedWithId }
|
||||
},
|
||||
uploadedBy: {
|
||||
connect: { id: uploadedById }
|
||||
}
|
||||
},
|
||||
include: {
|
||||
uploadedBy: {
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
email: true,
|
||||
},
|
||||
},
|
||||
sharedWith: {
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
email: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async getAllUsers() {
|
||||
try {
|
||||
const users = await this.prisma.user.findMany({
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
email: true,
|
||||
isAdmin: true,
|
||||
sharedDocuments: true,
|
||||
},
|
||||
});
|
||||
console.log('All users:', users);
|
||||
return users;
|
||||
} catch (error) {
|
||||
console.error('Error fetching users:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,7 @@ import { PrismaModule } from './prisma/prisma.module';
|
||||
import { ConfigModule } from '@nestjs/config';
|
||||
import { AuthController } from './auth/auth.controller';
|
||||
import { DocumentsController } from './documents/documents.controller';
|
||||
import { JwtModule } from '@nestjs/jwt';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
@@ -26,7 +27,13 @@ import { DocumentsController } from './documents/documents.controller';
|
||||
// database: 'imk',
|
||||
// synchronize: true,
|
||||
// }),
|
||||
ConfigModule.forRoot(),
|
||||
ConfigModule.forRoot({
|
||||
isGlobal: true,
|
||||
}),
|
||||
JwtModule.register({
|
||||
secret: process.env.JWT_SECRET,
|
||||
signOptions: { expiresIn: '1h' },
|
||||
}),
|
||||
AuthModule,
|
||||
AdminModule,
|
||||
ClientModule,
|
||||
|
||||
@@ -3,12 +3,14 @@ import { JwtService } from '@nestjs/jwt';
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
import * as bcrypt from 'bcrypt';
|
||||
import { CreateUserDto } from '../dto/create-user.dto';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
|
||||
@Injectable()
|
||||
export class AuthService {
|
||||
constructor(
|
||||
private prisma: PrismaService,
|
||||
private jwtService: JwtService,
|
||||
private configService: ConfigService,
|
||||
) {}
|
||||
|
||||
async validateUser(username: string, password: string): Promise<any> {
|
||||
@@ -24,9 +26,16 @@ export class AuthService {
|
||||
}
|
||||
|
||||
async login(user: any) {
|
||||
const payload = { username: user.email, sub: user.id };
|
||||
// const payload = { username: user.email, sub: user.id };
|
||||
// return {
|
||||
// access_token: this.jwtService.sign(payload),
|
||||
// };
|
||||
const payload = { username: user.username, sub: user.id };
|
||||
console.log(payload);
|
||||
return {
|
||||
access_token: this.jwtService.sign(payload),
|
||||
access_token: this.jwtService.sign(payload, {
|
||||
secret: this.configService.get<string>('JWT_SECRET'),
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -59,15 +68,32 @@ export class AuthService {
|
||||
return result;
|
||||
}
|
||||
|
||||
// async getUserInfo(userId: number) {
|
||||
// return this.prisma.user.findUnique({
|
||||
// where: { id: userId },
|
||||
// select: {
|
||||
// id: true,
|
||||
// name: true,
|
||||
// email: true,
|
||||
// isAdmin: true,
|
||||
// },
|
||||
// });
|
||||
// }
|
||||
async getUserInfo(userId: number) {
|
||||
if (!userId) {
|
||||
throw new Error('User ID is required');
|
||||
}
|
||||
|
||||
return this.prisma.user.findUnique({
|
||||
where: { id: userId },
|
||||
where: {
|
||||
id: userId, // Make sure userId is properly passed and converted to number if needed
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
email: true,
|
||||
isAdmin: true,
|
||||
},
|
||||
isAdmin: true
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,15 @@
|
||||
import { ExtractJwt, Strategy } from 'passport-jwt';
|
||||
import { PassportStrategy } from '@nestjs/passport';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { jwtConstants } from './constants';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
|
||||
@Injectable()
|
||||
export class JwtStrategy extends PassportStrategy(Strategy) {
|
||||
constructor() {
|
||||
constructor(configService: ConfigService) {
|
||||
super({
|
||||
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
|
||||
ignoreExpiration: false,
|
||||
secretOrKey: jwtConstants.secret,
|
||||
secretOrKey: configService.get('JWT_SECRET'),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,11 @@ export class ClientService {
|
||||
async getDocuments(userId: string) {
|
||||
return this.prisma.document.findMany({
|
||||
where: {
|
||||
sharedWithId: Number(userId),
|
||||
sharedWith: {
|
||||
some: {
|
||||
id: Number(userId),
|
||||
},
|
||||
},
|
||||
},
|
||||
include: {
|
||||
sharedWith: {
|
||||
|
||||
@@ -1,33 +1,81 @@
|
||||
|
||||
import { Controller, Get, Param, Req, Res, UseGuards } from '@nestjs/common';
|
||||
import { Controller, Get, Param, Req, Res, UseGuards, Logger, Request } from '@nestjs/common';
|
||||
import { Response } from 'express';
|
||||
import { DocumentsService } from './documents.service';
|
||||
import { JwtAuthGuard } from 'src/auth/jwt-auth.guard';
|
||||
import { JwtAuthGuard } from '../auth/jwt-auth.guard';
|
||||
import { S3Service } from '../s3/s3.service';
|
||||
|
||||
interface S3File {
|
||||
buffer: Buffer;
|
||||
contentType: string;
|
||||
contentLength: number;
|
||||
fileName: string;
|
||||
}
|
||||
|
||||
@Controller('documents')
|
||||
export class DocumentsController {
|
||||
constructor(private readonly documentsService: DocumentsService) {}
|
||||
private readonly logger = new Logger(DocumentsController.name);
|
||||
|
||||
constructor(
|
||||
private readonly documentsService: DocumentsService,
|
||||
private readonly s3Service: S3Service
|
||||
) {}
|
||||
|
||||
@Get('shared/:userId')
|
||||
@UseGuards(JwtAuthGuard)
|
||||
async getSharedDocuments(@Param('userId') userId: string) {
|
||||
return this.documentsService.getClientDocuments(parseInt(userId));
|
||||
return this.documentsService.getClientDocuments(Number(userId));
|
||||
}
|
||||
|
||||
@Get('shared/download/:key')
|
||||
@Get('download/:key')
|
||||
@UseGuards(JwtAuthGuard)
|
||||
async downloadDocument(
|
||||
@Param('key') key: string,
|
||||
@Req() req,
|
||||
@Request() 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}"`,
|
||||
});
|
||||
try {
|
||||
this.logger.debug(`Download request for key: ${key}`);
|
||||
|
||||
const decodedKey = decodeURIComponent(key);
|
||||
this.logger.debug(`Decoded key: ${decodedKey}`);
|
||||
|
||||
res.send(file.buffer);
|
||||
// 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
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,236 +1,120 @@
|
||||
// // 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 { S3Service } from '../s3/s3.service';
|
||||
// import { Document, User } from '@prisma/client';
|
||||
|
||||
// @Injectable()
|
||||
// export class DocumentsService {
|
||||
// constructor(
|
||||
// private prisma: PrismaService,
|
||||
// private s3Service: S3Service,
|
||||
// ) {}
|
||||
|
||||
// async findAllByClient(userId: number): Promise<Document[]> {
|
||||
// const documents = await this.prisma.document.findMany({
|
||||
// where: {
|
||||
// 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 { Injectable, Logger, NotFoundException, ForbiddenException } 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';
|
||||
import { Document, User, Prisma } from '@prisma/client';
|
||||
|
||||
@Injectable()
|
||||
export class DocumentsService {
|
||||
private readonly logger = new Logger(DocumentsService.name);
|
||||
|
||||
constructor(
|
||||
private readonly prisma: PrismaService,
|
||||
private readonly s3Service: S3Service,
|
||||
private readonly s3Service: S3Service
|
||||
) {}
|
||||
|
||||
async getClientDocuments(clientId: number) {
|
||||
return this.prisma.document.findMany({
|
||||
where: {
|
||||
sharedWithId: clientId,
|
||||
},
|
||||
orderBy: {
|
||||
createdAt: 'desc',
|
||||
async findDocumentByS3Key(s3Key: string) {
|
||||
return this.prisma.document.findFirst({
|
||||
where: { s3Key },
|
||||
include: {
|
||||
uploadedBy: true,
|
||||
sharedWith: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async downloadDocument(s3Key: string, userId: number) {
|
||||
// Verify document access
|
||||
const document = await this.prisma.document.findFirst({
|
||||
where: {
|
||||
s3Key: s3Key,
|
||||
sharedWithId: userId,
|
||||
async verifyDocumentAccess(documentId: number, userId: number): Promise<boolean> {
|
||||
const document = await this.prisma.document.findUnique({
|
||||
where: { id: documentId },
|
||||
include: {
|
||||
uploadedBy: true,
|
||||
sharedWith: {
|
||||
where: {
|
||||
id: userId
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
if (!document) {
|
||||
throw new NotFoundException('Document not found or access denied');
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
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 from S3:', error);
|
||||
throw new NotFoundException('Failed to download file');
|
||||
}
|
||||
// User has access if they uploaded the document or it's shared with them
|
||||
return document.uploadedBy.id === userId || document.sharedWith.length > 0;
|
||||
}
|
||||
}
|
||||
|
||||
async getClientDocuments(clientId: number) {
|
||||
return this.prisma.document.findMany({
|
||||
where: {
|
||||
OR: [
|
||||
{ uploadedById: clientId },
|
||||
{
|
||||
sharedWith: {
|
||||
some: {
|
||||
id: clientId
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
include: {
|
||||
uploadedBy: {
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
email: true
|
||||
}
|
||||
},
|
||||
sharedWith: {
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
email: true
|
||||
}
|
||||
}
|
||||
},
|
||||
orderBy: {
|
||||
createdAt: 'desc'
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async uploadDocument(
|
||||
file: Express.Multer.File,
|
||||
title: string,
|
||||
sharedWithId: number,
|
||||
uploadedById: number
|
||||
) {
|
||||
const s3Key = await this.s3Service.uploadFile(file, 'documents');
|
||||
|
||||
return this.prisma.document.create({
|
||||
data: {
|
||||
title,
|
||||
s3Key,
|
||||
status: 'pending',
|
||||
uploadedBy: {
|
||||
connect: { id: uploadedById }
|
||||
},
|
||||
sharedWith: {
|
||||
connect: { id: sharedWithId }
|
||||
}
|
||||
},
|
||||
include: {
|
||||
uploadedBy: {
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
email: true,
|
||||
},
|
||||
},
|
||||
sharedWith: {
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
email: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
export interface FileResponse {
|
||||
buffer: Buffer;
|
||||
contentType: string;
|
||||
contentLength: number;
|
||||
fileName: string;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
export interface S3File {
|
||||
buffer: Buffer;
|
||||
contentType: string;
|
||||
contentLength: number;
|
||||
fileName: string;
|
||||
}
|
||||
@@ -1,101 +1,70 @@
|
||||
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
import {
|
||||
S3Client,
|
||||
DeleteObjectCommand,
|
||||
GetObjectCommand,
|
||||
ListObjectsCommand,
|
||||
PutObjectCommand,
|
||||
} from '@aws-sdk/client-s3';
|
||||
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
|
||||
import { Upload } from '@aws-sdk/lib-storage';
|
||||
import { Injectable, Logger, InternalServerErrorException } from '@nestjs/common';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { S3Client, PutObjectCommand, GetObjectCommand } from '@aws-sdk/client-s3';
|
||||
import { S3File } from '../interfaces/s3-file.interface';
|
||||
|
||||
@Injectable()
|
||||
export class S3Service {
|
||||
private s3Client: S3Client;
|
||||
private readonly s3Client: S3Client;
|
||||
private readonly logger = new Logger(S3Service.name);
|
||||
|
||||
constructor(private configService: ConfigService) {
|
||||
constructor(private readonly configService: ConfigService) {
|
||||
this.s3Client = new S3Client({
|
||||
region: this.configService.get('AWS_REGION'),
|
||||
endpoint: this.configService.get('AWS_ENDPOINT_URL'),
|
||||
region: this.configService.get<string>('AWS_REGION'),
|
||||
credentials: {
|
||||
accessKeyId: this.configService.get('AWS_ACCESS_KEY_ID'),
|
||||
secretAccessKey: this.configService.get('AWS_SECRET_ACCESS_KEY'),
|
||||
accessKeyId: this.configService.get<string>('AWS_ACCESS_KEY_ID'),
|
||||
secretAccessKey: this.configService.get<string>('AWS_SECRET_ACCESS_KEY'),
|
||||
},
|
||||
forcePathStyle: true, // Needed for non-AWS S3 compatible services
|
||||
endpoint: this.configService.get<string>('AWS_ENDPOINT_URL'),
|
||||
forcePathStyle: true, // Required for Contabo Object Storage
|
||||
});
|
||||
}
|
||||
|
||||
async getObject(key: string) {
|
||||
try {
|
||||
const command = new GetObjectCommand({
|
||||
Bucket: this.configService.get('AWS_S3_BUCKET_NAME'),
|
||||
Key: key,
|
||||
});
|
||||
|
||||
const response = await this.s3Client.send(command);
|
||||
return response;
|
||||
} catch (error) {
|
||||
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'),
|
||||
Bucket: this.configService.get<string>('AWS_S3_BUCKET_NAME'),
|
||||
Key: key,
|
||||
Body: file.buffer,
|
||||
ContentType: file.mimetype,
|
||||
});
|
||||
|
||||
await this.s3Client.send(command);
|
||||
this.logger.debug(`File uploaded successfully: ${key}`);
|
||||
|
||||
return key;
|
||||
} catch (error) {
|
||||
this.logger.error(`Error uploading file to S3: ${error.message}`);
|
||||
throw error;
|
||||
throw new InternalServerErrorException('Failed to upload file to storage');
|
||||
}
|
||||
}
|
||||
|
||||
async deleteFile(key: string): Promise<void> {
|
||||
async getFile(key: string): Promise<S3File> {
|
||||
try {
|
||||
const command = new DeleteObjectCommand({
|
||||
Bucket: this.configService.get('AWS_S3_BUCKET_NAME'),
|
||||
const command = new GetObjectCommand({
|
||||
Bucket: this.configService.get<string>('AWS_S3_BUCKET_NAME'),
|
||||
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> {
|
||||
const command = new GetObjectCommand({
|
||||
Bucket: this.configService.get('AWS_S3_BUCKET_NAME'),
|
||||
Key: key,
|
||||
});
|
||||
|
||||
const url = await getSignedUrl(this.s3Client, command, { expiresIn: 3600 }); // URL expires in 1 hour
|
||||
return url;
|
||||
}
|
||||
|
||||
async testConnection(): Promise<boolean> {
|
||||
try {
|
||||
await this.getObject('test-connection');
|
||||
return true;
|
||||
} catch (error) {
|
||||
if (error.name === 'NoSuchKey') {
|
||||
// This is expected as we're just testing connection
|
||||
return true;
|
||||
const response = await this.s3Client.send(command);
|
||||
|
||||
const chunks = [];
|
||||
for await (const chunk of response.Body as any) {
|
||||
chunks.push(chunk);
|
||||
}
|
||||
this.logger.error('Failed to connect to S3', error);
|
||||
return false;
|
||||
const buffer = Buffer.concat(chunks);
|
||||
|
||||
return {
|
||||
buffer,
|
||||
contentType: response.ContentType || 'application/octet-stream',
|
||||
contentLength: response.ContentLength || buffer.length,
|
||||
fileName: key.split('/').pop() || 'download',
|
||||
};
|
||||
} catch (error) {
|
||||
this.logger.error(`Error getting file from S3: ${error.message}`);
|
||||
throw new InternalServerErrorException('Failed to retrieve file from storage');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user