Skip to content

Commit

Permalink
Merge pull request #70 from boostcampwm-2024/be/feature/introudce
Browse files Browse the repository at this point in the history
[BE/feature] 소개글 수정 구현
  • Loading branch information
HBLEEEEE authored Nov 20, 2024
2 parents 4143d17 + 5c21b1f commit 47b2c6a
Show file tree
Hide file tree
Showing 11 changed files with 134 additions and 48 deletions.
16 changes: 15 additions & 1 deletion apps/backend/src/auth/auth.controller.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Body, Controller, Get, Post, Req, UseGuards } from '@nestjs/common';
import { Body, Controller, Get, Patch, Post, Req, UseGuards } from '@nestjs/common';
import { AuthService } from './auth.service';
import { SignUpDto } from './dto/signUp.dto';
import { successhandler, successMessage } from 'src/global/successhandler';
Expand All @@ -13,6 +13,9 @@ import { oauthResponseDecorator } from './decorator/oauth.decorator';
import { JwtAuthGuard } from 'src/global/utils/jwtAuthGuard';
import { Request } from 'express';
import { logoutResponseDecorator } from './decorator/logout.decorator';
import { UpdateIntroduceDto } from './dto/updateIntroduce.dto';
import { MemberData } from 'src/global/utils/requestInterface';
import { updateIntroduceResponseDecorator } from './decorator/updateIntroduce.decorator';

