Skip to content

Commit

Permalink
feat(be): implement image upload (#1514)
Browse files Browse the repository at this point in the history
* feat(be): implement image upload to S3

* docs(be): add upload image api docs

* chore(be): modify image bucket baseurl

* docs(be): add assert

* chore(be): add alt property to return type of image-upload api

* chore(infra): rename testcase to storage

* feat(infra): add env for media bucket

* chore(be): add sample image and modify return object
- 샘플 이미지 추가
- src, alt 반환하던 것에서 src만 반환하는 것으로 변경

* Optimised images with calibre/image-actions

* chore(be): add s3-media-provider to test code

* chore(be): use relative path for sample image

* chore(be): replace client with media-client

* feat(be): add delete-image function

* refactor: combine bucket setup scripts

* fix(be): modify modify image size calculating logic
- 5MB 초과 파일이 들어왔을 때도 계속해서 파일 사이즈를 계산하는 문제 수정
- 5MB 초과 시 파일 사이즈 계산 중지, 예외 던지도록 구현

* chore(be): modify filename to use uuid only

* chore(be): remove duplicated import lines

* chore(be): remove file extension

* feat(be): add delete-image and improve image upload, delete logic

* docs(be): add delete-image api docs

* chore(be): parallelize deleting image logic

* chore(be): parallelize image-delete api

---------

Co-authored-by: k1g99 <idmatthew9546@gmail.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Jiyun Park <80032256+cho-to@users.noreply.github.com>
  • Loading branch information
4 people authored and mnseok committed Jul 4, 2024
1 parent 1b984b7 commit 2e23941
Show file tree
Hide file tree
Showing 26 changed files with 821 additions and 405 deletions.
2 changes: 1 addition & 1 deletion .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"database",
"cache",
"rabbitmq",
"testcase",
"storage",
"test-database"
],
"workspaceFolder": "/workspace",
Expand Down
11 changes: 10 additions & 1 deletion .env.stage
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,26 @@ RABBITMQ_CONSUMER_CONNECTION_NAME=iris-consumer
RABBITMQ_CONSUMER_TAG=consumer
RABBITMQ_PRODUCER_CONNECTION_NAME=iris-producer

# Storage
STORAGE_BUCKET_ENDPOINT_URL=http://127.0.0.1:9000
# Testcase Endpoint
TESTCASE_BUCKET_NAME=test-bucket
TESTCASE_ENDPOINT_URL=http://127.0.0.1:9000
# TESTCASE_ENDPOINT_URL=http://127.0.0.1:9000
TESTCASE_ACCESS_KEY=skku
TESTCASE_SECRET_KEY=skku1234

# Media Upload Endpoint
MEDIA_BUCKET_NAME=image-bucket
# MEDIA_BUCKET_BASE_URL=http://127.0.0.1:9000/image-bucket/
MEDIA_ACCESS_KEY=skku
MEDIA_SECRET_KEY=skku1234

REDIS_HOST=127.0.0.1
REDIS_PORT=6380

DATABASE_URL=postgresql://postgres:1234@127.0.0.1:5433/skkuding?schema=public
TEST_DATABASE_URL=postgresql://postgres:1234@127.0.0.1:5434/skkuding?schema=public


# TODO: Add information where each of these variables are used
# TODO: I want to edit values after the container is created...
2 changes: 1 addition & 1 deletion .gitpod.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ ports:
- port: 15672 # RabbitMQ Dashboard
onOpen: ignore

- port: 30000 # Testcase Server
- port: 30000 # Storage Server
onOpen: ignore

tasks:
Expand Down
7 changes: 7 additions & 0 deletions apps/backend/apps/admin/src/problem/model/image.output.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { Field, ObjectType } from '@nestjs/graphql'

@ObjectType({ description: 'image' })
export class ImageSource {
@Field(() => String)
src: string
}
3 changes: 2 additions & 1 deletion apps/backend/apps/admin/src/problem/problem.module.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Module } from '@nestjs/common'
import { ConfigModule } from '@nestjs/config'
import { StorageModule } from '@admin/storage/storage.module'
import { ProblemTagResolver, TagResolver } from './problem-tag.resolver'
import {
Expand All @@ -9,7 +10,7 @@ import {
import { ProblemService } from './problem.service'

@Module({
imports: [StorageModule],
imports: [StorageModule, ConfigModule],
providers: [
ProblemResolver,
ProblemTagResolver,
Expand Down
40 changes: 40 additions & 0 deletions apps/backend/apps/admin/src/problem/problem.resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,14 @@ import {
} from '@nestjs/graphql'
import {
ContestProblem,
Image,
Problem,
ProblemTag,
ProblemTestcase,
WorkbookProblem
} from '@generated'
import { Prisma } from '@prisma/client'
import { PrismaClientKnownRequestError } from '@prisma/client/runtime/library'
import { AuthenticatedRequest } from '@libs/auth'
import { OPEN_SPACE_ID } from '@libs/constants'
import {
Expand All @@ -33,6 +35,7 @@ import {
UnprocessableDataException
} from '@libs/exception'
import { CursorValidationPipe, GroupIDPipe, RequiredIntPipe } from '@libs/pipe'
import { ImageSource } from './model/image.output'
import {
CreateProblemInput,
UploadFileInput,
Expand Down Expand Up @@ -105,6 +108,43 @@ export class ProblemResolver {
}
}

@Mutation(() => ImageSource)
async uploadImage(
@Args('input') input: UploadFileInput,
@Context('req') req: AuthenticatedRequest
) {
try {
return await this.problemService.uploadImage(input, req.user.id)
} catch (error) {
if (error instanceof UnprocessableDataException) {
throw error.convert2HTTPException()
}
this.logger.error(error)
throw new InternalServerErrorException()
}
}

@Mutation(() => Image)
async deleteImage(
@Args('filename') filename: string,
@Context('req') req: AuthenticatedRequest
) {
try {
return await this.problemService.deleteImage(filename, req.user.id)
} catch (error) {
if (error instanceof UnprocessableDataException) {
throw error.convert2HTTPException()
} else if (
error instanceof PrismaClientKnownRequestError &&
error.code == 'P2025'
) {
throw new NotFoundException(error.message)
}
this.logger.error(error)
throw new InternalServerErrorException()
}
}

@Query(() => [Problem])
async getProblems(
@Args(
Expand Down
3 changes: 2 additions & 1 deletion apps/backend/apps/admin/src/problem/problem.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
UnprocessableDataException
} from '@libs/exception'
import { PrismaService } from '@libs/prisma'
import { S3Provider } from '@admin/storage/s3.provider'
import { S3MediaProvider, S3Provider } from '@admin/storage/s3.provider'
import { StorageService } from '@admin/storage/storage.service'
import {
exampleContest,
Expand Down Expand Up @@ -96,6 +96,7 @@ describe('ProblemService', () => {
StorageService,
ConfigService,
S3Provider,
S3MediaProvider,
{ provide: CACHE_MANAGER, useValue: { del: () => null } }
]
}).compile()
Expand Down
Loading

0 comments on commit 2e23941

Please sign in to comment.