Skip to content

Commit

Permalink
Merge pull request #159 from modern-agile-team/feature/user
Browse files Browse the repository at this point in the history
Feature/user
  • Loading branch information
hobiJeong authored Dec 21, 2023
2 parents 6933ad7 + b431b4b commit b3eaf33
Show file tree
Hide file tree
Showing 10 changed files with 270 additions and 44 deletions.
124 changes: 90 additions & 34 deletions src/auth/services/auth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export class AuthService {
private readonly userRepository: UserRepository,
private readonly userImageRepository: UserImageRepository,
private readonly tokenService: TokenService,
) {}
) {}

async naverLogin(authorizeCode: string) {
try {
Expand All @@ -32,19 +32,23 @@ export class AuthService {
redirect_uri: process.env.NAVER_CALLBACK_URL,
};

const naverToken = (await axios.post(naverTokenUrl, naverTokenBody, naverTokenHeader)).data;
const naverToken = (
await axios.post(naverTokenUrl, naverTokenBody, naverTokenHeader)
).data;

const naverAccessToken = naverToken.access_token;
const naverRefreshToken = naverToken.refresh_token;

const naverUserInfoUrl = 'https://openapi.naver.com/v1/nid/me';
const naverUserInfoHeader = {
headers: {
Authorization: `Bearer ${naverAccessToken}`,
},
};

const naverUserInfo = (await axios.get(naverUserInfoUrl, naverUserInfoHeader)).data;
const naverUserInfo = (
await axios.get(naverUserInfoUrl, naverUserInfoHeader)
).data;
const nickname = naverUserInfo.response.nickname;
const email = naverUserInfo.response.email;
const profileImage = naverUserInfo.response.profile_image;
Expand All @@ -58,33 +62,44 @@ export class AuthService {
};

const checkUser = await this.userRepository.findUser(email, provider);
if (checkUser) { // 이미 존재하는 사용자인 경우
if (checkUser) {
// 이미 존재하는 사용자인 경우
const userId = checkUser.id;

await this.userRepository.updateUserName(userId, nickname); // 이름 업데이트

const userImage = (await this.userImageRepository.checkUserImage(userId)).imageUrl; // DB 이미지
const userImage = (
await this.userImageRepository.checkUserImage(userId)
).imageUrl; // DB 이미지
const imageUrlParts = userImage.split('/');
const dbImageProvider = imageUrlParts[imageUrlParts.length - 2]; // 이미지 제공자 이름

if (dbImageProvider != 'ma6-main.s3.ap-northeast-2.amazonaws.com') { // S3에 업로드된 이미지가 아닌 경우
if (dbImageProvider != 'ma6-main.s3.ap-northeast-2.amazonaws.com') {
// S3에 업로드된 이미지가 아닌 경우
await this.userImageRepository.updateUserImage(userId, profileImage); // DB에 이미지 URL 업데이트
}

return { userId, naverAccessToken, naverRefreshToken };
} else { // 존재하지 않는 사용자인 경우
} else {
// 존재하지 않는 사용자인 경우
const newUser = await this.userRepository.createUser(userInfo);
const userId = newUser.id;
if (!profileImage) {
await this.userImageRepository.uploadUserImage(userId, process.env.DEFAULT_USER_IMAGE);
await this.userImageRepository.uploadUserImage(
userId,
process.env.DEFAULT_USER_IMAGE,
);
} else {
await this.userImageRepository.uploadUserImage(userId, profileImage);
}
return { userId, naverAccessToken, naverRefreshToken };
}
} catch (error) {
if (error.response.status == 401) {
throw new HttpException('유효하지 않은 인가코드입니다.', HttpStatus.UNAUTHORIZED);
throw new HttpException(
'유효하지 않은 인가코드입니다.',
HttpStatus.UNAUTHORIZED,
);
}
}
}
Expand All @@ -104,7 +119,10 @@ export class AuthService {
code: authorizeCode,
};

const kakaoToken = (await axios.post(kakaoTokenUrl, kakaoTokenBody, kakaoTokenHeader)).data;
const kakaoToken = (
await axios.post(kakaoTokenUrl, kakaoTokenBody, kakaoTokenHeader)
).data;

const kakaoAccessToken = kakaoToken.access_token;
const kakaoRefreshToken = kakaoToken.refresh_token;

Expand All @@ -116,7 +134,9 @@ export class AuthService {
},
};

const kakaoUserInfo = (await axios.get(kakaoUserInfoUrl, kakaoUserInfoHeader)).data;
const kakaoUserInfo = (
await axios.get(kakaoUserInfoUrl, kakaoUserInfoHeader)
).data;
const nickname = kakaoUserInfo.properties.nickname;
const email = kakaoUserInfo.kakao_account.email;
const profileImage = kakaoUserInfo.properties.profile_image;
Expand All @@ -127,45 +147,65 @@ export class AuthService {
nickname,
email,
gender,
}
};

