Skip to content

Commit 2d59ae0

Browse files
Merge pull request #151 from DevKor-github/develop
#115 ~ #150 반영해 운영 서버 배포
2 parents fb1cafc + ad443de commit 2d59ae0

190 files changed

Lines changed: 7465 additions & 401 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/deploy-to-dev-ec2.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,10 +75,12 @@ jobs:
7575
APPLE_KEY_ID="${{ secrets.APPLE_KEY_ID }}" \
7676
APPLE_PRIVATE_KEY="${{ secrets.APPLE_PRIVATE_KEY }}" \
7777
JWT_SECRET="${{ secrets.JWT_SECRET }}" \
78+
KAKAO_ADMIN_KEY="${{ secrets.KAKAO_ADMIN_KEY }}" \
7879
KAKAO_CLIENT_ID="${{ secrets.KAKAO_CLIENT_ID }}" \
7980
KAKAO_REDIRECT_URI="${{ secrets.KAKAO_REDIRECT_URI }}" \
8081
KAKAO_CLIENT_SECRET="${{ secrets.KAKAO_CLIENT_SECRET }}" \
8182
KMS_KEY_ID="${{ secrets.KMS_KEY_ID }}" \
83+
FIREBASE_ADMIN_KEY="${{ secrets.FIREBASE_ADMIN_KEY }}" \
8284
nohup java -Duser.timezone=Asia/Seoul -jar $JAR_NAME --spring.profiles.active=dev > app.log 2>&1 &
8385
8486
echo "✅ 배포 완료"

.github/workflows/deploy-to-prod-ec2.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,10 +75,12 @@ jobs:
7575
APPLE_KEY_ID="${{ secrets.APPLE_KEY_ID }}" \
7676
APPLE_PRIVATE_KEY="${{ secrets.APPLE_PRIVATE_KEY }}" \
7777
JWT_SECRET="${{ secrets.JWT_SECRET }}" \
78+
KAKAO_ADMIN_KEY="${{ secrets.KAKAO_ADMIN_KEY }}" \
7879
KAKAO_CLIENT_ID="${{ secrets.KAKAO_CLIENT_ID }}" \
7980
KAKAO_REDIRECT_URI="${{ secrets.KAKAO_REDIRECT_URI }}" \
8081
KAKAO_CLIENT_SECRET="${{ secrets.KAKAO_CLIENT_SECRET }}" \
8182
KMS_KEY_ID="${{ secrets.KMS_KEY_ID }}" \
83+
FIREBASE_ADMIN_KEY="${{ secrets.FIREBASE_ADMIN_KEY }}" \
8284
nohup java -Duser.timezone=Asia/Seoul -jar $JAR_NAME --spring.profiles.active=prod > app.log 2>&1 &
8385
8486
echo "✅ 배포 완료"

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,7 @@ out/
4040
.env
4141

4242
# node js 관련
43-
node_modules
43+
node_modules
44+
45+
# Firebase Admin Key
46+
src/main/resources/config/firebase-admin-key.json

build.gradle

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,9 @@ dependencies {
7575

7676
// 금칙어 필터링 라이브러리 의존성 추가
7777
implementation 'io.github.vaneproject:badwordfiltering:1.0.0'
78+
79+
// FCM admin SDK 추가
80+
implementation 'com.google.firebase:firebase-admin:9.5.0'
7881
}
7982

