Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,22 +28,25 @@
"preinstall": "npx only-allow pnpm"
},
"dependencies": {
"@apollo/server": "^4.11.2",
"@aws-sdk/client-dynamodb": "^3.540.0",
"@aws-sdk/client-ses": "^3.549.0",
"@aws-sdk/lib-dynamodb": "^3.549.0",
"@nestjs/apollo": "^12.2.1",
"@nestjs/common": "^10.0.0",
"@nestjs/config": "^3.1.1",
"@nestjs/core": "^10.0.0",
"@nestjs/graphql": "^12.2.1",
"@nestjs/jwt": "^10.2.0",
"@nestjs/passport": "^10.0.3",
"@nestjs/platform-express": "^10.0.0",
"@nestjs/swagger": "^7.3.0",
"@supabase/supabase-js": "^2.45.3",
"bcrypt": "^5.1.1",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.1",
"commander": "^12.1.0",
"cookie-parser": "^1.4.6",
"graphql": "^16.9.0",
"mysql2": "^3.9.1",
"passport": "^0.7.0",
"passport-google-oauth20": "^2.0.0",
Expand All @@ -52,7 +55,6 @@
"pm2": "^5.3.1",
"reflect-metadata": "^0.1.14",
"rxjs": "^7.8.1",
"swagger-ui-express": "^5.0.0",
"typeorm": "^0.3.20",
"uuid": "^9.0.1"
},
Expand Down
813 changes: 758 additions & 55 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

19 changes: 18 additions & 1 deletion src/app.module.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,29 @@
import { Module } from '@nestjs/common';
import { GraphQLModule } from '@nestjs/graphql';
import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo';
import { ConfigModule } from '@nestjs/config';
import { HealthCheckController } from './modules/health-check/health-check.controller';
import { DynamoDBModule } from './database/dynamodb/dynamodb.module';
import { UserModule } from './modules/user/user.module';
import { AuthModule } from './modules/auth/auth.module';
import { TestModule } from './modules/test/test.module';
import { join } from 'path';

@Module({
imports: [DynamoDBModule, UserModule, AuthModule, TestModule],
imports: [
ConfigModule.forRoot({
isGlobal: true,
}),
GraphQLModule.forRoot<ApolloDriverConfig>({
driver: ApolloDriver,
autoSchemaFile: join(process.cwd(), 'src/schema.gql'),
sortSchema: true,
}),
DynamoDBModule,
UserModule,
AuthModule,
TestModule
],
controllers: [HealthCheckController],
providers: [],
})
Expand Down
8 changes: 0 additions & 8 deletions src/core/errors/unauthorized-error.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,5 @@
import { ApiProperty } from '@nestjs/swagger';

export class UnauthorizedError {
@ApiProperty({
example: ['No token', 'Invalid token', 'Token expired'],
})
message: string[];

@ApiProperty({
example: 'Unauthorized',
})
error: 'Unauthorized';
}
47 changes: 5 additions & 42 deletions src/main.ts
Original file line number Diff line number Diff line change
@@ -1,53 +1,16 @@
import 'dotenv/config';
import { NestFactory, Reflector } from '@nestjs/core';
import { ValidationPipe } from '@nestjs/common';
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { ClassSerializerInterceptor, ValidationPipe } from '@nestjs/common';
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
import * as cookieParser from 'cookie-parser';
import { BaseExceptionFilter } from 'src/core/filters/base-exception-filter';
import { writeFileSync } from 'fs';
import { join } from 'path';