const checkUser = await this.userRepository.findUser(email, provider);
if (checkUser) { // 이미 존재하는 사용자인 경우
if (checkUser) {
// 이미 존재하는 사용자인 경우
const userId = checkUser.id;

await this.userRepository.updateUserName(userId, nickname); // 이름 업데이트

const userImage = (await this.userImageRepository.checkUserImage(userId)).imageUrl; // DB 이미지

const userImage = (
await this.userImageRepository.checkUserImage(userId)
).imageUrl; // DB 이미지
const imageUrlParts = userImage.split('/');
const dbImageProvider = imageUrlParts[imageUrlParts.length - 2]; // 이미지 제공자 이름

if (dbImageProvider != 'ma6-main.s3.ap-northeast-2.amazonaws.com') { // S3에 업로드된 이미지가 아닌 경우
if (dbImageProvider != 'ma6-main.s3.ap-northeast-2.amazonaws.com') {
// S3에 업로드된 이미지가 아닌 경우
await this.userImageRepository.updateUserImage(userId, profileImage); // DB에 이미지 URL 업데이트
}

return { userId, kakaoAccessToken, kakaoRefreshToken };
} else { // 존재하지 않는 사용자인 경우
} else {
// 존재하지 않는 사용자인 경우
const newUser = await this.userRepository.createUser(userInfo);
const userId = newUser.id;
if (!profileImage) {
await this.userImageRepository.uploadUserImage(userId, process.env.DEFAULT_USER_IMAGE);
await this.userImageRepository.uploadUserImage(
userId,
process.env.DEFAULT_USER_IMAGE,
);
} else {
await this.userImageRepository.uploadUserImage(userId, profileImage);
}
return { userId, kakaoAccessToken, kakaoRefreshToken };
}
} catch (error) {
if (error.response.status == 400) {
throw new HttpException('유효하지 않은 인가코드입니다.', HttpStatus.UNAUTHORIZED);
throw new HttpException(
'유효하지 않은 인가코드입니다.',
HttpStatus.UNAUTHORIZED,
);
} else {
console.log(error);
throw new HttpException(
'카카오 로그인 중 오류가 발생했습니다.',
HttpStatus.INTERNAL_SERVER_ERROR,
);

}
}
}

async kakaoLogout(accessToken: string, refreshToken: string) {
try {
const checkValidKakaoToken = await this.tokenService.checkValidKakaoToken(accessToken);
const checkValidKakaoToken =
await this.tokenService.checkValidKakaoToken(accessToken);
if (checkValidKakaoToken === 401) {
const newKakaoToken = await this.tokenService.getNewKakaoToken(refreshToken);
const newKakaoToken =
await this.tokenService.getNewKakaoToken(refreshToken);
accessToken = newKakaoToken.access_token;
}

Expand All @@ -177,18 +217,23 @@ export class AuthService {
};

axios.post(kakaoLogoutUrl, {}, kakaoLogoutHeader);
return { message: "카카오 로그아웃이 완료되었습니다." };
return { message: '카카오 로그아웃이 완료되었습니다.' };
} catch (error) {
console.log(error);
throw new HttpException('카카오 로그아웃 중 오류가 발생했습니다.', HttpStatus.INTERNAL_SERVER_ERROR)
throw new HttpException(
'카카오 로그아웃 중 오류가 발생했습니다.',
HttpStatus.INTERNAL_SERVER_ERROR,
);
}
}

async kakaoUnlink(accessToken: string, refreshToken: string) {
try {
const checkValidKakaoToken = await this.tokenService.checkValidKakaoToken(accessToken);
const checkValidKakaoToken =
await this.tokenService.checkValidKakaoToken(accessToken);
if (checkValidKakaoToken === 401) {
const newKakaoToken = await this.tokenService.getNewKakaoToken(refreshToken);
const newKakaoToken =
await this.tokenService.getNewKakaoToken(refreshToken);
accessToken = newKakaoToken.access_token;
}

Expand All @@ -200,18 +245,23 @@ export class AuthService {
};

axios.post(kakaoUnlinkUrl, {}, kakaoUnlinkHeader);
return { message: "카카오 연결 끊기가 완료되었습니다." };
return { message: '카카오 연결 끊기가 완료되었습니다.' };
} catch (error) {
console.log(error);
throw new HttpException('카카오 연결 끊기 중 오류가 발생했습니다.', HttpStatus.INTERNAL_SERVER_ERROR)
throw new HttpException(
'카카오 연결 끊기 중 오류가 발생했습니다.',
HttpStatus.INTERNAL_SERVER_ERROR,
);
}
}

