Skip to content

Commit

Permalink
feat: merge 충돌 해결 (#47) (KAN-102)
Browse files Browse the repository at this point in the history
  • Loading branch information
seok019283501 committed Oct 31, 2024
1 parent 414b7da commit 7a70c5e
Show file tree
Hide file tree
Showing 69 changed files with 798 additions and 373 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,23 @@
import org.ioteatime.meonghanyangserver.auth.dto.reponse.RefreshResponse;
import org.ioteatime.meonghanyangserver.auth.dto.request.EmailRequest;
import org.ioteatime.meonghanyangserver.auth.dto.request.LoginRequest;
import org.ioteatime.meonghanyangserver.auth.dto.request.VerifyEmailRequest;
import org.ioteatime.meonghanyangserver.common.api.Api;
import org.ioteatime.meonghanyangserver.user.dto.UserDto;
import org.ioteatime.meonghanyangserver.user.dto.request.JoinRequest;
import org.ioteatime.meonghanyangserver.user.dto.response.UserSimpleResponse;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;

@Tag(name = "Auth Api", description = "인증 관련 API 목록입니다.")
public interface AuthApi {
@Operation(summary = "회원 가입을 합니다.")
Api<Object> registerUser(@Valid @RequestBody UserDto userDto);
Api<Object> registerUser(@Valid @RequestBody JoinRequest userDto);

@Operation(summary = "인증 메일 전송")
Api<?> verifyEmail(@Valid @RequestBody EmailRequest email);
Api<?> sendEmailCode(@Valid @RequestBody EmailRequest email);

@Operation(summary = "메일 인증 코드 검증")
Api<?> verifyEmail(@Valid @RequestBody VerifyEmailRequest verifyEmailRequest);

@Operation(summary = "로그인을 합니다.")
Api<LoginResponse> login(@RequestBody @Valid LoginRequest loginRequest);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@
import org.ioteatime.meonghanyangserver.auth.dto.reponse.RefreshResponse;
import org.ioteatime.meonghanyangserver.auth.dto.request.EmailRequest;
import org.ioteatime.meonghanyangserver.auth.dto.request.LoginRequest;
import org.ioteatime.meonghanyangserver.auth.dto.request.VerifyEmailRequest;
import org.ioteatime.meonghanyangserver.auth.service.AuthService;
import org.ioteatime.meonghanyangserver.common.api.Api;
import org.ioteatime.meonghanyangserver.user.dto.UserDto;
import org.ioteatime.meonghanyangserver.common.type.AuthSuccessType;
import org.ioteatime.meonghanyangserver.user.dto.request.JoinRequest;
import org.ioteatime.meonghanyangserver.user.dto.response.UserSimpleResponse;
import org.springframework.web.bind.annotation.*;

Expand All @@ -19,34 +21,40 @@ public class AuthController implements AuthApi {
private final AuthService authService;

@PostMapping("/sign-up")
public Api<Object> registerUser(@Valid @RequestBody UserDto userDto) {
public Api<Object> registerUser(@Valid @RequestBody JoinRequest userDto) {
authService.joinProcess(userDto);
return Api.CREATE();
return Api.success(AuthSuccessType.SIGN_UP);
}

@PostMapping("/email-verification")
public Api<?> verifyEmail(@Valid @RequestBody EmailRequest emailReq) {
public Api<?> sendEmailCode(@Valid @RequestBody EmailRequest emailReq) {
authService.send(emailReq.email());
return Api.OK();
return Api.success(AuthSuccessType.SEND_EMAIL_CODE);
}

@PostMapping("/check-verification")
public Api<?> verifyEmail(VerifyEmailRequest verifyEmailRequest) {
authService.verifyEmailCode(verifyEmailRequest.email(), verifyEmailRequest.code());
return Api.success(AuthSuccessType.VERIFY_EMAIL_CODE);
}

// Email 중복 확인
@PostMapping("/check-email")
public Api<UserSimpleResponse> duplicateEmail(@Valid @RequestBody EmailRequest emailReq) {
UserSimpleResponse response = authService.verifyEmail(emailReq.email());
return Api.OK(response);
return Api.success(AuthSuccessType.EMAIL_VERIFIED);
}

@PostMapping("/sign-in")
public Api<LoginResponse> login(LoginRequest loginRequest) {
LoginResponse loginResponse = authService.login(loginRequest);
return Api.OK(loginResponse);
return Api.success(AuthSuccessType.SIGN_IN, loginResponse);
}

@PostMapping("/refresh-token")
public Api<RefreshResponse> refreshToken(
@RequestHeader("Authorization") String authorizationHeader) {
RefreshResponse refreshResponse = authService.reissueAccessToken(authorizationHeader);
return Api.OK(refreshResponse);
return Api.success(AuthSuccessType.REISSUE_ACCESS_TOKEN, refreshResponse);
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
package org.ioteatime.meonghanyangserver.auth.dto.request;

import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.Valid;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotNull;

public record EmailRequest(
@Valid @Email @NotNull @Schema(description = "이메일", example = "[email protected]")
String email) {}
@Email @NotNull @Schema(description = "이메일", example = "[email protected]") String email) {}
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
package org.ioteatime.meonghanyangserver.auth.dto.request;

import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;

@Data
public class LoginRequest {
@NotBlank private String email;
@NotBlank private String password;
}
public record LoginRequest(
@NotBlank @Schema(description = "이메일", example = "[email protected]") String email,
@NotBlank @Schema(description = "비밀번호", example = "testpassword") String password) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.ioteatime.meonghanyangserver.auth.dto.request;

import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotNull;

public record VerifyEmailRequest(
@Email @NotNull @Schema(description = "이메일", example = "[email protected]") String email,
@NotNull @Schema(description = "인증 코드", example = "XV23W1") String code) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package org.ioteatime.meonghanyangserver.auth.mapper;

import org.ioteatime.meonghanyangserver.user.domain.UserEntity;
import org.ioteatime.meonghanyangserver.user.dto.request.JoinRequest;

public class AuthEntityMapper {
public static UserEntity of(JoinRequest userDto, String encodedPassword) {
return UserEntity.builder()
.nickname(userDto.getNickname())
.email(userDto.getEmail())
.password(encodedPassword)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package org.ioteatime.meonghanyangserver.auth.mapper;

import org.ioteatime.meonghanyangserver.auth.dto.reponse.LoginResponse;
import org.ioteatime.meonghanyangserver.auth.dto.reponse.RefreshResponse;
import org.ioteatime.meonghanyangserver.user.dto.response.UserSimpleResponse;

public class AuthResponseMapper {
public static UserSimpleResponse from(Long id, String email) {
return new UserSimpleResponse(id, email);
}

public static LoginResponse from(Long id, String accessToken, String refreshToken) {
return LoginResponse.builder()
.userId(id)
.accessToken(accessToken)
.refreshToken(refreshToken)
.build();
}

public static RefreshResponse from(String newAccessToken) {
return new RefreshResponse(newAccessToken);
}
}
Original file line number Diff line number Diff line change
@@ -1,18 +1,28 @@
package org.ioteatime.meonghanyangserver.auth.service;

import java.util.Collections;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ThreadLocalRandom;
import lombok.RequiredArgsConstructor;
import org.ioteatime.meonghanyangserver.auth.dto.reponse.LoginResponse;
import org.ioteatime.meonghanyangserver.auth.dto.reponse.RefreshResponse;
import org.ioteatime.meonghanyangserver.auth.dto.request.LoginRequest;
import org.ioteatime.meonghanyangserver.auth.mapper.AuthEntityMapper;
import org.ioteatime.meonghanyangserver.auth.mapper.AuthResponseMapper;
import org.ioteatime.meonghanyangserver.clients.google.GoogleMailClient;
import org.ioteatime.meonghanyangserver.common.error.ErrorTypeCode;
import org.ioteatime.meonghanyangserver.common.exception.ApiException;
import org.ioteatime.meonghanyangserver.common.exception.BadRequestException;
import org.ioteatime.meonghanyangserver.common.exception.NotFoundException;
import org.ioteatime.meonghanyangserver.common.exception.UnauthorizedException;
import org.ioteatime.meonghanyangserver.common.type.AuthErrorType;
import org.ioteatime.meonghanyangserver.common.utils.JwtUtils;
import org.ioteatime.meonghanyangserver.group.repository.groupuser.GroupUserRepository;
import org.ioteatime.meonghanyangserver.redis.EmailCode;
import org.ioteatime.meonghanyangserver.redis.EmailCodeRepository;
import org.ioteatime.meonghanyangserver.redis.RefreshToken;
import org.ioteatime.meonghanyangserver.redis.RefreshTokenRepository;
import org.ioteatime.meonghanyangserver.user.domain.UserEntity;
import org.ioteatime.meonghanyangserver.user.dto.UserDto;
import org.ioteatime.meonghanyangserver.user.dto.request.JoinRequest;
import org.ioteatime.meonghanyangserver.user.dto.response.UserSimpleResponse;
import org.ioteatime.meonghanyangserver.user.repository.UserRepository;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
Expand All @@ -25,68 +35,90 @@ public class AuthService {
private final GoogleMailClient googleMailClient;
private final BCryptPasswordEncoder bCryptPasswordEncoder;
private final JwtUtils jwtUtils;
private final EmailCodeRepository emailCodeRepository;
private final RefreshTokenRepository refreshTokenRepository;
private final GroupUserRepository groupUserRepository;

public LoginResponse login(LoginRequest loginRequest) {
UserEntity userEntity =
userRepository
.findByEmail(loginRequest.getEmail())
.orElseThrow(
() -> new ApiException(ErrorTypeCode.BAD_REQUEST, "없는 회원입니다."));
.findByEmail(loginRequest.email())
.orElseThrow(() -> new NotFoundException(AuthErrorType.NOT_FOUND));

boolean passwordMatch =
bCryptPasswordEncoder.matches(loginRequest.getPassword(), userEntity.getPassword());
bCryptPasswordEncoder.matches(loginRequest.password(), userEntity.getPassword());
if (!passwordMatch) {
throw new ApiException(ErrorTypeCode.BAD_REQUEST, "비밀번호가 틀렸습니다.");
throw new BadRequestException(AuthErrorType.PASSWORD_NOT_MATCH);
}

String accessToken = jwtUtils.generateAccessToken(userEntity);
String refreshToken = jwtUtils.generateRefreshToken(userEntity);

if (accessToken.isEmpty() || refreshToken.isEmpty()) {
throw new ApiException(ErrorTypeCode.SERVER_ERROR);
throw new NotFoundException(AuthErrorType.TOKEN_NOT_FOUND);
}
RefreshToken refreshTokenEntity = RefreshToken.builder().refreshToken(refreshToken).build();

refreshTokenRepository.save(refreshTokenEntity);
accessToken = jwtUtils.includeBearer(accessToken);
refreshToken = jwtUtils.includeBearer(refreshToken);

return LoginResponse.builder()
.userId(userEntity.getId())
.accessToken(accessToken)
.refreshToken(refreshToken)
.build();
return AuthResponseMapper.from(userEntity.getId(), accessToken, refreshToken);
}

public UserSimpleResponse joinProcess(UserDto userDto) {
public UserSimpleResponse joinProcess(JoinRequest userDto) {
String encodedPassword = bCryptPasswordEncoder.encode(userDto.getPassword());
UserEntity user = userRepository.save(AuthEntityMapper.of(userDto, encodedPassword));

UserEntity userEntity =
UserEntity.builder()
.nickname(userDto.getNickname())
.email(userDto.getEmail())
.password(encodedPassword)
.build();

userRepository.save(userEntity);

// 회원가입 응답 생성
return new UserSimpleResponse(userEntity.getId(), userEntity.getEmail());
return AuthResponseMapper.from(user.getId(), user.getEmail());
}

public void send(String email) {
// TODO. Redis 적용 후 코드 발급 구현 필요
googleMailClient.sendMail(email, "hello", "world");
String code = getCode();
emailCodeRepository.save(EmailCode.builder().email(email).code(code).build());
String mailSubject = "[\uD83D\uDC36 멍하냥] 이메일 인증 코드입니다.";
String mailContent =
"""
<h3>환영해요!</h3>
<b>인증코드를 입력하세요</b>
<p>%s</p>
"""
.formatted(code);
googleMailClient.sendMail(email, mailSubject, mailContent);
}

private static String getCode() {
ThreadLocalRandom random = ThreadLocalRandom.current();

List<String> authStr = new CopyOnWriteArrayList<>();
for (int i = 0; i < 3; i++) {
authStr.add(String.valueOf(random.nextInt(10)));
}
for (int i = 0; i < 3; i++) {
authStr.add(String.valueOf((char) (random.nextInt(26) + 65)));
}

Collections.shuffle(authStr);
return String.join("", authStr);
}

public UserSimpleResponse verifyEmail(String email) {
UserEntity userEntity =
userRepository
.findByEmail(email)
.orElseThrow(() -> new ApiException(ErrorTypeCode.NULL_POINT));
return UserSimpleResponse.from(userEntity);
.orElseThrow(() -> new NotFoundException(AuthErrorType.NOT_FOUND));

return AuthResponseMapper.from(userEntity.getId(), userEntity.getEmail());
}

public void verifyEmailCode(String email, String code) {
EmailCode emailCode =
emailCodeRepository
.findByEmail(email)
.orElseThrow(() -> new NotFoundException(AuthErrorType.NOT_FOUND));
if (!code.equals(emailCode.getCode())) {
throw new UnauthorizedException(AuthErrorType.CODE_NOT_EQUALS);
}
}

public RefreshResponse reissueAccessToken(String authorizationHeader) {
Expand All @@ -96,30 +128,25 @@ public RefreshResponse reissueAccessToken(String authorizationHeader) {
UserEntity userEntity =
userRepository
.findById(userId)
.orElseThrow(
() ->
new ApiException(
ErrorTypeCode.BAD_REQUEST, "유효하지 않은 사용자입니다."));
.orElseThrow(() -> new NotFoundException(AuthErrorType.NOT_FOUND));

if (!jwtUtils.validateToken(refreshToken, userEntity)) {
throw new ApiException(ErrorTypeCode.BAD_REQUEST, "Refresh token이 만료되었거나 유효하지 않습니다.");
throw new NotFoundException(AuthErrorType.REFRESH_TOKEN_INVALID);
}

RefreshToken storedToken =
refreshTokenRepository
.findByRefreshToken(refreshToken)
.orElseThrow(
() ->
new ApiException(
ErrorTypeCode.BAD_REQUEST,
"유효하지 않은 Refresh token입니다."));
() -> new NotFoundException(AuthErrorType.REFRESH_TOKEN_INVALID));

if (!storedToken.getRefreshToken().equals(refreshToken)) {
throw new ApiException(ErrorTypeCode.BAD_REQUEST, "토큰이 일치하지 않습니다.");
throw new BadRequestException(AuthErrorType.TOKEN_NOT_EQUALS);
}

String newAccessToken = jwtUtils.generateAccessToken(userEntity);
newAccessToken = jwtUtils.includeBearer(newAccessToken);
return new RefreshResponse(newAccessToken);

return AuthResponseMapper.from(newAccessToken);
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
package org.ioteatime.meonghanyangserver.cctv.dto.response;

public record CctvInfoResponse(Long cctvId, String cctvNickname, String kvsChannelName) {
}
public record CctvInfoResponse(Long cctvId, String cctvNickname, String kvsChannelName) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package org.ioteatime.meonghanyangserver.cctv.dto.response;

import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;

@Schema(description = "CCTV 초대 응답")
public record CctvInviteResponse(
@NotNull @Schema(description = "그룹 ID", example = "1") Long groupId,
@NotNull
@Schema(
description = "KVS 채널 이름",
example = "kvs-9bc02f62-df22-40b0-bdc1-610d8fd293b4")
String kvsChannelId) {}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
import org.ioteatime.meonghanyangserver.cctv.dto.response.CctvInfoResponse;

public class CctvResponseMapper {
public static CctvInfoResponse toCctvInfoResponse(CctvEntity cctvEntity){
return new CctvInfoResponse(cctvEntity.getId(), cctvEntity.getCctvNickname(), cctvEntity.getCctvNickname());
public static CctvInfoResponse toCctvInfoResponse(CctvEntity cctvEntity) {
return new CctvInfoResponse(
cctvEntity.getId(), cctvEntity.getCctvNickname(), cctvEntity.getCctvNickname());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package org.ioteatime.meonghanyangserver.cctv.repository;

public interface CctvRepository {
boolean existsByKvsChannelName(String kvsChannelName);
}
Loading

0 comments on commit 7a70c5e

Please sign in to comment.