move to storage service

This commit is contained in:
2024-03-15 13:37:07 +01:00
parent b2a971fd30
commit 22bc42f635
11 changed files with 132 additions and 1295 deletions

View File

@@ -24,9 +24,7 @@
"test:e2e": "jest --config ./test/jest-e2e.json" "test:e2e": "jest --config ./test/jest-e2e.json"
}, },
"dependencies": { "dependencies": {
"@aws-sdk/client-s3": "^3.454.0", "@it-incubator/storage-sdk": "^0.0.8",
"@aws-sdk/client-ses": "^3.454.0",
"@aws-sdk/credential-provider-node": "^3.451.0",
"@nestjs-modules/mailer": "^1.9.1", "@nestjs-modules/mailer": "^1.9.1",
"@nestjs/common": "10.2.9", "@nestjs/common": "10.2.9",
"@nestjs/config": "^3.1.1", "@nestjs/config": "^3.1.1",
@@ -41,7 +39,6 @@
"@nestjs/swagger": "^7.1.16", "@nestjs/swagger": "^7.1.16",
"@prisma/client": "4.16.0", "@prisma/client": "4.16.0",
"@types/passport-local": "^1.0.38", "@types/passport-local": "^1.0.38",
"aws-sdk": "^2.1499.0",
"bcrypt": "5.1.0", "bcrypt": "5.1.0",
"class-transformer": "0.5.1", "class-transformer": "0.5.1",
"class-validator": "^0.14.0", "class-validator": "^0.14.0",
@@ -56,7 +53,6 @@
"reflect-metadata": "^0.1.13", "reflect-metadata": "^0.1.13",
"remeda": "^1.29.0", "remeda": "^1.29.0",
"rxjs": "^7.8.1", "rxjs": "^7.8.1",
"swagger-themes": "^1.2.30",
"uuid": "^9.0.1" "uuid": "^9.0.1"
}, },
"devDependencies": { "devDependencies": {

1262
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,7 @@
import { join } from 'path' import { join } from 'path'
import * as process from 'process' import * as process from 'process'
import { StorageModule } from '@it-incubator/storage-sdk'
import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common' import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common'
import { CqrsModule } from '@nestjs/cqrs' import { CqrsModule } from '@nestjs/cqrs'
import { ServeStaticModule } from '@nestjs/serve-static' import { ServeStaticModule } from '@nestjs/serve-static'
@@ -28,6 +29,12 @@ import { ConfigModule } from './settings/config.module'
ServeStaticModule.forRoot({ ServeStaticModule.forRoot({
rootPath: join(__dirname, '..', 'client'), rootPath: join(__dirname, '..', 'client'),
}), }),
StorageModule.register({
baseURL: process.env.STORAGE_SERVICE_URL,
headers: {
'service-token': process.env.STORAGE_SERVICE_TOKEN,
},
}),
MailerModule.forRoot({ MailerModule.forRoot({
transport: { transport: {
host: process.env.AWS_SES_SMTP_HOST, host: process.env.AWS_SES_SMTP_HOST,

View File

@@ -1,46 +1,35 @@
import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3' import { CreateFileDto, FileType, StorageService } from '@it-incubator/storage-sdk'
import { Injectable } from '@nestjs/common' import { Injectable } from '@nestjs/common'
import { v4 as uuid } from 'uuid'
import { PrismaService } from '../../prisma.service' import { PrismaService } from '../../prisma.service'
@Injectable() @Injectable()
export class FileUploadService { export class FileUploadService {
constructor(private prismaService: PrismaService) {} constructor(
private prismaService: PrismaService,
private storageService: StorageService
) {}
async uploadFile(dataBuffer: Buffer, fileName: string) { private async uploadFileToStorageService(dto: CreateFileDto) {
const key = `${uuid()}-${fileName}` return await this.storageService.create(dto).then(data => data.data)
const bucketName = process.env.AWS_BUCKET_NAME }
const region = 'eu-central-1'
const s3 = new S3Client({
region,
credentials: {
accessKeyId: process.env.AWS_S3_ACCESS_KEY,
secretAccessKey: process.env.AWS_S3_SECRET_ACCESS_KEY,
},
})
const encodeFileName = encodeURIComponent(key)
const fileUrl = `https://${bucketName}.s3.${region}.amazonaws.com/${encodeFileName}` async uploadFile(file: CreateFileDto['file']) {
try {
const savedFile = await this.uploadFileToStorageService({
fileType: FileType.Image,
file,
})
const fileStorageInDB = { return this.prismaService.fileEntity.create({
fileName, data: {
fileUrl, fileName: savedFile.name,
key, fileUrl: savedFile.url,
key: savedFile.url,
},
})
} catch (e) {
console.log(e)
} }
const putCommand = new PutObjectCommand({
Bucket: bucketName,
Body: dataBuffer,
Key: key,
ContentDisposition: 'inline',
ContentType: `image/${fileName.split('.').at(-1)}`,
})
await s3.send(putCommand)
return this.prismaService.fileEntity.create({
data: fileStorageInDB,
})
} }
} }

View File

@@ -25,10 +25,7 @@ export class UpdateUserHandler implements ICommandHandler<UpdateUserCommand> {
let avatar: string | null let avatar: string | null
if (command.avatar) { if (command.avatar) {
const addAvatarImagePromise = this.fileUploadService.uploadFile( const addAvatarImagePromise = this.fileUploadService.uploadFile(command.avatar)
command.avatar?.buffer,
command.avatar?.originalname
)
const result = await addAvatarImagePromise const result = await addAvatarImagePromise

View File

@@ -35,32 +35,20 @@ export class UpdateCardHandler implements ICommandHandler<UpdateCardCommand> {
let questionImg, answerImg let questionImg, answerImg
if (command.questionImg && command.answerImg) { if (command.questionImg && command.answerImg) {
const addQuestionImagePromise = this.fileUploadService.uploadFile( const addQuestionImagePromise = this.fileUploadService.uploadFile(command.questionImg)
command.questionImg?.buffer, const addAnswerImagePromise = this.fileUploadService.uploadFile(command.answerImg)
command.questionImg?.originalname
)
const addAnswerImagePromise = this.fileUploadService.uploadFile(
command.answerImg?.buffer,
command.answerImg?.originalname
)
const result = await Promise.all([addQuestionImagePromise, addAnswerImagePromise]) const result = await Promise.all([addQuestionImagePromise, addAnswerImagePromise])
questionImg = result[0].fileUrl questionImg = result[0].fileUrl
answerImg = result[1].fileUrl answerImg = result[1].fileUrl
} else if (command.answerImg) { } else if (command.answerImg) {
const addAnswerImagePromise = this.fileUploadService.uploadFile( const addAnswerImagePromise = this.fileUploadService.uploadFile(command.answerImg)
command.answerImg?.buffer,
command.answerImg?.originalname
)
const result = await addAnswerImagePromise const result = await addAnswerImagePromise
answerImg = result.fileUrl answerImg = result.fileUrl
} else if (command.questionImg) { } else if (command.questionImg) {
const addQuestionImagePromise = this.fileUploadService.uploadFile( const addQuestionImagePromise = this.fileUploadService.uploadFile(command.questionImg)
command.questionImg?.buffer,
command.questionImg?.originalname
)
const result = await addQuestionImagePromise const result = await addQuestionImagePromise
questionImg = result.fileUrl questionImg = result.fileUrl

View File

@@ -43,6 +43,7 @@ import {
PaginatedDecksWithMaxCardsCount, PaginatedDecksWithMaxCardsCount,
} from './entities/deck.entity' } from './entities/deck.entity'
import { MinMaxCards } from './entities/min-max-cards.entity' import { MinMaxCards } from './entities/min-max-cards.entity'
import { DecksRepository } from './infrastructure/decks.repository'
import { import {
CreateCardCommand, CreateCardCommand,
CreateDeckCommand, CreateDeckCommand,
@@ -60,7 +61,10 @@ import {
@ApiTags('Decks') @ApiTags('Decks')
@Controller('decks') @Controller('decks')
export class DecksController { export class DecksController {
constructor(private commandBus: CommandBus) {} constructor(
private commandBus: CommandBus,
private decksRepository: DecksRepository
) {}
@HttpCode(HttpStatus.PARTIAL_CONTENT) @HttpCode(HttpStatus.PARTIAL_CONTENT)
@ApiOperation({ @ApiOperation({
@@ -89,6 +93,25 @@ export class DecksController {
return this.commandBus.execute(new GetAllDecksV2Command({ ...finalQuery, userId: req.user.id })) return this.commandBus.execute(new GetAllDecksV2Command({ ...finalQuery, userId: req.user.id }))
} }
@HttpCode(HttpStatus.PARTIAL_CONTENT)
@ApiOperation({ description: 'Retrieve paginated decks list.', summary: 'Paginated decks list' })
@ApiUnauthorizedResponse({ description: 'Unauthorized' })
@UseGuards(JwtAuthGuard)
@Version('2')
@Get('empty')
async findAllEmpty(@Query() query: GetAllDecksDto, @Req() req) {
const result: PaginatedDecks = await this.commandBus.execute(
new GetAllDecksV2Command({
itemsPerPage: 5000,
minCardsCount: 0,
maxCardsCount: 0,
userId: req.user.id,
})
)
return this.decksRepository.deleteManyById(result.items.map(({ id }) => id))
}
@HttpCode(HttpStatus.OK) @HttpCode(HttpStatus.OK)
@ApiOperation({ @ApiOperation({
description: 'Retrieve the minimum and maximum amount of cards in a deck.', description: 'Retrieve the minimum and maximum amount of cards in a deck.',

View File

@@ -294,6 +294,21 @@ LIMIT $${conditions.length + havingConditions.length + 1} OFFSET $${
} }
} }
public async deleteManyById(id: string[]) {
try {
return await this.prisma.deck.deleteMany({
where: {
id: {
in: id,
},
},
})
} catch (e) {
this.logger.error(e?.message)
throw new InternalServerErrorException(e?.message)
}
}
public async deleteDeckById(id: string) { public async deleteDeckById(id: string) {
try { try {
return await this.prisma.deck.delete({ return await this.prisma.deck.delete({

View File

@@ -38,32 +38,20 @@ export class CreateCardHandler implements ICommandHandler<CreateCardCommand> {
} }
if (command.questionImg && command.answerImg) { if (command.questionImg && command.answerImg) {
const addQuestionImagePromise = this.fileUploadService.uploadFile( const addQuestionImagePromise = this.fileUploadService.uploadFile(command.questionImg)
command.questionImg?.buffer, const addAnswerImagePromise = this.fileUploadService.uploadFile(command.answerImg)
command.questionImg?.originalname
)
const addAnswerImagePromise = this.fileUploadService.uploadFile(
command.answerImg?.buffer,
command.answerImg?.originalname
)
const result = await Promise.all([addQuestionImagePromise, addAnswerImagePromise]) const result = await Promise.all([addQuestionImagePromise, addAnswerImagePromise])
questionImg = result[0].fileUrl questionImg = result[0].fileUrl
answerImg = result[1].fileUrl answerImg = result[1].fileUrl
} else if (command.answerImg) { } else if (command.answerImg) {
const addAnswerImagePromise = this.fileUploadService.uploadFile( const addAnswerImagePromise = this.fileUploadService.uploadFile(command.answerImg)
command.answerImg?.buffer,
command.answerImg?.originalname
)
const result = await addAnswerImagePromise const result = await addAnswerImagePromise
answerImg = result.fileUrl answerImg = result.fileUrl
} else if (command.questionImg) { } else if (command.questionImg) {
const addQuestionImagePromise = this.fileUploadService.uploadFile( const addQuestionImagePromise = this.fileUploadService.uploadFile(command.questionImg)
command.questionImg?.buffer,
command.questionImg?.originalname
)
const result = await addQuestionImagePromise const result = await addQuestionImagePromise
questionImg = result.fileUrl questionImg = result.fileUrl

View File

@@ -23,10 +23,7 @@ export class CreateDeckHandler implements ICommandHandler<CreateDeckCommand> {
let cover let cover
if (command.cover) { if (command.cover) {
const result = await this.fileUploadService.uploadFile( const result = await this.fileUploadService.uploadFile(command.cover)
command.cover.buffer,
command.cover.originalname
)
cover = result.fileUrl cover = result.fileUrl
} }

View File

@@ -35,10 +35,7 @@ export class UpdateDeckHandler implements ICommandHandler<UpdateDeckCommand> {
let cover let cover
if (command.cover) { if (command.cover) {
const result = await this.fileUploadService.uploadFile( const result = await this.fileUploadService.uploadFile(command.cover)
command.cover.buffer,
command.cover.originalname
)
cover = result.fileUrl cover = result.fileUrl
} else if (command.deck.cover === '') { } else if (command.deck.cover === '') {