@Controller('api/auth')
export class AuthController {
Expand Down Expand Up @@ -71,4 +74,15 @@ export class AuthController {
const tokens = await this.authService.kakaoLogin(kakaoLoginDto);
return successhandler(successMessage.LOGIN_SUCCESS, tokens);
}

@Patch('introduce')
@UseGuards(JwtAuthGuard)
@ApiOperation({ summary: '유저 소개글 변경 API' })
@updateIntroduceResponseDecorator()
async updateIntroduce(@Req() req: Request, @Body() updateIntroduceDto: UpdateIntroduceDto) {
const { memberId } = req.user as MemberData;
const { introduce } = updateIntroduceDto;
await this.authService.updateIntroduce(memberId, introduce);
return successhandler(successMessage.INTRODUCE_UPDATE_SUCCESS);
}
}
2 changes: 1 addition & 1 deletion apps/backend/src/auth/auth.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { KakaoStrategy } from './strategies/kakao.strategy';
JwtModule.registerAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: (configService: ConfigService) => ({
useFactory: async (configService: ConfigService) => ({
secret: configService.get<string>('JWT_SECRET'),
signOptions: { expiresIn: '1h' }
})
Expand Down
3 changes: 2 additions & 1 deletion apps/backend/src/auth/auth.queries.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export const authQueries = {
signUpQuery: 'INSERT INTO members (email, password, nickname) VALUES ($1, $2, $3) RETURNING *',
findByEmailQuery:
'SELECT member_id, email, password, nickname FROM members WHERE email = $1 LIMIT 1'
'SELECT member_id, email, password, nickname FROM members WHERE email = $1 LIMIT 1',
upateMemberQuery: 'UPDATE members SET introduce = $1 WHERE member_id = $2'
};
13 changes: 7 additions & 6 deletions apps/backend/src/auth/auth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,8 @@ export class AuthService {
throw new HttpException('이메일 또는 비밀번호가 올바르지 않습니다.', HttpStatus.UNAUTHORIZED);

const isPasswordValid = await bcrypt.compare(password, member.rows[0].password);
if (!isPasswordValid) {
if (!isPasswordValid)
throw new HttpException('이메일 또는 비밀번호가 올바르지 않습니다.', HttpStatus.UNAUTHORIZED);
}

const payload = {
memberId: member.rows[0].member_id,
Expand Down Expand Up @@ -104,13 +103,15 @@ export class AuthService {
if (!token) throw new HttpException('토큰이 필요합니다.', HttpStatus.BAD_REQUEST);

const decodedToken = this.jwtService.decode(token) as { exp: number };
if (!decodedToken || !decodedToken.exp) {
if (!decodedToken || !decodedToken.exp)
throw new HttpException('유효하지 않은 토큰입니다.', HttpStatus.UNAUTHORIZED);
}

const remainingTime = decodedToken.exp * 1000 - Date.now();
if (remainingTime > 0) {
if (remainingTime > 0)
await this.redisClient.set(`blacklist:${token}`, 'true', { PX: remainingTime });
}
}

async updateIntroduce(memberId: number, introduce: string | null) {
await this.databaseService.query(authQueries.upateMemberQuery, [introduce, memberId]);
}
}
39 changes: 4 additions & 35 deletions apps/backend/src/auth/decorator/logout.decorator.ts
Original file line number Diff line number Diff line change
@@ -1,47 +1,16 @@
import { applyDecorators } from '@nestjs/common';
import { ApiResponse } from '@nestjs/swagger';
import { ApiBearerAuth, ApiResponse } from '@nestjs/swagger';
import { LogoutSuccessResponseDto } from '../dto/logout.dto';
import { TokenDecorator } from 'src/global/utils/tokenSwagger';

export function logoutResponseDecorator() {
return applyDecorators(
ApiBearerAuth(),
ApiResponse({
status: 200,
description: '로그아웃 성공',
type: LogoutSuccessResponseDto
}),
ApiResponse({
status: 400,
description: '로그아웃 실패 - 토큰 없음 오류',
content: {
'application/json': {
examples: {
noToken: {
summary: '토큰 없음',
value: {
code: 400,
message: '토큰이 필요합니다.'
}
}
}
}
}
}),
ApiResponse({
status: 401,
description: '로그아웃 실패 - 인증 오류',
content: {
'application/json': {
examples: {
expiredToken: {
summary: '만료된 토큰',
value: {
code: 401,
message: '유효하지 않은 토큰입니다.'
}
}
}
}
}
})
TokenDecorator()
);
}
16 changes: 16 additions & 0 deletions apps/backend/src/auth/decorator/updateIntroduce.decorator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { applyDecorators } from '@nestjs/common';
import { ApiBearerAuth, ApiResponse } from '@nestjs/swagger';
import { TokenDecorator } from 'src/global/utils/tokenSwagger';
import { updateIntroduceSuccessResponseDto } from '../dto/updateIntroduce.dto';

export function updateIntroduceResponseDecorator() {
return applyDecorators(
ApiBearerAuth(),
ApiResponse({
status: 200,
description: '소개글 수정 성공',
type: updateIntroduceSuccessResponseDto
}),
TokenDecorator()
);
}
23 changes: 23 additions & 0 deletions apps/backend/src/auth/dto/updateIntroduce.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { ApiProperty } from '@nestjs/swagger';

export class UpdateIntroduceDto {
@ApiProperty({
description: '소개글',
example: '나는 누구?'
})
introduce: string;
}

export class updateIntroduceSuccessResponseDto {
@ApiProperty({
description: '응답 코드',
example: 200
})
code: number;

@ApiProperty({
description: '응답 메세지',
example: '로그인 되었습니다.'
})
message: string;
}
1 change: 1 addition & 0 deletions apps/backend/src/global/successhandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export const successMessage = {
LOGIN_SUCCESS: { code: 200, message: '로그인 되었습니다.' },
LOGOUT_SUCCESS: { code: 200, message: '로그아웃 되었습니다.' },
GET_MEMBER_SUCCESS: { code: 201, message: '회원 가입되었습니다.' },
INTRODUCE_UPDATE_SUCCESS: { code: 200, message: '소개글이 변경되었습니다.' },
GET_MAIL_SUCCESS: { code: 200, message: '메일 조회를 완료했습니다.' },
DELETE_MAIL_SUCCESS: { code: 200, message: '메일 삭제를 완료했습니다.' },
GET_MAIL_ALARM_SUCCESS: { code: 200, message: 'Catch alarm!.' },
Expand Down
13 changes: 9 additions & 4 deletions apps/backend/src/global/utils/jwtAuthGuard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,23 @@ export class JwtAuthGuard {
async canActivate(context: any) {
const request = context.switchToHttp().getRequest();
const token = request.headers.authorization?.split(' ')[1];

if (!token) throw new HttpException('허가되지 않은 사용자입니다.', HttpStatus.UNAUTHORIZED);

const isBlacklist = await this.redisClient.exists(`blacklist:${token}`);
if (isBlacklist) throw new HttpException('유효하지 않은 토큰입니다.', HttpStatus.UNAUTHORIZED);

try {
const decoded = await this.jwtService.verifyAsync(token);
request.user = decoded;

const currentTime = Math.floor(Date.now() / 1000);
if (decoded.exp < currentTime) {
throw new HttpException('만료된 토큰입니다.', HttpStatus.UNAUTHORIZED);
}

const { memberId, email, nickname } = decoded;
request.user = { memberId, email, nickname };
return true;
} catch {
throw new HttpException('유효하지 않은 토큰입니다.', HttpStatus.UNAUTHORIZED);
throw new HttpException('잘못된 토큰입니다.', HttpStatus.UNAUTHORIZED);
}
}
}
5 changes: 5 additions & 0 deletions apps/backend/src/global/utils/requestInterface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export interface MemberData {
memberId: number;
email: string;
nickname: string;
}
51 changes: 51 additions & 0 deletions apps/backend/src/global/utils/tokenSwagger.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { applyDecorators } from '@nestjs/common';
import { ApiHeader, ApiResponse } from '@nestjs/swagger';

export function TokenDecorator() {
return applyDecorators(
ApiHeader({
name: 'Authorization',
description: 'Bearer <token>',
required: true,
example: 'asjudjasdnsodnaowdoiqndoiqwnoidqwndioqwndasjdnalkjsnqw'
}),
ApiResponse({
status: 401,
description: '토큰 오류로 인한 API 호출 실패',
content: {
'application/json': {
examples: {
noTokens: {
summary: '허가되지 않은 토큰',
value: {
code: 401,
message: '허가되지 않은 사용자입니다.'
}
},
blacklistTokens: {
summary: '유효하지 않은 토큰',
value: {
code: 401,
message: '유효하지 않은 토큰입니다.'
}
},
invalidTokens: {
summary: '잘못된 토큰',
value: {
code: 401,
message: '잘못된 토큰입니다.'
}
},
timeoutTokens: {
summary: '만료된 토큰',
value: {
code: 401,
message: '만료된 토큰입니다.'
}
}
}
}
}
})
);
}

0 comments on commit 47b2c6a

Please sign in to comment.