Skip to content

Commit

Permalink
Merge branch 'main' into 1471-change-contestConfig-nullability
Browse files Browse the repository at this point in the history
  • Loading branch information
Lee-won-hyeok committed Feb 29, 2024
2 parents 652bdea + abf6e02 commit 3f945f2
Show file tree
Hide file tree
Showing 107 changed files with 3,524 additions and 2,394 deletions.
3 changes: 2 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ jobs:
- name: Load Next.js environment
if: ${{ matrix.target == 'frontend-client' }}
run: |
echo "NEXT_PUBLIC_BASEURL=https://dev.codedang.com/api" >> frontend-client/.env
echo "NEXT_PUBLIC_BASEURL=https://dev-aws.codedang.com/api" >> frontend-client/.env
echo "NEXT_PUBLIC_GQL_BASEURL=https://dev-aws.codedang.com/graphql" >> frontend-client/.env
echo "NEXT_URL=https://dev.codedang.com" >> frontend-client/.env
# If target is backend-admin, run `pnpm --filter backend build admin`
Expand Down
60 changes: 40 additions & 20 deletions Caddyfile
Original file line number Diff line number Diff line change
@@ -1,23 +1,36 @@
(cors) {
@cors_preflight{args[0]} {
# cors_preflight{args[0]} matcher 생성
header Origin {args[0]} # header와 method를 조건으로 설정
# 1. Preflight request
# Matcher: If Origin header equals to the argument and method is OPTIONS
# Handler: Set CORS headers and respond with 204
@cors-preflight{args[0]} {
header Origin {args[0]}
method OPTIONS
}
@cors{args[0]} header Origin {args[0]} # cors{args[0]} matcher 생성

handle @cors_preflight{args[0]} {
# cors_preflight{args[0]}를 처리할 handler
header Access-Control-Allow-Credentials true
header Access-Control-Allow-Origin "{args[0]}"
header Access-Control-Allow-Methods "GET, POST, PUT, PATCH, DELETE"
header Access-Control-Allow-Headers "Content-Type, Authorization, Email-Auth"
handle @cors-preflight{args[0]} {
header {
Access-Control-Allow-Credentials true
Access-Control-Allow-Origin "{header.Origin}"
Access-Control-Allow-Methods "GET, POST, PUT, PATCH, DELETE"
Access-Control-Allow-Headers "Content-Type, Authorization, Email-Auth, Apollo-Require-Preflight"
Access-Control-Expose-Headers "Email-Auth"
}
respond "" 204
}


# 2. Actual request
# Matcher: If Origin header equals to the argument
# Handler: Set CORS headers
@cors{args[0]} header Origin {args[0]}
handle @cors{args[0]} {
# cors{args[0]}를 처리할 handler
header Access-Control-Allow-Origin "{args[0]}"
header {
Access-Control-Allow-Credentials true
Access-Control-Allow-Origin "{header.Origin}"
Access-Control-Allow-Methods "GET, POST, PUT, PATCH, DELETE"
Access-Control-Allow-Headers "Content-Type, Authorization, Email-Auth, Apollo-Require-Preflight"
Access-Control-Expose-Headers "Email-Auth"
}
}
}

Expand All @@ -37,27 +50,34 @@ dev.codedang.com {
handle /api/* {
reverse_proxy 127.0.0.1:4000

# CORS rules
# frontend server (Next.js)
import cors http://localhost:5525

# Use wildcard matcher to allow all subdomains
# https://caddyserver.com/docs/caddyfile/matchers#header
import cors *.codedang.com
import cors *.gitpod.io
import cors *.vercel.app

# 캐시 설정
header {
Set-Cookie (.*) "$1; SameSite=None; Secure"
defer
}

# cors 허용할 도메인
import cors http://localhost:5173 # frontend server (Vue)
import cors http://localhost:5525 # frontend server (Next.js)
}

handle /logs/* {
handle /logs* {
reverse_proxy 127.0.0.1:9999
}

handle /graphql {
reverse_proxy 127.0.0.1:3000

# cors 허용할 도메인
import cors http://localhost:5173 # frontend server (Vue)
import cors http://localhost:5525 # frontend server (Next.js)
import cors http://localhost:5525
import cors *.codedang.com
import cors *.gitpod.io
import cors *.vercel.app
}

handle {
Expand Down
5 changes: 3 additions & 2 deletions backend/apps/admin/src/admin.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
import { CacheConfigService } from '@libs/cache'
import { AdminExceptionFilter } from '@libs/exception'
import { apolloErrorFormatter } from '@libs/exception'
import { pinoLoggerModuleOption } from '@libs/logger'
import { LoggingPlugin, pinoLoggerModuleOption } from '@libs/logger'
import { PrismaModule } from '@libs/prisma'
import { NoticeModule } from '@admin/notice/notice.module'
import { AdminController } from './admin.controller'
Expand Down Expand Up @@ -62,7 +62,8 @@ import { UserModule } from './user/user.module'
AdminService,
{ provide: APP_GUARD, useClass: JwtAuthGuard },
{ provide: APP_GUARD, useClass: GroupLeaderGuard },
{ provide: APP_FILTER, useClass: AdminExceptionFilter }
{ provide: APP_FILTER, useClass: AdminExceptionFilter },
LoggingPlugin
]
})
export class AdminModule implements OnApplicationBootstrap {
Expand Down
4 changes: 3 additions & 1 deletion backend/apps/admin/src/problem/problem.service.spec.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { CACHE_MANAGER } from '@nestjs/cache-manager'
import { ConfigService } from '@nestjs/config'
import { Test, type TestingModule } from '@nestjs/testing'
import { Level } from '@generated'
Expand Down Expand Up @@ -87,7 +88,8 @@ describe('ProblemService', () => {
{ provide: PrismaService, useValue: db },
StorageService,
ConfigService,
S3Provider
S3Provider,
{ provide: CACHE_MANAGER, useValue: { del: () => null } }
]
}).compile()

Expand Down
20 changes: 13 additions & 7 deletions backend/apps/admin/src/problem/problem.service.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { Injectable } from '@nestjs/common'
import { CACHE_MANAGER } from '@nestjs/cache-manager'
import { Cache } from '@nestjs/cache-manager'
import { Inject, Injectable } from '@nestjs/common'
import { Language } from '@generated'
import type { ContestProblem, Tag, WorkbookProblem } from '@generated'
import { Level } from '@generated'
Expand Down Expand Up @@ -33,7 +35,8 @@ type TestCaseInFile = {
export class ProblemService {
constructor(
private readonly prisma: PrismaService,
private readonly storageService: StorageService
private readonly storageService: StorageService,
@Inject(CACHE_MANAGER) private readonly cacheManager: Cache
) {}

async createProblem(
Expand Down Expand Up @@ -385,11 +388,14 @@ export class ProblemService {
}

async updateTestcases(problemId: number, testcases: Array<Testcase>) {
await this.prisma.problemTestcase.deleteMany({
where: {
problemId
}
})
await Promise.all([
this.prisma.problemTestcase.deleteMany({
where: {
problemId
}
}),
this.cacheManager.del(`${problemId}`)
])

const filename = `${problemId}.json`
const toBeUploaded: Array<TestCaseInFile> = []
Expand Down
6 changes: 5 additions & 1 deletion backend/apps/client/src/auth/auth.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,12 @@ export class AuthController {
@Req() req: AuthenticatedRequest,
@Res({ passthrough: true }) res: Response
) {
const refreshToken = req.cookies['refresh_token']
// FIX ME: refreshToken이 없을 때 에러를 던지는 것이 맞는지 확인
// 일단은 refreshToken이 없을 때는 무시하도록 함
if (!refreshToken) return
try {
await this.authService.deleteRefreshToken(req.user.id)
await this.authService.deleteRefreshToken(req.user.id, refreshToken)
res.clearCookie('refresh_token', REFRESH_TOKEN_COOKIE_OPTIONS)
} catch (error) {
this.logger.error(error)
Expand Down
2 changes: 1 addition & 1 deletion backend/apps/client/src/auth/auth.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ describe('AuthService', () => {
describe('isValidRefreshToken', () => {
it("should return true when the given refresh token match with the user's cached refresh token", async () => {
//given
stub(cache, 'get').resolves(REFRESH_TOKEN)
stub(cache, 'get').resolves(1)

//when
const result = await service.isValidRefreshToken(REFRESH_TOKEN, user.id)
Expand Down
15 changes: 9 additions & 6 deletions backend/apps/client/src/auth/auth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,9 @@ export class AuthService {

async isValidRefreshToken(refreshToken: string, userId: number) {
const cachedRefreshToken = await this.cacheManager.get(
refreshTokenCacheKey(userId)
refreshTokenCacheKey(userId, refreshToken)
)
if (cachedRefreshToken !== refreshToken) {
if (cachedRefreshToken !== 1) {
return false
}
return true
Expand All @@ -92,17 +92,20 @@ export class AuthService {
expiresIn: REFRESH_TOKEN_EXPIRE_TIME
})

// userId: refreshToken을 key로 cache에 저장
await this.cacheManager.set(
refreshTokenCacheKey(userId),
refreshToken,
refreshTokenCacheKey(userId, refreshToken),
1,
REFRESH_TOKEN_EXPIRE_TIME * 1000 // milliseconds
)

return { accessToken, refreshToken }
}

async deleteRefreshToken(userId: number) {
return await this.cacheManager.del(refreshTokenCacheKey(userId))
async deleteRefreshToken(userId: number, refreshToken: string) {
return await this.cacheManager.del(
refreshTokenCacheKey(userId, refreshToken)
)
}

async githubLogin(res: Response, githubUser: GithubUser) {
Expand Down
3 changes: 2 additions & 1 deletion backend/apps/client/src/contest/contest.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,7 @@ export class ContestService {
}
throw error
}
/* HACK: standings 업데이트 로직 수정 후 삭제
// get contest participants ranking using ContestRecord
const sortedContestRecordsWithUserDetail =
await this.prisma.contestRecord.findMany({
Expand Down Expand Up @@ -333,10 +334,10 @@ export class ContestService {
standing: index + 1
})
)
*/
// combine contest and sortedContestRecordsWithUserDetail
return {
...contest,
standings: UsersWithStandingDetail,
canRegister
}
}
Expand Down
7 changes: 7 additions & 0 deletions backend/apps/client/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,13 @@ const bootstrap = async () => {
app.useGlobalPipes(new ValidationPipe({ whitelist: true }))
app.use(cookieParser())
if (process.env.NODE_ENV !== 'production') {
app.enableCors({
origin: 'http://localhost:5525',
credentials: true,
methods: 'GET,HEAD,PUT,PATCH,POST,DELETE,OPTIONS',
allowedHeaders: ['*'],
exposedHeaders: ['Content-Type', 'Authorization', 'Email-Auth']
})
const config = new DocumentBuilder()
.setTitle('SKKU coding platform')
.setDescription('API description')
Expand Down
26 changes: 24 additions & 2 deletions backend/apps/client/src/problem/problem.controller.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import { Test, type TestingModule } from '@nestjs/testing'
import { expect } from 'chai'
import { RolesService } from '@libs/auth'
import { ProblemController } from './problem.controller'
import {
ProblemController,
ContestProblemController
} from './problem.controller'
import {
ContestProblemService,
ProblemService,
Expand All @@ -16,7 +19,6 @@ describe('ProblemController', () => {
controllers: [ProblemController],
providers: [
{ provide: ProblemService, useValue: {} },
{ provide: ContestProblemService, useValue: {} },
{ provide: WorkbookProblemService, useValue: {} },
{ provide: RolesService, useValue: {} }
]
Expand All @@ -29,3 +31,23 @@ describe('ProblemController', () => {
expect(controller).to.be.ok
})
})

describe('ContestProblemController', () => {
let controller: ContestProblemController

beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [ContestProblemController],
providers: [
{ provide: ContestProblemService, useValue: {} },
{ provide: RolesService, useValue: {} }
]
}).compile()

controller = module.get<ContestProblemController>(ContestProblemController)
})

it('should be defined', () => {
expect(controller).to.be.ok
})
})
Loading

0 comments on commit 3f945f2

Please sign in to comment.