Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.ezcode.codetest.application.usermanagement.user.dto.request;

import io.swagger.v3.oas.annotations.media.Schema;

@Schema(description = "깃허브 repository 선택 요청")
public record UserGithubRepoSelectRequest(
@Schema(description = "repository 이름", example = "Practice coding")
String repositoryName
) {}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.ezcode.codetest.application.usermanagement.user.dto.response;

import java.util.List;
import java.util.Map;

import io.swagger.v3.oas.annotations.media.Schema;
Expand Down Expand Up @@ -41,14 +42,20 @@ public String getName() {
}

@Override
@Schema(description = "Github Id", example = "1345932")
@Schema(description = "깃허브 고유 Id", example = "1345932")
public String getGithubId() {
return attributes.get("id").toString();
}

@Override
@Schema(description = "Github URL", example = "https://github.com/id")
@Schema(description = "깃허브 홈 URL", example = "https://github.com/id")
public String getGithubUrl(){
return attributes.get("html_url").toString();
}

@Override
@Schema(description = "깃허브 아이디(login name)", example = "ezcode")
public String getOwner(){
return attributes.get("login").toString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,5 +48,9 @@ public String getGithubUrl() {
return "";
}

@Override
public String getOwner() {
return "";
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,8 @@ public interface OAuth2Response {

//깃헙 링크
String getGithubUrl();


//깃헙 owner
String getOwner();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package org.ezcode.codetest.application.usermanagement.user.dto.response;


import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;

@Getter
@Schema(description = "유저의 Public Repository 리스트를 보여줍니다")
public class UserGithubRepoResponse {
private String repoName;
private String defaultBranch;

public UserGithubRepoResponse(String repoName, String defaultBranch) {
this.repoName = repoName;
this.defaultBranch = defaultBranch;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package org.ezcode.codetest.common.security.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpHeaders;
import org.springframework.web.reactive.function.client.WebClient;

@Configuration
public class WebClientConfig {
@Bean
public WebClient githubWebClient() {
return WebClient.builder()
.baseUrl("https://api.github.com")
.defaultHeader(HttpHeaders.ACCEPT, "application/vnd.github+json")
.build();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

GitHub API 요청을 위해 User-Agent 헤더 추가 및 URL 하드코딩 제거 제안

GitHub API는 User-Agent 헤더가 없는 요청을 거부합니다. 또한 베이스 URL을 소스에 하드코딩하면 테스트/스테이징 환경 분리가 어려워집니다.
다음과 같이 수정하면 재사용성과 환경 분리가 용이합니다.

     return WebClient.builder()
-            .baseUrl("https://api.github.com")
-            .defaultHeader(HttpHeaders.ACCEPT, "application/vnd.github+json")
+            .baseUrl("${github.api.base-url:https://api.github.com}")
+            .defaultHeader(HttpHeaders.ACCEPT, "application/vnd.github+json")
+            .defaultHeader(HttpHeaders.USER_AGENT, "ezcode-codetest-server")
             .build();

application.yml 예시:

github:
  api:
    base-url: https://api.github.com
🤖 Prompt for AI Agents
In src/main/java/org/ezcode/codetest/common/security/config/WebClientConfig.java
around lines 12 to 15, the WebClient configuration is missing the required
'User-Agent' header for GitHub API requests and has the base URL hardcoded. To
fix this, add a default header for 'User-Agent' with an appropriate value, and
refactor the base URL to be injected from a configuration property (e.g., from
application.yml) instead of hardcoding it, enabling easier environment
separation and reuse.

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@
import org.ezcode.codetest.domain.user.exception.code.AuthExceptionCode;
import org.ezcode.codetest.domain.user.model.entity.CustomOAuth2User;
import org.ezcode.codetest.domain.user.model.entity.User;
import org.ezcode.codetest.domain.user.model.entity.UserGithubInfo;
import org.ezcode.codetest.domain.user.service.UserDomainService;
import org.ezcode.codetest.common.security.util.JwtUtil;
import org.ezcode.codetest.domain.user.service.UserGithubService;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
Expand All @@ -32,17 +34,20 @@ public class CustomSuccessHandler extends SimpleUrlAuthenticationSuccessHandler

private final JwtUtil jwtUtil;
private final UserDomainService userDomainService;
private final UserGithubService userGithubService;
private final RedisTemplate<String, String> redisTemplate;
private final ObjectMapper objectMapper; //json직렬화
private final OAuth2AuthorizedClientService authorizedClientService;
private final AESUtil aesUtil;

public CustomSuccessHandler(JwtUtil jwtUtil, UserDomainService userDomainService,
UserGithubService userGithubService,
RedisTemplate<String, String> redisTemplate, ObjectMapper objectMapper,
OAuth2AuthorizedClientService authorizedClientService, AESUtil aesUtil) {
this.jwtUtil = jwtUtil;
this.userDomainService = userDomainService;
this.redisTemplate = redisTemplate;
this.userGithubService = userGithubService;
this.redisTemplate = redisTemplate;
this.objectMapper = objectMapper;
this.authorizedClientService = authorizedClientService;
this.aesUtil = aesUtil;
Expand All @@ -66,15 +71,18 @@ public void onAuthenticationSuccess(HttpServletRequest request, HttpServletRespo
oauthToken.getName()
);

UserGithubInfo userGithubInfo = userGithubService.getUserGithubInfoById(loginUser.getId());

//AES 암호화
try {
String encodedGithubToken = aesUtil.encrypt(client.getAccessToken().getTokenValue());
loginUser.setGithubAccessToken(encodedGithubToken);

userGithubInfo.setGithubAccessToken(encodedGithubToken);
} catch (Exception e) {
log.error(e.getMessage());
throw new AuthException(AuthExceptionCode.TOKEN_ENCODE_FAIL);
}
userDomainService.updateUserGithubAccessToken(loginUser);
userGithubService.updateUserGithubAccessToken(userGithubInfo);
}

String accessToken = jwtUtil.createAccessToken(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
public enum UserExceptionCode implements ResponseCode {

NOT_ENOUGH_TOKEN(false, HttpStatus.BAD_REQUEST, "리뷰 토큰이 부족합니다."),
NOT_MATCH_CODE(false, HttpStatus.BAD_REQUEST, "이메일 인증 코드가 일치하지 않습니다.");
NOT_MATCH_CODE(false, HttpStatus.BAD_REQUEST, "이메일 인증 코드가 일치하지 않습니다."),
NO_GITHUB_INFO(false, HttpStatus.BAD_REQUEST, "깃허브 정보가 없습니다.");

private final boolean success;
private final HttpStatus status;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ public class User extends BaseEntity {

private boolean verified; //이메일 인증 여부

private String githubAccessToken; //깃허브 access_token 값
private boolean gitPushStatus; //깃허브 자동 push 여부


/*
Expand All @@ -91,6 +91,7 @@ public static User emailUser(String email, String password, String username, Str
.role(UserRole.ADMIN) // 테스트용
.isDeleted(false)
.verified(false)
.gitPushStatus(false)
.build();
}

Expand All @@ -108,6 +109,7 @@ public static User socialUser(String email, String username, String nickname, St
.password(password)
.isDeleted(false)
.verified(false)
.gitPushStatus(false)
.build();
}

Expand All @@ -123,13 +125,14 @@ public static User githubUser(String email, String username, String nickname, St
.isDeleted(false)
.verified(false)
.githubUrl(githubUrl)
.gitPushStatus(false)
.build();
}


@Builder
public User(String email, String password, String username, String nickname,
Integer age, Tier tier, UserRole role, boolean isDeleted, boolean verified, String githubUrl) {
Integer age, Tier tier, UserRole role, boolean isDeleted, boolean verified, String githubUrl, boolean gitPushStatus) {
this.email = email;
this.password = password;
this.username = username;
Expand All @@ -140,6 +143,7 @@ public User(String email, String password, String username, String nickname,
this.isDeleted = isDeleted;
this.verified = verified;
this.githubUrl = githubUrl;
this.gitPushStatus = gitPushStatus;
}

/*
Expand Down Expand Up @@ -181,9 +185,6 @@ public void setGithubUrl(String githubUrl){
this.githubUrl = githubUrl;
}

public void setGithubAccessToken(String githubAccessToken){
this.githubAccessToken = githubAccessToken;
}

public void decreaseReviewToken() {
this.reviewToken -= 1;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ public static User createSocialUser(
) {
//나중에 확장성 고려해서 switch문 사용
return switch (provider.toLowerCase()) {
case "github" -> User.githubUser(
case "github" ->
User.githubUser(
response.getEmail(),
response.getName(),
nickname,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package org.ezcode.codetest.domain.user.model.entity;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.OneToOne;
import jakarta.persistence.Table;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Entity
@Getter
@Table(name = "user_github")
@NoArgsConstructor
public class UserGithubInfo {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id", unique = true)
private User user;

@Column(nullable = false)
private String owner;

private String repo;

private String branch;

private String githubAccessToken;

@Builder
public UserGithubInfo(User user, String owner) {
this.user = user;
this.owner = owner;
}

public void setGithubAccessToken(String githubAccessToken){
this.githubAccessToken = githubAccessToken;
}

public void setGithubRepo(String githubRepoName, String defaultBranch) {
this.repo = githubRepoName;
this.branch = defaultBranch;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package org.ezcode.codetest.domain.user.repository;

import org.ezcode.codetest.domain.user.model.entity.UserGithubInfo;

public interface UserGithubInfoRepository {
void createUserGithubInfo(UserGithubInfo userGithubInfo);

UserGithubInfo getUserGithubInfo(Long id);

void updateGithubAccessToken(UserGithubInfo userGithubInfo);

void updateGithubInfo(UserGithubInfo userGithub);
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,5 @@ public interface UserRepository {
void updateReviewTokens(List<Long> ids , int newToken);

void updateUserGithubAccessToken(User loginUser);

}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import org.ezcode.codetest.domain.user.model.entity.User;
import org.ezcode.codetest.domain.user.model.entity.UserAuthType;
import org.ezcode.codetest.domain.user.model.entity.UserFactory;
import org.ezcode.codetest.domain.user.model.entity.UserGithubInfo;
import org.ezcode.codetest.domain.user.model.enums.AuthType;
import org.ezcode.codetest.domain.user.repository.UserAuthTypeRepository;
import org.ezcode.codetest.domain.user.repository.UserRepository;
Expand All @@ -30,6 +31,7 @@ public class CustomOAuth2UserService extends DefaultOAuth2UserService {
private final UserRepository userRepository;
private final UserAuthTypeRepository userAuthTypeRepository;
private final UserDomainService userDomainService;
private final UserGithubService userGithubService;

@Override
@Transactional
Expand Down Expand Up @@ -75,13 +77,15 @@ private void processUser(
updateExistingUser(user, response, authType, provider);
}
}

private void createNewUser(OAuth2Response response, AuthType authType, String provider) {
String nickname = userDomainService.generateUniqueNickname();
User newUser = UserFactory.createSocialUser(response, nickname, provider);
newUser.setVerified();
newUser.setReviewToken(20);
userRepository.createUser(newUser);
userAuthTypeRepository.createUserAuthType(new UserAuthType(newUser, authType));
updateGithubUrl(newUser, response, provider);
}

private void updateExistingUser(User user, OAuth2Response response, AuthType authType, String provider) {
Expand All @@ -95,8 +99,16 @@ private void updateExistingUser(User user, OAuth2Response response, AuthType aut
}
}
private void updateGithubUrl(User user, OAuth2Response response, String provider) {
if ("github".equals(provider) && user.getGithubUrl()==null) {
if ("github".equals(provider)) {
user.setGithubUrl(response.getGithubUrl());

UserGithubInfo userGithubInfo =
UserGithubInfo.builder()
.owner(response.getOwner())
.user(user)
.build();

userGithubService.createUserGithubInfo(userGithubInfo);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@
import org.ezcode.codetest.domain.user.exception.UserException;
import org.ezcode.codetest.domain.user.exception.code.UserExceptionCode;
import org.ezcode.codetest.domain.user.model.entity.UserAuthType;
import org.ezcode.codetest.domain.user.model.entity.UserGithubInfo;
import org.ezcode.codetest.domain.user.model.enums.Adjective;
import org.ezcode.codetest.domain.user.model.enums.AuthType;
import org.ezcode.codetest.domain.user.model.enums.Noun;
import org.ezcode.codetest.domain.user.exception.AuthException;
import org.ezcode.codetest.domain.user.exception.code.AuthExceptionCode;
import org.ezcode.codetest.domain.user.model.entity.User;
import org.ezcode.codetest.domain.user.repository.UserAuthTypeRepository;
import org.ezcode.codetest.domain.user.repository.UserGithubInfoRepository;
import org.ezcode.codetest.domain.user.repository.UserRepository;
import org.ezcode.codetest.common.security.util.PasswordEncoder;
import org.springframework.stereotype.Service;
Expand All @@ -30,6 +32,7 @@
public class UserDomainService {
private final UserRepository userRepository;
private final UserAuthTypeRepository userAuthTypeRepository;
private final UserGithubInfoRepository userGithubInfoRepository;
private final PasswordEncoder passwordEncoder;
private static final java.util.Random RANDOM = new java.util.Random();

Expand Down Expand Up @@ -128,8 +131,5 @@ public User getUserByEmail(String email) {
return userRepository.getUserByEmail(email);
}

public void updateUserGithubAccessToken(User loginUser) {
log.info("Updating github access token for user: {}", loginUser);
userRepository.updateUserGithubAccessToken(loginUser);
}

}
Loading