mirror of
https://github.com/ershisan99/flashcards-api.git
synced 2025-12-17 12:33:22 +00:00
move to storage service
This commit is contained in:
@@ -24,9 +24,7 @@
|
||||
"test:e2e": "jest --config ./test/jest-e2e.json"
|
||||
},
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-s3": "^3.454.0",
|
||||
"@aws-sdk/client-ses": "^3.454.0",
|
||||
"@aws-sdk/credential-provider-node": "^3.451.0",
|
||||
"@it-incubator/storage-sdk": "^0.0.8",
|
||||
"@nestjs-modules/mailer": "^1.9.1",
|
||||
"@nestjs/common": "10.2.9",
|
||||
"@nestjs/config": "^3.1.1",
|
||||
@@ -41,7 +39,6 @@
|
||||
"@nestjs/swagger": "^7.1.16",
|
||||
"@prisma/client": "4.16.0",
|
||||
"@types/passport-local": "^1.0.38",
|
||||
"aws-sdk": "^2.1499.0",
|
||||
"bcrypt": "5.1.0",
|
||||
"class-transformer": "0.5.1",
|
||||
"class-validator": "^0.14.0",
|
||||
@@ -56,7 +53,6 @@
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"remeda": "^1.29.0",
|
||||
"rxjs": "^7.8.1",
|
||||
"swagger-themes": "^1.2.30",
|
||||
"uuid": "^9.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
1262
pnpm-lock.yaml
generated
1262
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,7 @@
|
||||
import { join } from 'path'
|
||||
import * as process from 'process'
|
||||
|
||||
import { StorageModule } from '@it-incubator/storage-sdk'
|
||||
import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common'
|
||||
import { CqrsModule } from '@nestjs/cqrs'
|
||||
import { ServeStaticModule } from '@nestjs/serve-static'
|
||||
@@ -28,6 +29,12 @@ import { ConfigModule } from './settings/config.module'
|
||||
ServeStaticModule.forRoot({
|
||||
rootPath: join(__dirname, '..', 'client'),
|
||||
}),
|
||||
StorageModule.register({
|
||||
baseURL: process.env.STORAGE_SERVICE_URL,
|
||||
headers: {
|
||||
'service-token': process.env.STORAGE_SERVICE_TOKEN,
|
||||
},
|
||||
}),
|
||||
MailerModule.forRoot({
|
||||
transport: {
|
||||
host: process.env.AWS_SES_SMTP_HOST,
|
||||
|
||||
@@ -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 { v4 as uuid } from 'uuid'
|
||||
|
||||
import { PrismaService } from '../../prisma.service'
|
||||
|
||||
@Injectable()
|
||||
export class FileUploadService {
|
||||
constructor(private prismaService: PrismaService) {}
|
||||
constructor(
|
||||
private prismaService: PrismaService,
|
||||
private storageService: StorageService
|
||||
) {}
|
||||
|
||||
async uploadFile(dataBuffer: Buffer, fileName: string) {
|
||||
const key = `${uuid()}-${fileName}`
|
||||
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}`
|
||||
|
||||
const fileStorageInDB = {
|
||||
fileName,
|
||||
fileUrl,
|
||||
key,
|
||||
private async uploadFileToStorageService(dto: CreateFileDto) {
|
||||
return await this.storageService.create(dto).then(data => data.data)
|
||||
}
|
||||
|
||||
const putCommand = new PutObjectCommand({
|
||||
Bucket: bucketName,
|
||||
Body: dataBuffer,
|
||||
Key: key,
|
||||
ContentDisposition: 'inline',
|
||||
ContentType: `image/${fileName.split('.').at(-1)}`,
|
||||
async uploadFile(file: CreateFileDto['file']) {
|
||||
try {
|
||||
const savedFile = await this.uploadFileToStorageService({
|
||||
fileType: FileType.Image,
|
||||
file,
|
||||
})
|
||||
|
||||
await s3.send(putCommand)
|
||||
|
||||
return this.prismaService.fileEntity.create({
|
||||
data: fileStorageInDB,
|
||||
data: {
|
||||
fileName: savedFile.name,
|
||||
fileUrl: savedFile.url,
|
||||
key: savedFile.url,
|
||||
},
|
||||
})
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,10 +25,7 @@ export class UpdateUserHandler implements ICommandHandler<UpdateUserCommand> {
|
||||
let avatar: string | null
|
||||
|
||||
if (command.avatar) {
|
||||
const addAvatarImagePromise = this.fileUploadService.uploadFile(
|
||||
command.avatar?.buffer,
|
||||
command.avatar?.originalname
|
||||
)
|
||||
const addAvatarImagePromise = this.fileUploadService.uploadFile(command.avatar)
|
||||
|
||||
const result = await addAvatarImagePromise
|
||||
|
||||
|
||||
@@ -35,32 +35,20 @@ export class UpdateCardHandler implements ICommandHandler<UpdateCardCommand> {
|
||||
let questionImg, answerImg
|
||||
|
||||
if (command.questionImg && command.answerImg) {
|
||||
const addQuestionImagePromise = this.fileUploadService.uploadFile(
|
||||
command.questionImg?.buffer,
|
||||
command.questionImg?.originalname
|
||||
)
|
||||
const addAnswerImagePromise = this.fileUploadService.uploadFile(
|
||||
command.answerImg?.buffer,
|
||||
command.answerImg?.originalname
|
||||
)
|
||||
const addQuestionImagePromise = this.fileUploadService.uploadFile(command.questionImg)
|
||||
const addAnswerImagePromise = this.fileUploadService.uploadFile(command.answerImg)
|
||||
|
||||
const result = await Promise.all([addQuestionImagePromise, addAnswerImagePromise])
|
||||
|
||||
questionImg = result[0].fileUrl
|
||||
answerImg = result[1].fileUrl
|
||||
} else if (command.answerImg) {
|
||||
const addAnswerImagePromise = this.fileUploadService.uploadFile(
|
||||
command.answerImg?.buffer,
|
||||
command.answerImg?.originalname
|
||||
)
|
||||
const addAnswerImagePromise = this.fileUploadService.uploadFile(command.answerImg)
|
||||
const result = await addAnswerImagePromise
|
||||
|
||||
answerImg = result.fileUrl
|
||||
} else if (command.questionImg) {
|
||||
const addQuestionImagePromise = this.fileUploadService.uploadFile(
|
||||
command.questionImg?.buffer,
|
||||
command.questionImg?.originalname
|
||||
)
|
||||
const addQuestionImagePromise = this.fileUploadService.uploadFile(command.questionImg)
|
||||
const result = await addQuestionImagePromise
|
||||
|
||||
questionImg = result.fileUrl
|
||||
|
||||
@@ -43,6 +43,7 @@ import {
|
||||
PaginatedDecksWithMaxCardsCount,
|
||||
} from './entities/deck.entity'
|
||||
import { MinMaxCards } from './entities/min-max-cards.entity'
|
||||
import { DecksRepository } from './infrastructure/decks.repository'
|
||||
import {
|
||||
CreateCardCommand,
|
||||
CreateDeckCommand,
|
||||
@@ -60,7 +61,10 @@ import {
|
||||
@ApiTags('Decks')
|
||||
@Controller('decks')
|
||||
export class DecksController {
|
||||
constructor(private commandBus: CommandBus) {}
|
||||
constructor(
|
||||
private commandBus: CommandBus,
|
||||
private decksRepository: DecksRepository
|
||||
) {}
|
||||
|
||||
@HttpCode(HttpStatus.PARTIAL_CONTENT)
|
||||
@ApiOperation({
|
||||
@@ -89,6 +93,25 @@ export class DecksController {
|
||||
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)
|
||||
@ApiOperation({
|
||||
description: 'Retrieve the minimum and maximum amount of cards in a deck.',
|
||||
|
||||
@@ -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) {
|
||||
try {
|
||||
return await this.prisma.deck.delete({
|
||||
|
||||
@@ -38,32 +38,20 @@ export class CreateCardHandler implements ICommandHandler<CreateCardCommand> {
|
||||
}
|
||||
|
||||
if (command.questionImg && command.answerImg) {
|
||||
const addQuestionImagePromise = this.fileUploadService.uploadFile(
|
||||
command.questionImg?.buffer,
|
||||
command.questionImg?.originalname
|
||||
)
|
||||
const addAnswerImagePromise = this.fileUploadService.uploadFile(
|
||||
command.answerImg?.buffer,
|
||||
command.answerImg?.originalname
|
||||
)
|
||||
const addQuestionImagePromise = this.fileUploadService.uploadFile(command.questionImg)
|
||||
const addAnswerImagePromise = this.fileUploadService.uploadFile(command.answerImg)
|
||||
|
||||
const result = await Promise.all([addQuestionImagePromise, addAnswerImagePromise])
|
||||
|
||||
questionImg = result[0].fileUrl
|
||||
answerImg = result[1].fileUrl
|
||||
} else if (command.answerImg) {
|
||||
const addAnswerImagePromise = this.fileUploadService.uploadFile(
|
||||
command.answerImg?.buffer,
|
||||
command.answerImg?.originalname
|
||||
)
|
||||
const addAnswerImagePromise = this.fileUploadService.uploadFile(command.answerImg)
|
||||
const result = await addAnswerImagePromise
|
||||
|
||||
answerImg = result.fileUrl
|
||||
} else if (command.questionImg) {
|
||||
const addQuestionImagePromise = this.fileUploadService.uploadFile(
|
||||
command.questionImg?.buffer,
|
||||
command.questionImg?.originalname
|
||||
)
|
||||
const addQuestionImagePromise = this.fileUploadService.uploadFile(command.questionImg)
|
||||
const result = await addQuestionImagePromise
|
||||
|
||||
questionImg = result.fileUrl
|
||||
|
||||
@@ -23,10 +23,7 @@ export class CreateDeckHandler implements ICommandHandler<CreateDeckCommand> {
|
||||
let cover
|
||||
|
||||
if (command.cover) {
|
||||
const result = await this.fileUploadService.uploadFile(
|
||||
command.cover.buffer,
|
||||
command.cover.originalname
|
||||
)
|
||||
const result = await this.fileUploadService.uploadFile(command.cover)
|
||||
|
||||
cover = result.fileUrl
|
||||
}
|
||||
|
||||
@@ -35,10 +35,7 @@ export class UpdateDeckHandler implements ICommandHandler<UpdateDeckCommand> {
|
||||
let cover
|
||||
|
||||
if (command.cover) {
|
||||
const result = await this.fileUploadService.uploadFile(
|
||||
command.cover.buffer,
|
||||
command.cover.originalname
|
||||
)
|
||||
const result = await this.fileUploadService.uploadFile(command.cover)
|
||||
|
||||
cover = result.fileUrl
|
||||
} else if (command.deck.cover === '') {
|
||||
|
||||
Reference in New Issue
Block a user