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
2 changes: 2 additions & 0 deletions src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import S3Module from './providers/storage/s3/s3.module';
import PointLogModule from './modules/pointLog/pointLog.module';
import { LoggerModule } from './common/logger/winston/logger.module';
import TransactionModule from './providers/database/transaction/transaction.module';
import ProfileModule from './modules/profile/profile.module';

@Module({
imports: [
Expand All @@ -39,6 +40,7 @@ import TransactionModule from './providers/database/transaction/transaction.modu
PrismaModule,
AuthModule,
UserModule,
ProfileModule,
NotificationModule,
FollowModule,
PlanModule,
Expand Down
90 changes: 45 additions & 45 deletions src/modules/auth/auth.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,16 @@ import {
ApiOperation,
ApiUnauthorizedResponse
} from '@nestjs/swagger';
import SignupDTO from 'src/modules/user/types/signup.dto';
import LoginDTO from 'src/modules/user/types/login.dto';
import { Cookies } from 'src/common/decorators/cookie.decorator';
import UnauthorizedError from 'src/common/errors/unauthorizedError';
import ErrorMessage from 'src/common/constants/errorMessage.enum';
import { Response } from 'express';
import { AuthGuard } from '@nestjs/passport';
import { User } from 'src/common/decorators/user.decorator';
import { OAuthProperties } from 'src/modules/user/types/user.types';
import { OAuthProperties } from 'src/modules/auth/types/auth.types';
import { OAuthProvider } from 'src/common/constants/oauth.type';
import SignupDTO from './types/signup.dto';
import LoginDTO from './types/login.dto';

@Controller('auth')
export default class AuthController {
Expand Down Expand Up @@ -50,7 +50,7 @@ export default class AuthController {
const { accessToken, refreshToken } = this.service.createTokens(tokenPayload);

res.cookie('refreshToken', refreshToken, {
path: '/user/token/refresh',
path: '/auth/refresh/token',
httpOnly: true,
sameSite: 'lax',
secure: true,
Expand All @@ -60,6 +60,44 @@ export default class AuthController {
return res.json({ accessToken });
}

@Public()
@Post('refresh/token')
@ApiCookieAuth('refreshToken')
@ApiOperation({ summary: '토큰 재발급', description: '유저의 Refresh Token을 확인하여 토큰을 재발급합니다' })
@ApiCreatedResponse({ description: '{ accessToken }' })
@ApiUnauthorizedResponse({ description: 'Refresh Token이 없거나 만료되었습니다' })
async getNewToken(@Cookies('refreshToken') refreshToken: string, @Res() res: Response): Promise<Response> {
if (!refreshToken) {
throw new UnauthorizedError(ErrorMessage.REFRESH_TOKEN_NOT_FOUND);
}

const { accessToken, refreshToken: newRefreshToken } = this.service.createNewToken(refreshToken);

res.cookie('refreshToken', newRefreshToken, {
path: '/auth/refresh/token',
httpOnly: true,
sameSite: 'lax',
secure: true,
maxAge: 7 * 24 * 60 * 60 * 1000
});

return res.json({ accessToken });
}

@Public()
@HttpCode(HttpStatus.OK)
@Post('check/email')
async checkEmail(@Body() body: { email: string }): Promise<boolean> {
return await this.service.checkEmail(body.email);
}

@Public()
@HttpCode(HttpStatus.OK)
@Post('check/nickname')
async checkNickName(@Body() body: { nickName: string }): Promise<boolean> {
return await this.service.checkNickName(body.nickName);
}

@Public()
@Get('google')
toGoogle(): { redirectUrl: string } {
Expand All @@ -80,7 +118,7 @@ export default class AuthController {

// 기존 회원의 경우 토큰 반환
res.cookie('refreshToken', tokens.refreshToken, {
path: '/user/token/refresh',
path: '/auth/refresh/token',
httpOnly: true,
sameSite: 'lax',
secure: true,
Expand Down Expand Up @@ -108,7 +146,7 @@ export default class AuthController {
}

res.cookie('refreshToken', tokens.refreshToken, {
path: '/user/token/refresh',
path: '/auth/refresh/token',
httpOnly: true,
sameSite: 'lax',
secure: true,
Expand Down Expand Up @@ -138,7 +176,7 @@ export default class AuthController {
}

res.cookie('refreshToken', tokens.refreshToken, {
path: '/user/token/refresh',
path: '/auth/refresh/token',
httpOnly: true,
sameSite: 'lax',
secure: true,
Expand All @@ -147,42 +185,4 @@ export default class AuthController {

return res.redirect(`${process.env.CLIENT_REDIRECT}?auth=${tokens.accessToken}`);
}

@Public()
@Post('refresh/token')
@ApiCookieAuth('refreshToken')
@ApiOperation({ summary: '토큰 재발급', description: '유저의 Refresh Token을 확인하여 토큰을 재발급합니다' })
@ApiCreatedResponse({ description: '{ accessToken }' })
@ApiUnauthorizedResponse({ description: 'Refresh Token이 없거나 만료되었습니다' })
async getNewToken(@Cookies('refreshToken') refreshToken: string, @Res() res: Response): Promise<Response> {
if (!refreshToken) {
throw new UnauthorizedError(ErrorMessage.REFRESH_TOKEN_NOT_FOUND);
}

const { accessToken, refreshToken: newRefreshToken } = this.service.createNewToken(refreshToken);

res.cookie('refreshToken', newRefreshToken, {
path: '/user/token/refresh',
httpOnly: true,
sameSite: 'lax',
secure: true,
maxAge: 7 * 24 * 60 * 60 * 1000
});

return res.json({ accessToken });
}

@Public()
@HttpCode(HttpStatus.OK)
@Post('check/email')
async checkEmail(@Body() body: { email: string }): Promise<boolean> {
return await this.service.checkEmail(body.email);
}

@Public()
@HttpCode(HttpStatus.OK)
@Post('check/nickname')
async checkNickName(@Body() body: { nickName: string }): Promise<boolean> {
return await this.service.checkNickName(body.nickName);
}
}
2 changes: 2 additions & 0 deletions src/modules/auth/auth.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@ import { GoogleStrategy } from './strategy/google.strategy';
import AuthController from './auth.controller';
import { KakaoStrategy } from './strategy/kakao.strategy';
import { NaverStrategy } from './strategy/naver.strategy';
import ProfileModule from '../profile/profile.module';

@Module({
imports: [
PassportModule,
JwtModule.register({
secret: process.env.JWT_SECRET
}),
ProfileModule,
UserStatsModule
],
controllers: [AuthController],
Expand Down
58 changes: 13 additions & 45 deletions src/modules/auth/auth.repository.ts
Original file line number Diff line number Diff line change
@@ -1,72 +1,40 @@
import { Injectable } from '@nestjs/common';
import { IDreamerProfile, IMakerProfile } from 'src/modules/user/domain/profile.interface';
import { DreamerProfileMapper, MakerProfileMapper } from 'src/modules/user/domain/profile.mapper';
import { IUser } from 'src/modules/user/domain/user.interface';
import UserMapper from 'src/modules/user/domain/user.mapper';
import { DreamerProfileProperties, MakerProfileProperties } from 'src/modules/user/types/profile.types';
import { OAuthProperties, SignupProperties } from 'src/modules/user/types/user.types';
import { IAuth } from 'src/modules/auth/domain/auth.interface';
import AuthMapper from 'src/modules/auth/domain/auth.mapper';
import DBClient from 'src/providers/database/prisma/DB.client';
import { OAuthProperties, SignupProperties } from './types/auth.types';

@Injectable()
export default class AuthRepository {
constructor(private readonly db: DBClient) {}

async findByEmail(email: string): Promise<IUser> {
async findByEmail(email: string): Promise<IAuth> {
const data = await this.db.user.findUnique({ where: { email } });

return new UserMapper(data).toDomain();
return new AuthMapper(data).toDomain();
}

async findByNickName(nickName: string): Promise<IUser> {
async findByNickName(nickName: string): Promise<IAuth> {
const data = await this.db.user.findUnique({ where: { nickName } });

return new UserMapper(data).toDomain();
return new AuthMapper(data).toDomain();
}

async findById(id: string): Promise<IUser> {
async findById(id: string): Promise<IAuth> {
const data = await this.db.user.findUnique({ where: { id } });

return new UserMapper(data).toDomain();
return new AuthMapper(data).toDomain();
}

async findByProvider(providerData: OAuthProperties): Promise<IUser> {
async findByProvider(providerData: OAuthProperties): Promise<IAuth> {
const data = await this.db.user.findUnique({ where: { provider_providerId: providerData } });

return new UserMapper(data).toDomain();
return new AuthMapper(data).toDomain();
}

async create(user: SignupProperties): Promise<IUser> {
async create(user: SignupProperties): Promise<IAuth> {
const data = await this.db.user.create({ data: user });

return new UserMapper(data).toDomain();
}

async createDreamer(user: Partial<DreamerProfileProperties>): Promise<IDreamerProfile> {
const profile = await this.db.dreamerProfile.create({
data: {
user: { connect: { id: user.userId } },
tripTypes: user.tripTypes,
serviceArea: user.serviceArea,
image: user.image
}
});

return new DreamerProfileMapper(profile).toDomain();
}

async createMaker(user: Partial<MakerProfileProperties>): Promise<IMakerProfile> {
const profile = await this.db.makerProfile.create({
data: {
user: { connect: { id: user.userId } },
serviceArea: user.serviceArea,
serviceTypes: user.serviceTypes,
gallery: user.gallery,
description: user.description,
detailDescription: user.detailDescription,
image: user.image
}
});

return new MakerProfileMapper(profile).toDomain();
return new AuthMapper(data).toDomain();
}
}
26 changes: 10 additions & 16 deletions src/modules/auth/auth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,26 @@ import ErrorMessage from 'src/common/constants/errorMessage.enum';
import BadRequestError from 'src/common/errors/badRequestError';
import { JwtService } from '@nestjs/jwt';
import { Injectable } from '@nestjs/common';
import User from 'src/modules/user/domain/user.domain';
import { DreamerProfile, MakerProfile } from 'src/modules/user/domain/profile.domain';
import UserStatsService from '../userStats/userStats.service';
import { FilteredUserProperties, OAuthProperties, UserProperties } from 'src/modules/user/types/user.types';
import { FilteredAuthProperties, OAuthProperties, AuthProperties } from 'src/modules/auth/types/auth.types';
import AuthRepository from './auth.repository';
import { Role, RoleValues } from 'src/common/constants/role.type';
import { DreamerProfileProperties, MakerProfileProperties } from 'src/modules/user/types/profile.types';
import { Role } from 'src/common/constants/role.type';
import { DreamerProfileProperties, MakerProfileProperties } from 'src/modules/profile/types/profile.types';
import { OAuthProvider } from 'src/common/constants/oauth.type';
import UnauthorizedError from 'src/common/errors/unauthorizedError';
import ProfileService from '../profile/profile.service';
import Auth from './domain/auth.domain';

@Injectable()
export default class AuthService {
constructor(
private readonly repository: AuthRepository,
private readonly jwt: JwtService,
private readonly profile: ProfileService,
private readonly userStats: UserStatsService
) {}

async createUser(user: UserProperties, profile: DreamerProfileProperties | MakerProfileProperties): Promise<null> {
async createUser(user: AuthProperties, profile: DreamerProfileProperties | MakerProfileProperties): Promise<null> {
// 유저 등록: 소셜 로그인의 경우 이메일이 없어 중복 확인 패스
const { provider, providerId } = user;

Expand All @@ -41,25 +42,18 @@ export default class AuthService {
throw new BadRequestError(ErrorMessage.USER_NICKNAME_EXIST);
}

const userData = await User.create(user);
const userData = await Auth.create(user);
const savedUser = await this.repository.create(userData.signupData());

// 역할에 따라 프로필 등록
if (savedUser.getRole() === RoleValues.DREAMER) {
const profileData = DreamerProfile.create({ ...profile, userId: savedUser.getId() });
await this.repository.createDreamer(profileData);
} else {
const profileData = MakerProfile.create({ ...profile, userId: savedUser.getId() });
await this.repository.createMaker(profileData.get());
}
await this.profile.createProfile(savedUser.getId(), savedUser.getRole(), profile);

// 유저 생성시 기본값으로 UserStats 생성
await this.userStats.create(savedUser.getId(), {});

return;
}

async login(email: string, password: string): Promise<FilteredUserProperties> {
async login(email: string, password: string): Promise<FilteredAuthProperties> {
const user = await this.repository.findByEmail(email);
if (!user) {
throw new BadRequestError(ErrorMessage.USER_UNAUTHORIZED_ID);
Expand Down
Loading