async naverUnlink(accessToken: string, refreshToken: string) {
try {
const checkValidNaverToken = await this.tokenService.checkValidNaverToken(accessToken);
const checkValidNaverToken =
await this.tokenService.checkValidNaverToken(accessToken);
if (checkValidNaverToken === 401) {
const newNaverToken = await this.tokenService.getNewNaverToken(refreshToken);
const newNaverToken =
await this.tokenService.getNewNaverToken(refreshToken);
accessToken = newNaverToken.access_token;
}

Expand All @@ -229,18 +279,24 @@ export class AuthService {
};

axios.post(naverUnlinkUrl, naverUnlinkBody, naverUnlinkHeader);
return { message: "네이버 연동 해제가 완료되었습니다." };
return { message: '네이버 연동 해제가 완료되었습니다.' };
} catch (error) {
console.log(error);
throw new HttpException('네이버 연결 끊기 중 오류가 발생했습니다.', HttpStatus.INTERNAL_SERVER_ERROR)
throw new HttpException(
'네이버 연결 끊기 중 오류가 발생했습니다.',
HttpStatus.INTERNAL_SERVER_ERROR,
);
}
}

async accountDelete(userId: number) {
const deleteUser = await this.userRepository.deleteUser(userId);
if (!deleteUser) {
throw new HttpException('사용자를 찾을 수 없습니다.', HttpStatus.NOT_FOUND);
throw new HttpException(
'사용자를 찾을 수 없습니다.',
HttpStatus.NOT_FOUND,
);
}
return { message: "사용자 계정 삭제가 완료되었습니다." };
return { message: '사용자 계정 삭제가 완료되었습니다.' };
}
}
7 changes: 7 additions & 0 deletions src/boards/repository/boards.repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,13 @@ export class BoardRepository {
});
}

async findBoardByuserId(userId: number): Promise<Board[]> {
return await this.entityManager.find(Board, {
relations: ['user', 'user.userImage', 'boardImages'],
where: { userId },
});
}

async updateBoard(
id: number,
boardData: Partial<CreateBoardDto>,
Expand Down
4 changes: 3 additions & 1 deletion src/common/decorators/get-userId.decorator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ import { ExecutionContext, createParamDecorator } from '@nestjs/common';
export const GetUserId = createParamDecorator(
(data, ctx: ExecutionContext): number => {
const req = ctx.switchToHttp().getRequest();

if (!req.user) {
return null;
}
return req.user.userId;
},
);
2 changes: 1 addition & 1 deletion src/config/guards/jwt-access-token.guard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export class JwtAccessTokenGuard {
const accessToken = request.headers['access_token'];

if (!accessToken) {
return false;
return true;
}

const userId = await this.tokenService.decodeToken(accessToken);
Expand Down
27 changes: 26 additions & 1 deletion src/users/controllers/user.controller.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
import { Controller, Get, UseGuards } from '@nestjs/common';
import {
Controller,
Get,
Param,
ParseIntPipe,
Query,
UseGuards,
} from '@nestjs/common';
import { UserService } from '../services/user.service';
import { ApiTags } from '@nestjs/swagger';
import { JwtAccessTokenGuard } from 'src/config/guards/jwt-access-token.guard';
import { GetUserId } from 'src/common/decorators/get-userId.decorator';
import { ApiGetMyInfo } from '../swagger-decorators/get-my-info-decorator';
import { ApiGetMyInfoWithOwner } from '../swagger-decorators/get-my-info-with-owner-decorator';
import { ApiGetInfo } from '../swagger-decorators/get-info-decorator';

@Controller('user')
@ApiTags('user API')
Expand All @@ -16,4 +25,20 @@ export class UserController {
async getMyInfo(@GetUserId() userId: number) {
return this.userService.getMyInfo(userId);
}

@ApiGetInfo()
@Get('info-board')
async getUserInfo(@Query('userId') userId: number) {
return this.userService.getUserInfo(userId);
}

@UseGuards(JwtAccessTokenGuard)
@ApiGetMyInfoWithOwner()
@Get('info/:targetId')
async getMyInfoWithOwner(
@GetUserId() userId: number,
@Param('targetId', ParseIntPipe) targetId: number,
) {
return this.userService.getMyInfoWithOwner(userId, targetId);
}
}
Loading

0 comments on commit b3eaf33

Please sign in to comment.