async function bootstrap() {
const app = await NestFactory.create(AppModule);
// set CORS
app.useGlobalPipes(new ValidationPipe());
app.enableCors({
origin: true,
origin: process.env.CORS_ORIGIN,
credentials: true,
exposedHeaders: ['Authorization'],
});
// set swagger module
const config = new DocumentBuilder()
.setTitle('Meta Test API')
.setDescription('The Meta Test API')
.setVersion('1.0.0')
.addBearerAuth()
.build();
const document = SwaggerModule.createDocument(app, config);
const swaggerJson = JSON.stringify(document, null, 2);
writeFileSync(join(process.cwd(), 'swagger-spec.json'), swaggerJson);
SwaggerModule.setup('api', app, document);

app.useGlobalPipes(
new ValidationPipe({
transform: true,
}),
);

app.useGlobalFilters(new BaseExceptionFilter());

app.useGlobalInterceptors(new ClassSerializerInterceptor(app.get(Reflector)));
app.use(cookieParser());
// set port
const port =
process.env.NODE_ENV === 'dev'
? 8000
: process.env.NODE_ENV === 'production'
? 8080
: 3000;

console.log('server start at', port);

const port = process.env.PORT || 3000;
await app.listen(port);
}
bootstrap();
185 changes: 2 additions & 183 deletions src/modules/auth/auth.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,66 +11,28 @@ import {
Res,
UseGuards,
} from '@nestjs/common';
import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger';
import { Response } from 'express';
import { KakaoAuthGuard } from 'src/auth/guard/kakao.auth.guard';
import { AuthService } from './auth.service';
import { CreateTokenRequestDto } from './dto/create-token-request.dto';
import { CreateTokenResponseDto } from './dto/create-token-response.dto';
import { RefreshTokenRequestDto } from './dto/refresh-token-request.dto';
import { RefreshTokenResponseDto } from './dto/refresh-token-response.dto';
import { SocialLoginRequestDto } from './dto/social-login-request.dto';
import { GoogleAuthGuard } from 'src/auth/guard/google.auth.guard';
import { ResetPasswordRequestDto } from './dto/reset-password-request.dto';
import { EmailVerificationRequestDto } from './dto/email-verification-request.dto';
import { UpdateEmailVerificationRequestDto } from './dto/update-email-verification-request.dto';
import { UserType } from 'src/types/userType';
import { CreateTokenRequestBodyError } from './error/create-token-error';
import { RefreshTokenRequestBodyError } from './error/refresh-token-error';
import { ResetPasswordRequestBodyError } from './error/reset-password-error';
import { EmailVerificationRequestBodyError } from './error/email-verification-error';
import { UpdateEmailVerificationRequestBodyError } from './error/update-email-verification-error';
import { EmailVerificationResponseDto } from './dto/email-verification-response.dto';

