Skip to content

Commit

Permalink
⚡ feat: 에러 핸들링 로직 향상 (#4) (#5)
Browse files Browse the repository at this point in the history
* feat: APIException DTO 추가

* style: HttpStatus ENUM 스타일 . 으로 변경, APIException..

Co-authored-by: lavi27 <[email protected]>
  • Loading branch information
kms0219kms and lavi27 authored Jan 8, 2024
1 parent f8b3559 commit 57d5ec0
Show file tree
Hide file tree
Showing 16 changed files with 133 additions and 186 deletions.
52 changes: 22 additions & 30 deletions src/common/dto/APIError.dto.ts
Original file line number Diff line number Diff line change
@@ -1,45 +1,37 @@
import { HttpStatus } from '@nestjs/common';

export class APIError {
/**
* 에러 코드 (ENUM)
* @example 'INTERNAL_SERVER_ERROR'
*/
private code: string = APIErrorCodes['500'];
constructor(status: HttpStatus, message: string, data?: any) {
this._status = status;
this._message = message;
this._data = data;
}

/**
* HTTP 상태 코드
* @example 500
* 에러 ENUM (ENUM Type)
* @example HttpStatus.INTERNAL_SERVER_ERROR
*/
private status: number = 500;
private _status: HttpStatus = HttpStatus.INTERNAL_SERVER_ERROR;

/**
* 에러 메시지
* @example '내부 서버 오류가 발생했습니다.'
*/
private message: string = '내부 서버 오류가 발생했습니다.';
private _message: string = '내부 서버 오류가 발생했습니다.';

private data?: any;
private _data?: any;

constructor(code, status, message, data?) {
this.code = code;
this.status = status;
this.message = message;
this.data = data;
public get status() {
return this._status;
}
}

export const APIErrorCodes = {
'400': 'BAD_REQUEST',
'401': 'UNAUTHORIZED',
'403': 'FORBIDDEN',
'404': 'NOT_FOUND',
'405': 'METHOD_NOT_ALLOWED',
'409': 'CONFLICT',
'410': 'GONE',
'422': 'UNPROCESSABLE_CONTENT',
'429': 'TOO_MANY_REQUESTS',
'500': 'INTERNAL_SERVER_ERROR',
'501': 'NOT_IMPLEMENTED',
'503': 'SERVICE_UNAVAILABLE',
};
public get message() {
return this._message;
}

public get data() {
return this._data;
}
}

export default APIError;
25 changes: 25 additions & 0 deletions src/common/dto/APIException.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { HttpStatus } from '@nestjs/common';
import APIError from './APIError.dto';

export default class APIException {
constructor(status: HttpStatus, message: string, data?: any) {
this._APIError = new APIError(status, message, data);
this._status = status;
}

/**
* 에러 ENUM (ENUM Type)
* @example HttpStatus.INTERNAL_SERVER_ERROR
*/
private _status: HttpStatus = HttpStatus.INTERNAL_SERVER_ERROR;

private _APIError: APIError;

public get status() {
return this._status;
}

public get APIError() {
return this._APIError;
}
}
4 changes: 2 additions & 2 deletions src/dashboard/auth/auth.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export class AuthController {
try {
return {
code: 'OPERATION_COMPLETE',
status: HttpStatus['OK'],
status: HttpStatus.OK,
data: await this.authService.callback(body.code),
};
} catch (e) {
Expand All @@ -76,7 +76,7 @@ export class AuthController {
getProfile(@Request() req): discordUserResponseDto {
return {
code: 'OPERATION_COMPLETE',
status: HttpStatus['OK'],
status: HttpStatus.OK,
data: req.user,
};
}
Expand Down
34 changes: 9 additions & 25 deletions src/dashboard/auth/auth.guard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { HttpService } from '@nestjs/axios';
import {
CanActivate,
ExecutionContext,
HttpException,
HttpStatus,
Inject,
Injectable,
Expand All @@ -17,7 +16,7 @@ import { AxiosError } from 'axios';
import { catchError, firstValueFrom } from 'rxjs';

import { type APIUser } from 'discord-api-types/v10';
import { APIError, APIErrorCodes } from 'src/common/dto/APIError.dto';
import APIException from 'src/common/dto/APIException.dto';

@Injectable()
export class AuthGuard implements CanActivate {
Expand All @@ -32,14 +31,7 @@ export class AuthGuard implements CanActivate {
const request = context.switchToHttp().getRequest();
const token = this.extractTokenFromHeader(request);
if (!token) {
throw new HttpException(
new APIError(
APIErrorCodes['401'],
HttpStatus['UNAUTHORIZED'],
'로그인이 필요합니다.',
),
HttpStatus['UNAUTHORIZED'],
);
throw new APIException(HttpStatus.UNAUTHORIZED, '로그인이 필요합니다.');
}

try {
Expand Down Expand Up @@ -76,24 +68,16 @@ export class AuthGuard implements CanActivate {
request['user'] = data;
} catch (e) {
if (e instanceof AxiosError && e.response?.status === 429) {
throw new HttpException(
new APIError(
APIErrorCodes['429'],
HttpStatus['TOO_MANY_REQUESTS'],
'Discord API 요청이 지연되고 있습니다. 잠시 후 다시 시도해주세요.',
e.response?.data,
),
HttpStatus['TOO_MANY_REQUESTS'],
throw new APIException(
HttpStatus.TOO_MANY_REQUESTS,
'Discord API 요청이 지연되고 있습니다. 잠시 후 다시 시도해주세요.',
e.response?.data,
);
}

throw new HttpException(
new APIError(
APIErrorCodes['401'],
HttpStatus['UNAUTHORIZED'],
'로그인 토큰이 만료되었거나 올바르지 않은 접근입니다.',
),
HttpStatus['UNAUTHORIZED'],
throw new APIException(
HttpStatus.UNAUTHORIZED,
'로그인 토큰이 만료되었거나 올바르지 않은 접근입니다.',
);
}

Expand Down
21 changes: 8 additions & 13 deletions src/dashboard/auth/auth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { catchError, firstValueFrom } from 'rxjs';
import { AxiosError } from 'axios';

import { authCallbackDto } from './dto/authCallback.dto';
import { APIError, APIErrorCodes } from 'src/common/dto/APIError.dto';
import { APIError } from 'src/common/dto/APIError.dto';
import { DiscordOauthAPIError } from 'src/common/dto/DiscordOauthAPIError.dto';

import {
Expand Down Expand Up @@ -54,16 +54,15 @@ export class AuthService {

if (err.response?.status === 429) {
throw new APIError(
APIErrorCodes['429'],
HttpStatus['TOO_MANY_REQUESTS'],
HttpStatus.TOO_MANY_REQUESTS,
'Discord API 요청이 지연되고 있습니다. 잠시 후 다시 시도해주세요.',
err.response?.data,
);
}

// FIXME - 이거 그냥 response 있는지 여부에 따라서...
throw new APIError(
APIErrorCodes[String(err.response.status || 500)],
err.response.status || HttpStatus['INTERNAL_SERVER_ERROR'],
err.response.status || HttpStatus.INTERNAL_SERVER_ERROR,
(err.response.data as DiscordOauthAPIError)?.error_description ||
'내부 서버 오류가 발생했습니다.',
);
Expand Down Expand Up @@ -113,16 +112,14 @@ export class AuthService {

if (err.response?.status === 429) {
throw new APIError(
APIErrorCodes['429'],
HttpStatus['TOO_MANY_REQUESTS'],
HttpStatus.TOO_MANY_REQUESTS,
'Discord API 요청이 지연되고 있습니다. 잠시 후 다시 시도해주세요.',
err.response?.data,
);
}

throw new APIError(
APIErrorCodes[String(err.response.status || 500)],
err.response.status || HttpStatus['INTERNAL_SERVER_ERROR'],
err.response.status || HttpStatus.INTERNAL_SERVER_ERROR,
(err.response.data as RESTError)?.message ||
'내부 서버 오류가 발생했습니다.',
);
Expand Down Expand Up @@ -169,16 +166,14 @@ export class AuthService {

if (err.response?.status === 429) {
throw new APIError(
APIErrorCodes['429'],
HttpStatus['TOO_MANY_REQUESTS'],
HttpStatus.TOO_MANY_REQUESTS,
'Discord API 요청이 지연되고 있습니다. 잠시 후 다시 시도해주세요.',
err.response?.data,
);
}

throw new APIError(
APIErrorCodes[String(err.response.status || 500)],
err.response.status || HttpStatus['INTERNAL_SERVER_ERROR'],
err.response.status || HttpStatus.INTERNAL_SERVER_ERROR,
(err.response.data as RESTError)?.message ||
'내부 서버 오류가 발생했습니다.',
);
Expand Down
24 changes: 7 additions & 17 deletions src/dashboard/invite/invite.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import { inviteConfigUpdateResponseDto } from './dto/inviteConfigUpdateResponse.

import { AuthGuard } from 'src/dashboard/auth/auth.guard';
import { AuthService } from 'src/dashboard/auth/auth.service';
import { APIError, APIErrorCodes } from 'src/common/dto/APIError.dto';
import { APIError } from 'src/common/dto/APIError.dto';

@ApiTags('Dashboard - Server API')
@ApiBearerAuth()
Expand Down Expand Up @@ -62,22 +62,17 @@ export class InviteController {

if (hasPermission) {
if (!id) {
throw new APIError(
APIErrorCodes['400'],
HttpStatus['BAD_REQUEST'],
'서버 ID를 입력해주세요.',
);
throw new APIError(HttpStatus.BAD_REQUEST, '서버 ID를 입력해주세요.');
}

return {
code: 'OPERATION_COMPLETE',
status: HttpStatus['OK'],
status: HttpStatus.OK,
data: await this.inviteService.getCurrentConfig(id, req.user),
};
} else {
throw new APIError(
APIErrorCodes['403'],
HttpStatus['FORBIDDEN'],
HttpStatus.FORBIDDEN,
'해당 서버에 접근할 권한이 없습니다.',
);
}
Expand Down Expand Up @@ -115,11 +110,7 @@ export class InviteController {

if (hasPermission) {
if (!id) {
throw new APIError(
APIErrorCodes['400'],
HttpStatus['BAD_REQUEST'],
'서버 ID를 입력해주세요.',
);
throw new APIError(HttpStatus.BAD_REQUEST, '서버 ID를 입력해주세요.');
}

const isAlreadyUsingService =
Expand All @@ -145,14 +136,13 @@ export class InviteController {

return {
code: 'OPERATION_COMPLETE',
status: HttpStatus['OK'],
status: HttpStatus.OK,
message:
"Successfully updated server's secure invite feature config.",
};
} else {
throw new APIError(
APIErrorCodes['403'],
HttpStatus['FORBIDDEN'],
HttpStatus.FORBIDDEN,
'해당 서버에 접근할 권한이 없습니다.',
);
}
Expand Down
29 changes: 6 additions & 23 deletions src/dashboard/invite/invite.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { catchError, firstValueFrom } from 'rxjs';
import { type APIUser } from 'discord-api-types/v10';

import { inviteConfigDto } from './dto/inviteConfig.dto';
import { APIError, APIErrorCodes } from 'src/common/dto/APIError.dto';
import { APIError } from 'src/common/dto/APIError.dto';

import { ISettings } from 'src/repository/schemas/settings.schema';
import { IEnterprise } from 'src/repository/schemas/enterprise.schema';
Expand Down Expand Up @@ -54,8 +54,7 @@ export class InviteService {
);

throw new APIError(
APIErrorCodes[String(err.response.status || 500)],
err.response.status || HttpStatus['INTERNAL_SERVER_ERROR'],
err.response.status || HttpStatus.INTERNAL_SERVER_ERROR,
(err.response.data as any)?.message ||
'내부 서버 오류가 발생했습니다.',
);
Expand Down Expand Up @@ -109,19 +108,11 @@ export class InviteService {
.equals(link);

if (isLinkAlreadyUsing && isLinkAlreadyUsing.guild !== id) {
throw new APIError(
APIErrorCodes['409'],
HttpStatus['CONFLICT'],
'이미 사용중인 링크입니다.',
);
throw new APIError(HttpStatus.CONFLICT, '이미 사용중인 링크입니다.');
}

if (settings === 0 || settings === 1) {
throw new APIError(
APIErrorCodes['400'],
HttpStatus['BAD_REQUEST'],
'잘못된 설정입니다.',
);
throw new APIError(HttpStatus.BAD_REQUEST, '잘못된 설정입니다.');
}

await this.settingsModel.create({
Expand Down Expand Up @@ -150,19 +141,11 @@ export class InviteService {
.equals(link);

if (isLinkAlreadyUsing && isLinkAlreadyUsing.guild !== id) {
throw new APIError(
APIErrorCodes['409'],
HttpStatus['CONFLICT'],
'이미 사용중인 링크입니다.',
);
throw new APIError(HttpStatus.CONFLICT, '이미 사용중인 링크입니다.');
}

if (settings === 0 || settings === 1) {
throw new APIError(
APIErrorCodes['400'],
HttpStatus['BAD_REQUEST'],
'잘못된 설정입니다.',
);
throw new APIError(HttpStatus.BAD_REQUEST, '잘못된 설정입니다.');
}

await this.settingsModel.updateOne(
Expand Down
Loading

0 comments on commit 57d5ec0

Please sign in to comment.