8083
tasks.named('test') {

src/main/java/org/devkor/apu/saerok_server/domain/auth/api/LocalAuthController.java

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,8 @@
77
import org.devkor.apu.saerok_server.domain.auth.api.dto.response.LocalAccessTokenResponse;
88
import org.devkor.apu.saerok_server.domain.auth.application.LocalAuthService;
99
import org.springframework.context.annotation.Profile;
10-
import org.springframework.web.bind.annotation.GetMapping;
11-
import org.springframework.web.bind.annotation.RequestMapping;
12-
import org.springframework.web.bind.annotation.RestController;
10+
import org.springframework.http.HttpStatus;
11+
import org.springframework.web.bind.annotation.*;
1312

1413
@Profile("local")
1514
@RestController
@@ -29,4 +28,15 @@ public class LocalAuthController {
2928
public LocalAccessTokenResponse issueLocalDummyUserToken() {
3029
return localAuthService.issueLocalDummyUserToken();
3130
}
31+
32+
@Operation(
33+
summary = "탈퇴한 더미 유저 재가입 처리",
34+
description = "탈퇴 처리된 더미 유저를 재가입시킵니다."
35+
)
36+
@PostMapping("/rejoin-dummy-user")
37+
@PermitAll
38+
@ResponseStatus(HttpStatus.CREATED)
39+
public void rejoinDummyUser() {
40+
localAuthService.rejoinDummyUser();
41+
}
3242
}

src/main/java/org/devkor/apu/saerok_server/domain/auth/application/AbstractSocialAuthService.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,13 @@
66
import org.devkor.apu.saerok_server.domain.auth.api.dto.response.AccessTokenResponse;
77
import org.devkor.apu.saerok_server.domain.auth.application.facade.AuthTokenFacade;
88
import org.devkor.apu.saerok_server.domain.auth.application.facade.AuthTokenFacade.AuthBundle;
9+
import org.devkor.apu.saerok_server.domain.auth.core.dto.SocialUserInfo;
910
import org.devkor.apu.saerok_server.domain.auth.core.entity.SocialAuth;
1011
import org.devkor.apu.saerok_server.domain.auth.core.entity.SocialProviderType;
1112
import org.devkor.apu.saerok_server.domain.auth.core.repository.SocialAuthRepository;
1213
import org.devkor.apu.saerok_server.domain.auth.core.service.UserProvisioningService;
1314
import org.devkor.apu.saerok_server.domain.auth.infra.SocialAuthClient;
14-
import org.devkor.apu.saerok_server.domain.auth.core.dto.SocialUserInfo;
15+
import org.devkor.apu.saerok_server.domain.user.core.entity.SignupStatusType;
1516
import org.devkor.apu.saerok_server.global.security.crypto.DataCryptoService;
1617
import org.devkor.apu.saerok_server.global.security.crypto.EncryptedPayload;
1718
import org.devkor.apu.saerok_server.global.shared.util.dto.ClientInfo;
@@ -40,6 +41,13 @@ public ResponseEntity<AccessTokenResponse> authenticate(String authorizationCode
4041

4142
SocialAuth socialAuth = socialAuthRepository
4243
.findByProviderAndProviderUserId(provider, userInfo.sub())
44+
.map(link -> {
45+
// 탈퇴했던 유저면 재프로비저닝
46+
if (link.getUser().getSignupStatus() == SignupStatusType.WITHDRAWN) {
47+
userProvisioningService.provisionRejoinedUser(link.getUser(), userInfo.email());
48+
}
49+
return link;
50+
})
4351
.orElseGet(() -> userProvisioningService.provisionNewUser(provider, userInfo));
4452

4553
if (userInfo.refreshToken() != null) {

src/main/java/org/devkor/apu/saerok_server/domain/auth/application/LocalAuthService.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,26 @@
22

33
import lombok.RequiredArgsConstructor;
44
import org.devkor.apu.saerok_server.domain.auth.api.dto.response.LocalAccessTokenResponse;
5+
import org.devkor.apu.saerok_server.domain.auth.core.service.UserProvisioningService;
56
import org.devkor.apu.saerok_server.domain.user.core.entity.User;
67
import org.devkor.apu.saerok_server.domain.user.core.repository.UserRepository;
78
import org.devkor.apu.saerok_server.domain.user.core.repository.UserRoleRepository;
89
import org.devkor.apu.saerok_server.global.security.token.AccessTokenProvider;
10+
import org.devkor.apu.saerok_server.global.shared.exception.NotFoundException;
911
import org.springframework.stereotype.Service;
1012
import org.springframework.transaction.annotation.Transactional;
1113

1214
import java.util.List;
1315

1416
@Service
15-
@Transactional(readOnly = true)
17+
@Transactional
1618
@RequiredArgsConstructor
1719
public class LocalAuthService {
1820

1921
private final AccessTokenProvider accessTokenProvider;
2022
private final UserRepository userRepository;
2123
private final UserRoleRepository userRoleRepository;
24+
private final UserProvisioningService userProvisioningService;
2225

2326
public LocalAccessTokenResponse issueLocalDummyUserToken() {
2427

@@ -34,4 +37,10 @@ public LocalAccessTokenResponse issueLocalDummyUserToken() {
3437

3538
return new LocalAccessTokenResponse(token, user.getSignupStatus().name());
3639
}
40+
41+
public void rejoinDummyUser() {
42+
43+
User user = userRepository.findDeletedUserById(99999L).orElseThrow(() -> new NotFoundException("탈퇴한 로컬 더미 유저가 존재하지 않습니다"));
44+
userProvisioningService.provisionRejoinedUser(user, "fake-email@saerok.com");
45+
}
3746
}

src/main/java/org/devkor/apu/saerok_server/domain/auth/core/repository/SocialAuthRepository.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import org.devkor.apu.saerok_server.domain.auth.core.entity.SocialProviderType;
77
import org.springframework.stereotype.Repository;
88

9+
import java.util.List;
910
import java.util.Optional;
1011

1112
@Repository
@@ -27,4 +28,11 @@ public SocialAuth save(SocialAuth socialAuth) {
2728
em.persist(socialAuth);
2829
return socialAuth;
2930
}
31+
32+
public List<SocialAuth> findByUserId(Long userId) {
33+
return em.createQuery(
34+
"SELECT s FROM SocialAuth s WHERE s.user.id = :userId", SocialAuth.class)
35+
.setParameter("userId", userId)
36+
.getResultList();
37+
}
3038
}

src/main/java/org/devkor/apu/saerok_server/domain/auth/core/repository/UserRefreshTokenRepository.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,10 @@ public Optional<UserRefreshToken> findByRefreshTokenHash(String refreshTokenHash
2323
.getResultStream()
2424
.findFirst();
2525
}
26+
27+
public int deleteByUserId(Long userId) {
28+
return em.createQuery("DELETE FROM UserRefreshToken rt WHERE rt.user.id = :userId")
29+
.setParameter("userId", userId)
30+
.executeUpdate();
31+
}
2632
}

src/main/java/org/devkor/apu/saerok_server/domain/auth/core/service/UserProvisioningService.java

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,10 @@
1111
import org.devkor.apu.saerok_server.domain.user.core.entity.UserRoleType;
1212
import org.devkor.apu.saerok_server.domain.user.core.repository.UserRepository;
1313
import org.devkor.apu.saerok_server.domain.user.core.repository.UserRoleRepository;
14+
import org.devkor.apu.saerok_server.domain.user.core.service.ProfileImageDefaultService;
1415
import org.devkor.apu.saerok_server.global.shared.exception.SocialAuthAlreadyExistsException;
1516
import org.springframework.stereotype.Service;
1617

17-
/**
18-
* 새로운 유저에 대한 프로비저닝(Provisioning: 사용자가 요청한 IT 자원을 사용할 수 있는 상태로 준비하는 것)을 담당하는 도메인 서비스
19-
* 사용자 관점에서는 "회원가입", 시스템 관점에서는 "프로비저닝".
20-
*/
2118
@Service
2219
@RequiredArgsConstructor
2320
@Transactional
@@ -26,9 +23,10 @@ public class UserProvisioningService {
2623
private final UserRepository userRepository;
2724
private final UserRoleRepository userRoleRepository;
2825
private final SocialAuthRepository socialAuthRepository;
26+
private final ProfileImageDefaultService profileImageDefaultService;
2927

3028
/**
31-
* 새로운 사용자에게 필요한 자원(User, UserRole, SocialAuth)을 할당한다.
29+
* 새로운 사용자에게 필요한 자원을 할당한다.
3230
* @param provider 소셜 공급자 (ex. Apple, Kakao)
3331
* @param userInfo 소셜 공급자로부터 받은 사용자 정보 (sub, email)
3432
* @return 해당 사용자에게 생성된 SocialAuth 엔티티
@@ -40,10 +38,39 @@ public SocialAuth provisionNewUser(SocialProviderType provider, SocialUserInfo u
4038
}
4139

4240
User user = userRepository.save(User.createUser(userInfo.email()));
41+
profileImageDefaultService.setRandomVariant(user);
42+
4343
userRoleRepository.save(UserRole.createUserRole(user, UserRoleType.USER));
4444

4545
return socialAuthRepository.save(
4646
SocialAuth.createSocialAuth(user, provider, userInfo.sub())
4747
);
4848
}
49+
50+
/**
51+
* 탈퇴했던 사용자가 재가입할 때 필요한 자원을 다시 준비한다.
52+
* 기존 users 레코드를 그대로 사용하고, 다음을 수행한다:
53+
* - signupStatus → PROFILE_REQUIRED
54+
* - deleted_at → null
55+
* - joined_at → 현재 시각
56+
* - 이메일이 비어 있으면 소셜에서 받은 이메일로 복구
57+
* - USER 롤이 없으면 다시 부여
58+
* - 기본 프로필 이미지가 비어 있으면 랜덤 배정
59+
*/
60+
public void provisionRejoinedUser(User user, String email) {
61+
62+
user.restoreForRejoin();
63+
64+
if (user.getEmail() == null && email != null) {
65+
user.setEmail(email);
66+
}
67+
68+
if (userRoleRepository.findByUser(user).isEmpty()) {
69+
userRoleRepository.save(UserRole.createUserRole(user, UserRoleType.USER));
70+
}
71+
72+
if (user.getDefaultProfileImageVariant() == null) {
73+
profileImageDefaultService.setRandomVariant(user);
74+
}
75+
}
4976
}

0 commit comments

Comments
 (0)