@ApiTags('auth')
@Controller({ path: 'auth' })
export class AuthController {
constructor(private readonly authService: AuthService) {}

@Post('token')
@ApiOperation({ summary: '로그인', description: '토큰 발급' })
@ApiResponse({
status: 201,
description: 'Created',
type: CreateTokenResponseDto,
})
@ApiResponse({
status: 400,
description: 'Bad request',
type: CreateTokenRequestBodyError,
})
@ApiResponse({
status: 401,
description: 'Unauthorized',
content: {
'application/json': {
example: {
message: 'User not found or password does not match',
error: 'Unauthorized',
},
},
},
})
@ApiResponse({
status: 500,
description: 'Internal Server Error',
})
@HttpCode(HttpStatus.CREATED)
async createToken(
@Body() createTokenInfoDto: CreateTokenRequestDto,
@Res() res: Response,
) {
//NOTE: 이 API가 호출되는 유저는 일반 유저이므로 userType = NORMAL
const tokens = await this.authService.create(
createTokenInfoDto,
UserType.NORMAL,
Expand All @@ -86,36 +48,6 @@ export class AuthController {
}

@Post('token/refresh')
@ApiOperation({
summary: '토큰 재발급',
description: '토큰 재발급',
})
@ApiResponse({
status: 201,
description: 'Created',
type: RefreshTokenResponseDto,
})
@ApiResponse({
status: 400,
description: 'Bad request',
type: RefreshTokenRequestBodyError,
})
@ApiResponse({
status: 401,
description: 'Unauthorized',
content: {
'application/json': {
example: {
message: 'Invalid token',
error: 'Unauthorized',
},
},
},
})
@ApiResponse({
status: 500,
description: 'Internal Server Error',
})
@HttpCode(HttpStatus.CREATED)
async refreshToken(
@Body() refreshTokenInfoDto: RefreshTokenRequestDto,
Expand All @@ -132,14 +64,6 @@ export class AuthController {
res.status(HttpStatus.CREATED).send();
}

@ApiOperation({
summary: '카카오 소셜로그인',
description: '기가입 유저는 로그인, 신규 유저는 회원가입 진행',
})
@ApiResponse({
status: 201,
description: 'Created',
})
@UseGuards(KakaoAuthGuard)
@Get('login/kakao')
async loginWithKakao(
Expand All @@ -158,14 +82,6 @@ export class AuthController {
res.status(HttpStatus.CREATED).send();
}

@ApiOperation({
summary: '구글 소셜로그인',
description: '기가입 유저는 로그인, 신규 유저는 회원가입 진행',
})
@ApiResponse({
status: 201,
description: 'Created',
})
@UseGuards(GoogleAuthGuard)
@Get('login/google')
async loginWithGoogle(
Expand All @@ -185,61 +101,13 @@ export class AuthController {
}

@Patch('password')
@ApiOperation({
summary: '비밀번호 초기화',
description: '비밀번호 변경하고 이메일로 전송합니다.',
})
@ApiResponse({
status: 200,
description: 'OK',
})
@ApiResponse({
status: 400,
description: 'Bad request',
type: ResetPasswordRequestBodyError,
})
@ApiResponse({
status: 404,
description: 'Not Found',
content: {
'application/json': {
example: {
message: 'User does not exist',
error: 'Not Found',
},
},
},
})
@ApiResponse({
status: 500,
description: 'Internal Server Error',
})
@HttpCode(HttpStatus.OK)
async updateUserPassword(@Body() body: ResetPasswordRequestDto) {
const email = body.email;
//NOTE: reset password는 일반유저만 가능(소셜로그인 유저 X)
return await this.authService.resetUserPassword(email, UserType.NORMAL);
}

@Post('email-verification')
@ApiOperation({
summary: '이메일 인증 요청',
description: '이메일 인증 요청 메일을 발송',
})
@ApiResponse({
status: 201,
description: 'Created',
type: EmailVerificationResponseDto,
})
@ApiResponse({
status: 400,
description: 'Bad request',
type: EmailVerificationRequestBodyError,
})
@ApiResponse({
status: 500,
description: 'Internal Server Error',
})
@HttpCode(HttpStatus.CREATED)
async createEmailVerificaiton(
@Body() body: EmailVerificationRequestDto,
Expand All @@ -251,62 +119,13 @@ export class AuthController {
}

@Patch('email-verification')
@ApiOperation({
summary: '이메일 인증 검증',
description: '임시토큰으로 이메일 인증을 검증',
})
@ApiResponse({
status: 200,
description: 'OK',
})
@ApiResponse({
status: 400,
description: 'Bad request',
type: UpdateEmailVerificationRequestBodyError,
})
@ApiResponse({
status: 401,
description: 'Unauthorized',
content: {
'application/json': {
examples: {
UpdateEmailVerificationUnauthorizedError: {
value: {
message: 'Invalid request_id',
error: 'Unauthorized',
},
description: 'request_id가 유효하지 않음',
},
UpdateEmailVerificationUnauthorizedError2: {
value: {
message: 'Invalid code',
error: 'Unauthorized',
},
description: 'code가 유효하지 않음',
},
UpdateEmailVerificationUnauthorizedError3: {
value: {
message: 'Expired request_id',
error: 'Unauthorized',
},
description: 'request_id가 만료됨',
},
},
},
},
})
@ApiResponse({
status: 500,
description: 'Internal Server Error',
})
@HttpCode(HttpStatus.OK)
async updateEmailVerificaiton(
@Body() body: UpdateEmailVerificationRequestDto,
@Res({ passthrough: true }) res: Response,
) {
const id = body.request_id;
const code = body.code;
await this.authService.updateEmailVerificaiton(id, code);
const { request_id, code } = body;
await this.authService.updateEmailVerificaiton(request_id, code);
res.status(HttpStatus.OK).send();
}
}
Loading