Skip to content

Commit

Permalink
merge :: 회원가입, 로그인 API 구현 (#10)
Browse files Browse the repository at this point in the history
* add :: local signup api

* add :: local login  api

* modify :: add @UseFilters
  • Loading branch information
jyk1029 authored Feb 21, 2024
1 parent 1e15ede commit 3a3a6a9
Show file tree
Hide file tree
Showing 7 changed files with 114 additions and 3 deletions.
39 changes: 39 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"@nestjs/platform-express": "^10.0.0",
"@nestjs/typeorm": "^10.0.2",
"bcrypt": "^5.1.1",
"class-validator": "^0.14.1",
"mysql2": "^3.9.1",
"passport": "^0.7.0",
"passport-google-oauth20": "^2.0.0",
Expand All @@ -43,6 +44,7 @@
"@nestjs/cli": "^10.0.0",
"@nestjs/schematics": "^10.0.0",
"@nestjs/testing": "^10.0.0",
"@types/bcrypt": "^5.0.2",
"@types/express": "^4.17.17",
"@types/jest": "^29.5.2",
"@types/node": "^20.3.1",
Expand Down
15 changes: 14 additions & 1 deletion src/application/domain/auth/controller/auth.controller.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import { Controller, Get, Req, UseGuards } from "@nestjs/common";
import { Body, Controller, Get, Post, Req, UseFilters, UseGuards } from "@nestjs/common";
import { AuthGuard } from "@nestjs/passport";
import { AuthService } from "../services/auth.service";
import { LoginRequest, SignUpRequest } from "../dto/auth.dto";
import { GlobalExceptionFilter } from "../../../../infrastructure/global/filter/global.exception.filter";

@UseFilters(GlobalExceptionFilter)
@Controller('auth')
export class AuthController {
constructor(
Expand Down Expand Up @@ -41,4 +44,14 @@ export class AuthController {
async naverCallback(@Req() req) {
return this.authServie.naverLogin(req)
}

@Post('signup')
async localSignup(@Body() request: SignUpRequest) {
return this.authServie.localSignup(request)
}

@Post('login')
async localLogin(@Body() request: LoginRequest) {
return this.authServie.localLogin(request)
}
}
27 changes: 27 additions & 0 deletions src/application/domain/auth/dto/auth.dto.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,30 @@
import { IsNotEmpty, IsNumber, IsString, Matches } from "class-validator";

export class TokenResponse {
accessToken: string;
}

export class SignUpRequest {
@Matches(/^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}$/, {
message: '유효한 이메일 주소를 입력해주세요.',
})
email: string;

@Matches(/^(?=.*[a-z])(?=.*[0-9])(?=.*[!@#$%^&*])[a-zA-Z0-9!@#$%^&*]{5,}$/, {
message: '소문자, 숫자, 특수문자를 포함하여 5자 이상으로 입력해주세요.'
})
password: string;
@IsNotEmpty()
@IsString()
nickname: string;
@IsNotEmpty()
@IsNumber()
age: number;
}

export class LoginRequest {
@IsNotEmpty()
email: string;
@IsNotEmpty()
password: string;
}
32 changes: 31 additions & 1 deletion src/application/domain/auth/services/auth.service.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { Injectable } from "@nestjs/common";
import { HttpException, HttpStatus, Injectable } from "@nestjs/common";
import { Repository } from "typeorm";
import { UserEntity } from "../../../../infrastructure/domain/user/persistence/user.entity";
import { InjectRepository } from "@nestjs/typeorm";
import { JwtAdapter } from "../../../../infrastructure/global/jwt/jwt.adapter";
import { GoogleStrategy } from "../../../../infrastructure/global/oauth/google.strategy";
import { ConfigService } from "@nestjs/config";
import { LoginRequest, SignUpRequest } from "../dto/auth.dto";
import * as bcrypt from 'bcrypt';

@Injectable()
export class AuthService {
Expand Down Expand Up @@ -73,4 +75,32 @@ export class AuthService {

return this.jwtAdapter.receiveToken(user.email)
}

async localSignup(request: SignUpRequest) {
let { email, password, nickname, age } = request
if (await this.userRepository.existsBy({ email })) {
throw new HttpException('Already Emails Exist', HttpStatus.CONFLICT)
}
const hashPassword = bcrypt.hashSync(password, 10)
await this.userRepository.save({ email, password: hashPassword, nickname, age, profile: "", provider: 'LOCAL' })
}

async localLogin(request: LoginRequest) {
let email = request.email
let user = await this.userRepository.findOneBy({ email })
this.validateUser(request, user)
return await this.jwtAdapter.receiveToken(user.email)
}

private validateUser(request: LoginRequest, user: UserEntity) {
if (!user) {
throw new HttpException('User Not Found', HttpStatus.NOT_FOUND)
}
if (user.provider !== 'LOCAL') {
throw new HttpException(user.provider + ' login', HttpStatus.BAD_REQUEST)
}
if (!bcrypt.compareSync(request.password, user.password)) {
throw new HttpException('Password Mismatched', HttpStatus.BAD_REQUEST)
}
}
}
1 change: 0 additions & 1 deletion src/infrastructure/domain/user/persistence/user.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';

@Entity('tbl_user')
export class UserEntity {

@PrimaryGeneratedColumn('uuid')
id: string;

Expand Down
1 change: 1 addition & 0 deletions src/infrastructure/global/oauth/kakao.strategy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export class KakaoStrategy extends PassportStrategy(Strategy, 'kakao') {
clientID: config.get('KAKAO_CLIENT_ID'),
clientSecret: config.get('KAKAO_CLIENT_SECRET'),
callbackURL: config.get('KAKAO_REDIRECT_URL'),
scope: [ 'account_email', 'profile' ]
});
}

Expand Down

0 comments on commit 3a3a6a9

Please sign in to comment.