From 95decbcfb10f7fa80d11b3c1396d474f87c5878b Mon Sep 17 00:00:00 2001 From: JaeSeo Yang <96044622+psychology50@users.noreply.github.com> Date: Sat, 10 Feb 2024 08:39:10 +0900 Subject: [PATCH 1/4] fix: #106 smsRedisMapper refactor --- .../api/apis/auth/mapper/SmsRedisMapper.java | 66 ++++++++----------- .../api/apis/oauth/controller/OauthApi.java | 1 - .../api/apis/oauth/usecase/OauthUseCase.java | 44 +++++-------- .../redis/sms/provider/SmsOauthProvider.java | 2 + .../redis/sms/qualify/SmsOauthQualifier.java | 13 ++++ .../infra/client/sms/snes/dto/SensInfo.java | 7 -- 6 files changed, 58 insertions(+), 75 deletions(-) create mode 100644 fitapet-domain/src/main/java/kr/co/fitapet/domain/common/redis/sms/qualify/SmsOauthQualifier.java delete mode 100644 fitapet-infra/src/main/java/kr/co/fitapet/infra/client/sms/snes/dto/SensInfo.java diff --git a/fitapet-app-external-api/src/main/java/kr/co/fitapet/api/apis/auth/mapper/SmsRedisMapper.java b/fitapet-app-external-api/src/main/java/kr/co/fitapet/api/apis/auth/mapper/SmsRedisMapper.java index 1eae9ec3..f7b74258 100644 --- a/fitapet-app-external-api/src/main/java/kr/co/fitapet/api/apis/auth/mapper/SmsRedisMapper.java +++ b/fitapet-app-external-api/src/main/java/kr/co/fitapet/api/apis/auth/mapper/SmsRedisMapper.java @@ -1,65 +1,51 @@ package kr.co.fitapet.api.apis.auth.mapper; -import kr.co.fitapet.domain.common.redis.sms.service.SmsOauthService; -import kr.co.fitapet.domain.common.redis.sms.service.SmsPasswordService; -import kr.co.fitapet.domain.common.redis.sms.service.SmsRegisterService; -import kr.co.fitapet.domain.common.redis.sms.service.SmsUidService; +import kr.co.fitapet.domain.common.redis.sms.provider.SmsRedisProvider; +import kr.co.fitapet.domain.common.redis.sms.qualify.SmsOauthQualifier; +import kr.co.fitapet.domain.common.redis.sms.qualify.SmsPasswordQualifier; +import kr.co.fitapet.domain.common.redis.sms.qualify.SmsRegisterQualifier; +import kr.co.fitapet.domain.common.redis.sms.qualify.SmsUidQualifier; import kr.co.fitapet.domain.common.redis.sms.type.SmsPrefix; -import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; import java.time.LocalDateTime; +import java.util.Map; @Component -@RequiredArgsConstructor public class SmsRedisMapper { - private final SmsOauthService smsOauthService; - private final SmsRegisterService smsRegisterService; - private final SmsPasswordService smsPasswordService; - private final SmsUidService smsUidService; + private final Map smsProviderMap; + + public SmsRedisMapper( + @SmsOauthQualifier SmsRedisProvider smsOauthService, + @SmsRegisterQualifier SmsRedisProvider smsRegisterService, + @SmsPasswordQualifier SmsRedisProvider smsPasswordService, + @SmsUidQualifier SmsRedisProvider smsUidService + ) { + smsProviderMap = Map.of( + SmsPrefix.OAUTH, smsOauthService, + SmsPrefix.REGISTER, smsRegisterService, + SmsPrefix.PASSWORD, smsPasswordService, + SmsPrefix.UID, smsUidService + ); + } public void saveSmsAuthToken(String phone, String code, SmsPrefix prefix) { - switch (prefix) { - case OAUTH -> smsOauthService.save(phone, code, prefix); - case REGISTER -> smsRegisterService.save(phone, code, prefix); - case PASSWORD -> smsPasswordService.save(phone, code, prefix); - case UID -> smsUidService.save(phone, code, prefix); - } + smsProviderMap.get(prefix).saveSmsAuthToken(phone, code, prefix); } public boolean isCorrectCode(String phone, String code, SmsPrefix prefix) { - return switch (prefix) { - case OAUTH -> smsOauthService.isCorrectCode(phone, code, prefix); - case REGISTER -> smsRegisterService.isCorrectCode(phone, code, prefix); - case PASSWORD -> smsPasswordService.isCorrectCode(phone, code, prefix); - case UID -> smsUidService.isCorrectCode(phone, code, prefix); - }; + return smsProviderMap.get(prefix).isCorrectCode(phone, code, prefix); } public boolean isExistsCode(String phone, SmsPrefix prefix) { - return switch (prefix) { - case OAUTH -> smsOauthService.isExistsCode(phone, prefix); - case REGISTER -> smsRegisterService.isExistsCode(phone, prefix); - case PASSWORD -> smsPasswordService.isExistsCode(phone, prefix); - case UID -> smsUidService.isExistsCode(phone, prefix); - }; + return smsProviderMap.get(prefix).isExistsCode(phone, prefix); } public void removeCode(String phone, SmsPrefix prefix) { - switch (prefix) { - case OAUTH -> smsOauthService.removeCode(phone, prefix); - case REGISTER -> smsRegisterService.removeCode(phone, prefix); - case PASSWORD -> smsPasswordService.removeCode(phone, prefix); - case UID -> smsUidService.removeCode(phone, prefix); - }; + smsProviderMap.get(prefix).removeCode(phone, prefix); } public LocalDateTime getExpiredTime(String phone, SmsPrefix prefix) { - return switch (prefix) { - case OAUTH -> smsOauthService.getExpiredTime(phone, prefix); - case REGISTER -> smsRegisterService.getExpiredTime(phone, prefix); - case PASSWORD -> smsPasswordService.getExpiredTime(phone, prefix); - case UID -> smsUidService.getExpiredTime(phone, prefix); - }; + return smsProviderMap.get(prefix).getExpiredTime(phone, prefix); } } diff --git a/fitapet-app-external-api/src/main/java/kr/co/fitapet/api/apis/oauth/controller/OauthApi.java b/fitapet-app-external-api/src/main/java/kr/co/fitapet/api/apis/oauth/controller/OauthApi.java index 30f2948c..4871dfac 100644 --- a/fitapet-app-external-api/src/main/java/kr/co/fitapet/api/apis/oauth/controller/OauthApi.java +++ b/fitapet-app-external-api/src/main/java/kr/co/fitapet/api/apis/oauth/controller/OauthApi.java @@ -38,7 +38,6 @@ @RequestMapping("/api/v1/auth/oauth") @Slf4j public class OauthApi { - private final MemberAuthUseCase memberAuthUseCase; private final OauthUseCase oauthUseCase; private final CookieUtil cookieUtil; diff --git a/fitapet-app-external-api/src/main/java/kr/co/fitapet/api/apis/oauth/usecase/OauthUseCase.java b/fitapet-app-external-api/src/main/java/kr/co/fitapet/api/apis/oauth/usecase/OauthUseCase.java index dabfcd1a..03719211 100644 --- a/fitapet-app-external-api/src/main/java/kr/co/fitapet/api/apis/oauth/usecase/OauthUseCase.java +++ b/fitapet-app-external-api/src/main/java/kr/co/fitapet/api/apis/oauth/usecase/OauthUseCase.java @@ -1,18 +1,17 @@ package kr.co.fitapet.api.apis.oauth.usecase; import kr.co.fitapet.api.apis.auth.dto.SmsRes; +import kr.co.fitapet.api.apis.auth.mapper.JwtMapper; +import kr.co.fitapet.api.apis.auth.mapper.SmsRedisMapper; import kr.co.fitapet.api.apis.oauth.dto.OauthSmsReq; import kr.co.fitapet.api.apis.oauth.helper.OauthOIDCHelper; -import kr.co.fitapet.api.common.security.jwt.JwtProvider; +import kr.co.fitapet.api.common.security.jwt.consts.JwtType; import kr.co.fitapet.api.common.security.jwt.dto.Jwt; import kr.co.fitapet.api.common.security.jwt.dto.JwtSubInfo; import kr.co.fitapet.api.common.security.jwt.dto.JwtUserInfo; import kr.co.fitapet.api.common.security.jwt.dto.SmsOauthInfo; import kr.co.fitapet.api.common.security.jwt.exception.AuthErrorCode; import kr.co.fitapet.api.common.security.jwt.exception.AuthErrorException; -import kr.co.fitapet.api.common.security.jwt.qualifier.AccessTokenQualifier; -import kr.co.fitapet.api.common.security.jwt.qualifier.RefreshTokenQualifier; -import kr.co.fitapet.api.common.security.jwt.qualifier.SmsOauthTokenQualifier; import kr.co.fitapet.common.annotation.UseCase; import kr.co.fitapet.common.execption.GlobalErrorException; import kr.co.fitapet.domain.common.redis.forbidden.ForbiddenTokenService; @@ -60,18 +59,14 @@ public class OauthUseCase { private final OauthApplicationConfigMapper oauthApplicationConfigMapper; private final OauthClientMapper oauthClientMapper; - @AccessTokenQualifier - private final JwtProvider accessTokenProvider; - @RefreshTokenQualifier - private final JwtProvider refreshTokenProvider; - @SmsOauthTokenQualifier - private final JwtProvider smsOauthTokenProvider; + private final JwtMapper jwtMapper; + private final ForbiddenTokenService forbiddenTokenService; private final OIDCTokenService oidcTokenService; private final SmsProvider smsProvider; - @SmsRegisterQualifier - private final SmsRedisProvider smsRedisHelper; + + private final SmsRedisMapper smsRedisMapper; @Transactional public Optional> signInByOIDC(String id, String idToken, ProviderType provider, String nonce) { @@ -81,7 +76,7 @@ public Optional> signInByOIDC(String id, String idToken, Provide if (oauthSearchService.isExistMember(id, provider)) { Member member = oauthSearchService.findMemberByOauthIdAndProvider(id, provider); - return Optional.of(Pair.of(member.getId(), generateToken(JwtUserInfo.from(member)))); + return Optional.of(Pair.of(member.getId(), jwtMapper.login(JwtUserInfo.from(member)))); } else { oidcTokenService.saveOIDCToken(idToken, provider, id); return Optional.empty(); @@ -90,11 +85,11 @@ public Optional> signInByOIDC(String id, String idToken, Provide @Transactional public Pair signUpByOIDC(String id, ProviderType provider, String requestOauthAccessToken, OauthSignUpReq req) { - String accessToken = smsOauthTokenProvider.resolveToken(requestOauthAccessToken); - JwtSubInfo subs = smsOauthTokenProvider.getSubInfoFromToken(accessToken); + String smsOauthToken = jwtMapper.resolveToken(requestOauthAccessToken, JwtType.SMS_OAUTH_TOKEN); + JwtSubInfo subs = jwtMapper.getSubInfoFromToken(smsOauthToken, JwtType.SMS_OAUTH_TOKEN); String phone = getPhoneByTopic(subs.phoneNumber()); - validateToken(accessToken, subs.phoneNumber(), provider); + validateToken(smsOauthToken, subs.phoneNumber(), provider); String idToken = oidcTokenService.findOIDCToken(req.idToken()).getToken(); OIDCDecodePayload payload = getPayload(provider, idToken, req.nonce()); @@ -102,17 +97,18 @@ public Pair signUpByOIDC(String id, ProviderType provider, String req Member member = Member.builder().uid(req.uid()).name(req.name()) .phone(phone).isOauth(Boolean.TRUE).role(RoleType.USER).build(); memberSaveService.saveMember(member); + OauthAccount oauthAccount = OauthAccount.of(id, provider, payload.email()); oauthAccount.updateMember(member); oidcTokenService.deleteOIDCToken(req.idToken()); forbiddenTokenService.register( - AccessToken.of(accessToken, subs.id(), smsOauthTokenProvider.getExpiryDate(accessToken)) + AccessToken.of(smsOauthToken, subs.id(), jwtMapper.getExpiryDate(smsOauthToken, JwtType.SMS_OAUTH_TOKEN)) ); log.info("success oauth signup member id : {} - oauth id : {} [provider: {}]", member.getId(), oauthAccount.getOauthId(), oauthAccount.getProvider()); - return Pair.of(member.getId(), generateToken(JwtUserInfo.from(member))); + return Pair.of(member.getId(), jwtMapper.login(JwtUserInfo.from(member))); } @Transactional @@ -120,6 +116,7 @@ public SmsRes sendCode(OauthSmsReq dto, ProviderType provider) { SnesDto.SensInfo smsInfo = smsProvider.sendCodeByPhoneNumber(SnesDto.Request.of(dto.to())); String key = makeTopic(dto.to(), provider); + smsRedisHelper.saveSmsAuthToken(key, smsInfo.code(), SmsPrefix.OAUTH); LocalDateTime expireTime = smsRedisHelper.getExpiredTime(key, SmsPrefix.OAUTH); log.info("인증번호 만료 시간: {}", expireTime); @@ -144,10 +141,10 @@ public Pair checkCertificationNumber(OauthSmsReq req, String id, Stri oauthAccount.updateMember(member); oidcTokenService.deleteOIDCToken(req.idToken()); - return Pair.of(member.getId(), generateToken(JwtUserInfo.from(member))); + return Pair.of(member.getId(), jwtMapper.login(JwtUserInfo.from(member))); } - return Pair.of(0L, Jwt.of(smsOauthTokenProvider.generateToken(SmsOauthInfo.of(id, key)), null)); + return Pair.of(0L, Jwt.of(jwtMapper.generateToken(SmsOauthInfo.of(id, key), JwtType.SMS_OAUTH_TOKEN), null)); } /** @@ -192,11 +189,4 @@ private ProviderType getProviderByTopic(String topic) { private String getPhoneByTopic(String topic) { return topic.split("_")[1]; } - - private Jwt generateToken(JwtSubInfo jwtSubInfo) { - return Jwt.builder() - .accessToken(accessTokenProvider.generateToken(jwtSubInfo)) - .refreshToken(refreshTokenProvider.generateToken(jwtSubInfo)) - .build(); - } } diff --git a/fitapet-domain/src/main/java/kr/co/fitapet/domain/common/redis/sms/provider/SmsOauthProvider.java b/fitapet-domain/src/main/java/kr/co/fitapet/domain/common/redis/sms/provider/SmsOauthProvider.java index 80d886f2..c50cda16 100644 --- a/fitapet-domain/src/main/java/kr/co/fitapet/domain/common/redis/sms/provider/SmsOauthProvider.java +++ b/fitapet-domain/src/main/java/kr/co/fitapet/domain/common/redis/sms/provider/SmsOauthProvider.java @@ -4,6 +4,7 @@ import kr.co.fitapet.domain.common.redis.exception.RedisErrorException; import kr.co.fitapet.domain.common.redis.sms.dao.SmsOauthRepository; import kr.co.fitapet.domain.common.redis.sms.domain.SmsOauth; +import kr.co.fitapet.domain.common.redis.sms.qualify.SmsOauthQualifier; import kr.co.fitapet.domain.common.redis.sms.type.SmsPrefix; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -18,6 +19,7 @@ @Service @Primary @RequiredArgsConstructor +@SmsOauthQualifier public class SmsOauthProvider implements SmsRedisProvider { private final SmsOauthRepository smsOauthRepository; diff --git a/fitapet-domain/src/main/java/kr/co/fitapet/domain/common/redis/sms/qualify/SmsOauthQualifier.java b/fitapet-domain/src/main/java/kr/co/fitapet/domain/common/redis/sms/qualify/SmsOauthQualifier.java new file mode 100644 index 00000000..b42f3c44 --- /dev/null +++ b/fitapet-domain/src/main/java/kr/co/fitapet/domain/common/redis/sms/qualify/SmsOauthQualifier.java @@ -0,0 +1,13 @@ +package kr.co.fitapet.domain.common.redis.sms.qualify; + +import org.springframework.beans.factory.annotation.Qualifier; + +import java.lang.annotation.*; + +@Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD, + ElementType.TYPE, ElementType.ANNOTATION_TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Qualifier("smsOauthQualifier") +public @interface SmsOauthQualifier { +} diff --git a/fitapet-infra/src/main/java/kr/co/fitapet/infra/client/sms/snes/dto/SensInfo.java b/fitapet-infra/src/main/java/kr/co/fitapet/infra/client/sms/snes/dto/SensInfo.java deleted file mode 100644 index 997b8372..00000000 --- a/fitapet-infra/src/main/java/kr/co/fitapet/infra/client/sms/snes/dto/SensInfo.java +++ /dev/null @@ -1,7 +0,0 @@ -package kr.co.fitapet.infra.client.sms.snes.dto; - -import lombok.Builder; - -import java.time.LocalDateTime; - - From 1136f2a6d84725b296685b01beac870077c98875 Mon Sep 17 00:00:00 2001 From: JaeSeo Yang <96044622+psychology50@users.noreply.github.com> Date: Sun, 11 Feb 2024 17:15:48 +0900 Subject: [PATCH 2/4] =?UTF-8?q?fix:=20#106=20oauthUseCase=EC=97=90?= =?UTF-8?q?=EC=84=9C=20forbiddenTokenService=20=EC=A7=81=EC=A0=91=20?= =?UTF-8?q?=EC=A2=85=EC=86=8D=EC=84=B1=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/apis/auth/mapper/JwtMapper.java | 5 +++++ .../api/apis/oauth/usecase/OauthUseCase.java | 20 +++++++------------ 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/fitapet-app-external-api/src/main/java/kr/co/fitapet/api/apis/auth/mapper/JwtMapper.java b/fitapet-app-external-api/src/main/java/kr/co/fitapet/api/apis/auth/mapper/JwtMapper.java index 2e14aa10..8fd533cc 100644 --- a/fitapet-app-external-api/src/main/java/kr/co/fitapet/api/apis/auth/mapper/JwtMapper.java +++ b/fitapet-app-external-api/src/main/java/kr/co/fitapet/api/apis/auth/mapper/JwtMapper.java @@ -131,6 +131,11 @@ public boolean isTokenExpired(String token, JwtType type) { return jwtProviderMap.get(type).isTokenExpired(token); } + /** + * 토큰을 블랙 리스트에 등록하는 메서드 + * @param token : 블랙 리스트에 등록할 토큰 + * @param type : 토큰의 타입 + */ public void ban(String token, JwtType type) { AccessToken forbiddenToken = AccessToken.of(token, getSubInfoFromToken(token, type).id(), getExpiryDate(token, type)); forbiddenTokenService.register(forbiddenToken); diff --git a/fitapet-app-external-api/src/main/java/kr/co/fitapet/api/apis/oauth/usecase/OauthUseCase.java b/fitapet-app-external-api/src/main/java/kr/co/fitapet/api/apis/oauth/usecase/OauthUseCase.java index 03719211..421931ff 100644 --- a/fitapet-app-external-api/src/main/java/kr/co/fitapet/api/apis/oauth/usecase/OauthUseCase.java +++ b/fitapet-app-external-api/src/main/java/kr/co/fitapet/api/apis/oauth/usecase/OauthUseCase.java @@ -60,14 +60,11 @@ public class OauthUseCase { private final OauthClientMapper oauthClientMapper; private final JwtMapper jwtMapper; - - private final ForbiddenTokenService forbiddenTokenService; + private final SmsRedisMapper smsRedisMapper; private final OIDCTokenService oidcTokenService; private final SmsProvider smsProvider; - private final SmsRedisMapper smsRedisMapper; - @Transactional public Optional> signInByOIDC(String id, String idToken, ProviderType provider, String nonce) { OIDCDecodePayload payload = getPayload(provider, idToken, nonce); @@ -102,10 +99,7 @@ public Pair signUpByOIDC(String id, ProviderType provider, String req oauthAccount.updateMember(member); oidcTokenService.deleteOIDCToken(req.idToken()); - forbiddenTokenService.register( - AccessToken.of(smsOauthToken, subs.id(), jwtMapper.getExpiryDate(smsOauthToken, JwtType.SMS_OAUTH_TOKEN)) - ); - + jwtMapper.ban(smsOauthToken, JwtType.SMS_OAUTH_TOKEN); log.info("success oauth signup member id : {} - oauth id : {} [provider: {}]", member.getId(), oauthAccount.getOauthId(), oauthAccount.getProvider()); return Pair.of(member.getId(), jwtMapper.login(JwtUserInfo.from(member))); @@ -117,8 +111,8 @@ public SmsRes sendCode(OauthSmsReq dto, ProviderType provider) { String key = makeTopic(dto.to(), provider); - smsRedisHelper.saveSmsAuthToken(key, smsInfo.code(), SmsPrefix.OAUTH); - LocalDateTime expireTime = smsRedisHelper.getExpiredTime(key, SmsPrefix.OAUTH); + smsRedisMapper.saveSmsAuthToken(key, smsInfo.code(), SmsPrefix.OAUTH); + LocalDateTime expireTime = smsRedisMapper.getExpiredTime(key, SmsPrefix.OAUTH); log.info("인증번호 만료 시간: {}", expireTime); return SmsRes.of(dto.to(), smsInfo.requestTime(), expireTime); } @@ -127,11 +121,11 @@ public SmsRes sendCode(OauthSmsReq dto, ProviderType provider) { public Pair checkCertificationNumber(OauthSmsReq req, String id, String code, ProviderType provider) { String key = makeTopic(req.to(), provider); log.info("key: {}", key); - if (!smsRedisHelper.isCorrectCode(key, code, SmsPrefix.OAUTH)) { + if (!smsRedisMapper.isCorrectCode(key, code, SmsPrefix.OAUTH)) { log.warn("인증번호 불일치 -> 사용자 입력 인증 번호 : {}", code); throw new GlobalErrorException(SmsErrorCode.INVALID_AUTH_CODE); } - smsRedisHelper.removeCode(key, SmsPrefix.OAUTH); + smsRedisMapper.removeCode(key, SmsPrefix.OAUTH); if (memberSearchService.isExistByPhone(req.to())) { Member member = memberSearchService.findByPhone(req.to()); @@ -174,7 +168,7 @@ private String makeTopic(String phoneNumber, ProviderType provider) { } private void validateToken(String accessToken, String value, ProviderType provider) { - if (forbiddenTokenService.isForbidden(accessToken)) + if (jwtMapper.isForbidden(accessToken)) throw new AuthErrorException(AuthErrorCode.FORBIDDEN_ACCESS_TOKEN, "forbidden access token"); ProviderType tokenProvider = getProviderByTopic(value); From 985ccf35cafc20c524477d21c86f3ba11cb84320 Mon Sep 17 00:00:00 2001 From: JaeSeo Yang <96044622+psychology50@users.noreply.github.com> Date: Mon, 12 Feb 2024 20:38:19 +0900 Subject: [PATCH 3/4] =?UTF-8?q?fix:=20#106=20Oauth=20Client=20&=20Config?= =?UTF-8?q?=20=EB=B9=88=20=EC=98=A4=EC=A3=BC=EC=9E=85=EB=90=98=EB=8D=98=20?= =?UTF-8?q?=ED=98=84=EC=83=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mapper/OauthApplicationConfigMapper.java | 32 +++++++++++++++ .../apis/oauth/mapper/OauthClientMapper.java | 41 +++++++++++++++++++ .../api/apis/oauth/usecase/OauthUseCase.java | 17 +++----- .../OauthApplicationConfig.java | 2 +- .../infra/client/oauth/OauthClientMapper.java | 22 ---------- .../oauth/dto/OIDCPublicKeyResponse.java | 7 ++++ .../OauthApplicationConfigMapper.java | 22 ---------- .../apple/AppleApplicationConfig.java | 2 +- .../google/GoogleApplicationConfig.java | 2 +- .../kakao/KakaoApplicationConfig.java | 2 +- .../InfraRedisConnectionFactory.java | 13 ++++++ .../infra/config/redis/CacheConfig.java | 25 +++++++++-- .../src/main/resources/application-infra.yml | 8 ++++ 13 files changed, 132 insertions(+), 63 deletions(-) create mode 100644 fitapet-app-external-api/src/main/java/kr/co/fitapet/api/apis/oauth/mapper/OauthApplicationConfigMapper.java create mode 100644 fitapet-app-external-api/src/main/java/kr/co/fitapet/api/apis/oauth/mapper/OauthClientMapper.java rename fitapet-infra/src/main/java/kr/co/fitapet/infra/client/oauth/{environment => }/OauthApplicationConfig.java (74%) delete mode 100644 fitapet-infra/src/main/java/kr/co/fitapet/infra/client/oauth/OauthClientMapper.java delete mode 100644 fitapet-infra/src/main/java/kr/co/fitapet/infra/client/oauth/environment/OauthApplicationConfigMapper.java create mode 100644 fitapet-infra/src/main/java/kr/co/fitapet/infra/common/annotation/InfraRedisConnectionFactory.java diff --git a/fitapet-app-external-api/src/main/java/kr/co/fitapet/api/apis/oauth/mapper/OauthApplicationConfigMapper.java b/fitapet-app-external-api/src/main/java/kr/co/fitapet/api/apis/oauth/mapper/OauthApplicationConfigMapper.java new file mode 100644 index 00000000..f3165df9 --- /dev/null +++ b/fitapet-app-external-api/src/main/java/kr/co/fitapet/api/apis/oauth/mapper/OauthApplicationConfigMapper.java @@ -0,0 +1,32 @@ +package kr.co.fitapet.api.apis.oauth.mapper; + +import kr.co.fitapet.infra.client.oauth.OauthApplicationConfig; +import kr.co.fitapet.infra.client.oauth.provider.apple.AppleApplicationConfig; +import kr.co.fitapet.infra.client.oauth.provider.google.GoogleApplicationConfig; +import kr.co.fitapet.infra.client.oauth.provider.kakao.KakaoApplicationConfig; +import kr.co.fitapet.infra.client.oauth.type.Provider; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +import java.util.Map; + +@Component +public class OauthApplicationConfigMapper { + private final Map oauthApplicationConfigMap; + + public OauthApplicationConfigMapper( + KakaoApplicationConfig kakaoApplicationConfig, + GoogleApplicationConfig googleApplicationConfig, + AppleApplicationConfig appleApplicationConfig + ) { + this.oauthApplicationConfigMap = Map.of( + Provider.KAKAO, kakaoApplicationConfig, + Provider.GOOGLE, googleApplicationConfig, + Provider.APPLE, appleApplicationConfig + ); + } + + public OauthApplicationConfig getOauthApplicationConfig(Provider provider) { + return oauthApplicationConfigMap.get(provider); + } +} diff --git a/fitapet-app-external-api/src/main/java/kr/co/fitapet/api/apis/oauth/mapper/OauthClientMapper.java b/fitapet-app-external-api/src/main/java/kr/co/fitapet/api/apis/oauth/mapper/OauthClientMapper.java new file mode 100644 index 00000000..f8ec668a --- /dev/null +++ b/fitapet-app-external-api/src/main/java/kr/co/fitapet/api/apis/oauth/mapper/OauthClientMapper.java @@ -0,0 +1,41 @@ +package kr.co.fitapet.api.apis.oauth.mapper; + +import kr.co.fitapet.infra.client.oauth.OauthApplicationConfig; +import kr.co.fitapet.infra.client.oauth.OauthClient; +import kr.co.fitapet.infra.client.oauth.dto.OIDCPublicKeyResponse; +import kr.co.fitapet.infra.client.oauth.provider.apple.AppleOauthClient; +import kr.co.fitapet.infra.client.oauth.provider.google.GoogleOauthClient; +import kr.co.fitapet.infra.client.oauth.provider.kakao.KakaoOauthClient; +import kr.co.fitapet.infra.client.oauth.type.Provider; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +import java.util.Map; + +@Component +public class OauthClientMapper { + private final Map oauthClientMap; + private final OauthApplicationConfigMapper oauthApplicationConfigMapper; + + public OauthClientMapper( + KakaoOauthClient kakaoOauthClient, + GoogleOauthClient googleOauthClient, + AppleOauthClient appleOauthClient, + OauthApplicationConfigMapper oauthApplicationConfigMapper + ) { + this.oauthClientMap = Map.of( + Provider.KAKAO, kakaoOauthClient, + Provider.GOOGLE, googleOauthClient, + Provider.APPLE, appleOauthClient + ); + this.oauthApplicationConfigMapper = oauthApplicationConfigMapper; + } + + public OIDCPublicKeyResponse getPublicKeyResponse(Provider provider) { + return oauthClientMap.get(provider).getOIDCPublicKey(); + } + + public OauthApplicationConfig getOauthApplicationConfig(Provider provider) { + return oauthApplicationConfigMapper.getOauthApplicationConfig(provider); + } +} diff --git a/fitapet-app-external-api/src/main/java/kr/co/fitapet/api/apis/oauth/usecase/OauthUseCase.java b/fitapet-app-external-api/src/main/java/kr/co/fitapet/api/apis/oauth/usecase/OauthUseCase.java index 421931ff..64e54996 100644 --- a/fitapet-app-external-api/src/main/java/kr/co/fitapet/api/apis/oauth/usecase/OauthUseCase.java +++ b/fitapet-app-external-api/src/main/java/kr/co/fitapet/api/apis/oauth/usecase/OauthUseCase.java @@ -14,12 +14,8 @@ import kr.co.fitapet.api.common.security.jwt.exception.AuthErrorException; import kr.co.fitapet.common.annotation.UseCase; import kr.co.fitapet.common.execption.GlobalErrorException; -import kr.co.fitapet.domain.common.redis.forbidden.ForbiddenTokenService; import kr.co.fitapet.domain.common.redis.oauth.OIDCTokenService; -import kr.co.fitapet.domain.common.redis.sms.provider.SmsRedisProvider; -import kr.co.fitapet.domain.common.redis.sms.qualify.SmsRegisterQualifier; import kr.co.fitapet.domain.common.redis.sms.type.SmsPrefix; -import kr.co.fitapet.domain.domains.member.domain.AccessToken; import kr.co.fitapet.domain.domains.member.domain.Member; import kr.co.fitapet.domain.domains.member.service.MemberSaveService; import kr.co.fitapet.domain.domains.member.service.MemberSearchService; @@ -30,11 +26,11 @@ import kr.co.fitapet.domain.domains.oauth.service.OauthSearchService; import kr.co.fitapet.domain.domains.oauth.type.ProviderType; import kr.co.fitapet.infra.client.oauth.OauthClient; -import kr.co.fitapet.infra.client.oauth.OauthClientMapper; +import kr.co.fitapet.api.apis.oauth.mapper.OauthClientMapper; import kr.co.fitapet.infra.client.oauth.dto.OIDCDecodePayload; import kr.co.fitapet.infra.client.oauth.dto.OIDCPublicKeyResponse; -import kr.co.fitapet.infra.client.oauth.environment.OauthApplicationConfig; -import kr.co.fitapet.infra.client.oauth.environment.OauthApplicationConfigMapper; +import kr.co.fitapet.infra.client.oauth.OauthApplicationConfig; +import kr.co.fitapet.api.apis.oauth.mapper.OauthApplicationConfigMapper; import kr.co.fitapet.infra.client.oauth.type.Provider; import kr.co.fitapet.infra.client.sms.snes.SmsProvider; import kr.co.fitapet.infra.client.sms.snes.dto.SnesDto; @@ -56,7 +52,6 @@ public class OauthUseCase { private final MemberSearchService memberSearchService; private final OauthOIDCHelper oauthOIDCHelper; - private final OauthApplicationConfigMapper oauthApplicationConfigMapper; private final OauthClientMapper oauthClientMapper; private final JwtMapper jwtMapper; @@ -110,7 +105,6 @@ public SmsRes sendCode(OauthSmsReq dto, ProviderType provider) { SnesDto.SensInfo smsInfo = smsProvider.sendCodeByPhoneNumber(SnesDto.Request.of(dto.to())); String key = makeTopic(dto.to(), provider); - smsRedisMapper.saveSmsAuthToken(key, smsInfo.code(), SmsPrefix.OAUTH); LocalDateTime expireTime = smsRedisMapper.getExpiredTime(key, SmsPrefix.OAUTH); log.info("인증번호 만료 시간: {}", expireTime); @@ -145,9 +139,8 @@ public Pair checkCertificationNumber(OauthSmsReq req, String id, Stri * idToken을 통해 payload를 가져온다. */ private OIDCDecodePayload getPayload(ProviderType provider, String idToken, String nonce) { - OauthClient oauthClient = oauthClientMapper.getOauthClient(Provider.valueOf(provider.name())); - OauthApplicationConfig oauthApplicationConfig = oauthApplicationConfigMapper.getOauthApplicationConfig(Provider.valueOf(provider.name())); - OIDCPublicKeyResponse oidcPublicKeyResponse = oauthClient.getOIDCPublicKey(); + OauthApplicationConfig oauthApplicationConfig = oauthClientMapper.getOauthApplicationConfig(Provider.valueOf(provider.name())); + OIDCPublicKeyResponse oidcPublicKeyResponse = oauthClientMapper.getPublicKeyResponse(Provider.valueOf(provider.name())); return oauthOIDCHelper.getPayloadFromIdToken( idToken, oauthApplicationConfig.getJwksUri(), diff --git a/fitapet-infra/src/main/java/kr/co/fitapet/infra/client/oauth/environment/OauthApplicationConfig.java b/fitapet-infra/src/main/java/kr/co/fitapet/infra/client/oauth/OauthApplicationConfig.java similarity index 74% rename from fitapet-infra/src/main/java/kr/co/fitapet/infra/client/oauth/environment/OauthApplicationConfig.java rename to fitapet-infra/src/main/java/kr/co/fitapet/infra/client/oauth/OauthApplicationConfig.java index 61a54b13..f0885afd 100644 --- a/fitapet-infra/src/main/java/kr/co/fitapet/infra/client/oauth/environment/OauthApplicationConfig.java +++ b/fitapet-infra/src/main/java/kr/co/fitapet/infra/client/oauth/OauthApplicationConfig.java @@ -1,4 +1,4 @@ -package kr.co.fitapet.infra.client.oauth.environment; +package kr.co.fitapet.infra.client.oauth; public interface OauthApplicationConfig { String getJwksUri(); diff --git a/fitapet-infra/src/main/java/kr/co/fitapet/infra/client/oauth/OauthClientMapper.java b/fitapet-infra/src/main/java/kr/co/fitapet/infra/client/oauth/OauthClientMapper.java deleted file mode 100644 index ac42959c..00000000 --- a/fitapet-infra/src/main/java/kr/co/fitapet/infra/client/oauth/OauthClientMapper.java +++ /dev/null @@ -1,22 +0,0 @@ -package kr.co.fitapet.infra.client.oauth; - -import kr.co.fitapet.infra.client.oauth.type.Provider; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Component; - -@Component -@RequiredArgsConstructor -public class OauthClientMapper { - private final OauthClient kakaoOauthClient; - private final OauthClient googleOauthClient; - private final OauthClient appleOauthClient; - - public OauthClient getOauthClient(Provider provider) { - return switch (provider) { - case KAKAO -> kakaoOauthClient; - case GOOGLE -> googleOauthClient; - case APPLE -> appleOauthClient; - default -> throw new IllegalArgumentException("Invalid provider type"); - }; - } -} diff --git a/fitapet-infra/src/main/java/kr/co/fitapet/infra/client/oauth/dto/OIDCPublicKeyResponse.java b/fitapet-infra/src/main/java/kr/co/fitapet/infra/client/oauth/dto/OIDCPublicKeyResponse.java index 5b1c197d..cc3f0288 100644 --- a/fitapet-infra/src/main/java/kr/co/fitapet/infra/client/oauth/dto/OIDCPublicKeyResponse.java +++ b/fitapet-infra/src/main/java/kr/co/fitapet/infra/client/oauth/dto/OIDCPublicKeyResponse.java @@ -9,4 +9,11 @@ @NoArgsConstructor public class OIDCPublicKeyResponse { List keys; + + @Override + public String toString() { + return "OIDCPublicKeyResponse{" + + "keys=" + keys + + '}'; + } } diff --git a/fitapet-infra/src/main/java/kr/co/fitapet/infra/client/oauth/environment/OauthApplicationConfigMapper.java b/fitapet-infra/src/main/java/kr/co/fitapet/infra/client/oauth/environment/OauthApplicationConfigMapper.java deleted file mode 100644 index 48e640d3..00000000 --- a/fitapet-infra/src/main/java/kr/co/fitapet/infra/client/oauth/environment/OauthApplicationConfigMapper.java +++ /dev/null @@ -1,22 +0,0 @@ -package kr.co.fitapet.infra.client.oauth.environment; - -import kr.co.fitapet.infra.client.oauth.type.Provider; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Component; - -@Component -@RequiredArgsConstructor -public class OauthApplicationConfigMapper { - private final OauthApplicationConfig kakaoApplicationConfig; - private final OauthApplicationConfig googleApplicationConfig; - private final OauthApplicationConfig appleApplicationConfig; - - public OauthApplicationConfig getOauthApplicationConfig(Provider provider) { - return switch (provider) { - case KAKAO -> kakaoApplicationConfig; - case GOOGLE -> googleApplicationConfig; - case APPLE -> appleApplicationConfig; - default -> throw new IllegalArgumentException("Invalid provider type"); - }; - } -} diff --git a/fitapet-infra/src/main/java/kr/co/fitapet/infra/client/oauth/provider/apple/AppleApplicationConfig.java b/fitapet-infra/src/main/java/kr/co/fitapet/infra/client/oauth/provider/apple/AppleApplicationConfig.java index de7c49c6..c4e6489e 100644 --- a/fitapet-infra/src/main/java/kr/co/fitapet/infra/client/oauth/provider/apple/AppleApplicationConfig.java +++ b/fitapet-infra/src/main/java/kr/co/fitapet/infra/client/oauth/provider/apple/AppleApplicationConfig.java @@ -1,6 +1,6 @@ package kr.co.fitapet.infra.client.oauth.provider.apple; -import kr.co.fitapet.infra.client.oauth.environment.OauthApplicationConfig; +import kr.co.fitapet.infra.client.oauth.OauthApplicationConfig; import lombok.Getter; import lombok.Setter; import org.springframework.boot.context.properties.ConfigurationProperties; diff --git a/fitapet-infra/src/main/java/kr/co/fitapet/infra/client/oauth/provider/google/GoogleApplicationConfig.java b/fitapet-infra/src/main/java/kr/co/fitapet/infra/client/oauth/provider/google/GoogleApplicationConfig.java index 8462f08b..6d513aae 100644 --- a/fitapet-infra/src/main/java/kr/co/fitapet/infra/client/oauth/provider/google/GoogleApplicationConfig.java +++ b/fitapet-infra/src/main/java/kr/co/fitapet/infra/client/oauth/provider/google/GoogleApplicationConfig.java @@ -1,6 +1,6 @@ package kr.co.fitapet.infra.client.oauth.provider.google; -import kr.co.fitapet.infra.client.oauth.environment.OauthApplicationConfig; +import kr.co.fitapet.infra.client.oauth.OauthApplicationConfig; import lombok.Getter; import lombok.Setter; import org.springframework.boot.context.properties.ConfigurationProperties; diff --git a/fitapet-infra/src/main/java/kr/co/fitapet/infra/client/oauth/provider/kakao/KakaoApplicationConfig.java b/fitapet-infra/src/main/java/kr/co/fitapet/infra/client/oauth/provider/kakao/KakaoApplicationConfig.java index e9dd9b48..98b7d953 100644 --- a/fitapet-infra/src/main/java/kr/co/fitapet/infra/client/oauth/provider/kakao/KakaoApplicationConfig.java +++ b/fitapet-infra/src/main/java/kr/co/fitapet/infra/client/oauth/provider/kakao/KakaoApplicationConfig.java @@ -1,6 +1,6 @@ package kr.co.fitapet.infra.client.oauth.provider.kakao; -import kr.co.fitapet.infra.client.oauth.environment.OauthApplicationConfig; +import kr.co.fitapet.infra.client.oauth.OauthApplicationConfig; import lombok.Getter; import lombok.Setter; import org.springframework.boot.context.properties.ConfigurationProperties; diff --git a/fitapet-infra/src/main/java/kr/co/fitapet/infra/common/annotation/InfraRedisConnectionFactory.java b/fitapet-infra/src/main/java/kr/co/fitapet/infra/common/annotation/InfraRedisConnectionFactory.java new file mode 100644 index 00000000..fcfba6cd --- /dev/null +++ b/fitapet-infra/src/main/java/kr/co/fitapet/infra/common/annotation/InfraRedisConnectionFactory.java @@ -0,0 +1,13 @@ +package kr.co.fitapet.infra.common.annotation; + +import org.springframework.beans.factory.annotation.Qualifier; + +import java.lang.annotation.*; + +@Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD, + ElementType.TYPE, ElementType.ANNOTATION_TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Qualifier("infraRedisConnectionFactory") +public @interface InfraRedisConnectionFactory { +} diff --git a/fitapet-infra/src/main/java/kr/co/fitapet/infra/config/redis/CacheConfig.java b/fitapet-infra/src/main/java/kr/co/fitapet/infra/config/redis/CacheConfig.java index 74762e79..5a9e604a 100644 --- a/fitapet-infra/src/main/java/kr/co/fitapet/infra/config/redis/CacheConfig.java +++ b/fitapet-infra/src/main/java/kr/co/fitapet/infra/config/redis/CacheConfig.java @@ -1,9 +1,11 @@ package kr.co.fitapet.infra.config.redis; +import kr.co.fitapet.infra.common.annotation.InfraRedisConnectionFactory; import kr.co.fitapet.infra.common.annotation.ManagerCacheManager; import kr.co.fitapet.infra.common.annotation.OidcCacheManager; import kr.co.fitapet.infra.common.annotation.SecurityUserCacheManager; import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Value; import org.springframework.cache.CacheManager; import org.springframework.cache.annotation.EnableCaching; import org.springframework.context.annotation.Bean; @@ -12,6 +14,9 @@ import org.springframework.data.redis.cache.RedisCacheConfiguration; import org.springframework.data.redis.cache.RedisCacheManager; import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.connection.RedisStandaloneConfiguration; +import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration; +import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.RedisSerializationContext; import org.springframework.data.redis.serializer.StringRedisSerializer; @@ -23,9 +28,23 @@ @RequiredArgsConstructor @EnableCaching public class CacheConfig { + @Value("${spring.data.redis.host}") + private String host; + @Value("${spring.data.redis.port}") + private int port; + + @Bean + @InfraRedisConnectionFactory + public RedisConnectionFactory infraRedisConnectionFactory() { + RedisStandaloneConfiguration config = new RedisStandaloneConfiguration(host, port); +// config.setPassword(); // redis 패스워드 설정 시, 주석 해제 + LettuceClientConfiguration clientConfig = LettuceClientConfiguration.builder().build(); + return new LettuceConnectionFactory(config, clientConfig); + } + @Bean @SecurityUserCacheManager - public CacheManager securityUserCacheManager(RedisConnectionFactory redisConnectionFactory) { + public CacheManager securityUserCacheManager(@InfraRedisConnectionFactory RedisConnectionFactory redisConnectionFactory) { RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig() .disableCachingNullValues() .entryTtl(Duration.ofMinutes(3)) @@ -47,7 +66,7 @@ public CacheManager securityUserCacheManager(RedisConnectionFactory redisConnect @Bean @ManagerCacheManager - public CacheManager managerCacheManager(RedisConnectionFactory redisConnectionFactory) { + public CacheManager managerCacheManager(@InfraRedisConnectionFactory RedisConnectionFactory redisConnectionFactory) { RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig() .disableCachingNullValues() .entryTtl(Duration.ofMinutes(3)) @@ -69,7 +88,7 @@ public CacheManager managerCacheManager(RedisConnectionFactory redisConnectionFa @Bean @OidcCacheManager - public CacheManager oidcCacheManger(RedisConnectionFactory cf) { + public CacheManager oidcCacheManger(@InfraRedisConnectionFactory RedisConnectionFactory cf) { RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig() .serializeKeysWith( RedisSerializationContext.SerializationPair.fromSerializer( diff --git a/fitapet-infra/src/main/resources/application-infra.yml b/fitapet-infra/src/main/resources/application-infra.yml index 3ff50f52..f9f93802 100644 --- a/fitapet-infra/src/main/resources/application-infra.yml +++ b/fitapet-infra/src/main/resources/application-infra.yml @@ -20,6 +20,10 @@ spring: readTimeout: 10000 loggerLevel: full + data.redis: + host: ${REDIS_HOST} + port: ${REDIS_PORT} + oauth2: client: provider: @@ -64,6 +68,10 @@ spring: readTimeout: 10000 loggerLevel: full + data.redis: + host: ${REDIS_HOST} + port: ${REDIS_PORT} + oauth2: client: provider: From 598f8e0a9051e775d5934123e2f16909b4b60e92 Mon Sep 17 00:00:00 2001 From: JaeSeo Yang <96044622+psychology50@users.noreply.github.com> Date: Mon, 12 Feb 2024 23:18:05 +0900 Subject: [PATCH 4/4] =?UTF-8?q?docs:=20multi=20module=20readme=20=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 12 ++++++++++++ fitapet-app-external-api/Readme.md | 1 + fitapet-common/Readme.md | 1 + fitapet-domain/Readme.md | 1 + fitapet-infra/Readme.md | 1 + 5 files changed, 16 insertions(+) create mode 100644 fitapet-app-external-api/Readme.md create mode 100644 fitapet-common/Readme.md create mode 100644 fitapet-domain/Readme.md create mode 100644 fitapet-infra/Readme.md diff --git a/README.md b/README.md index 23cdc746..7213af4c 100644 --- a/README.md +++ b/README.md @@ -95,6 +95,18 @@ - WAS Server 내부에 Nginx를 통해 Reverse Proxy를 구현했습니다. +## Multi Module Architecture + +
+ +- Multi Module Architecture를 적용하여 각 모듈별로 분리하여 개발하고, 빌드 및 배포를 진행하고 있습니다. +- 각 모듈별로 담당하는 역할을 분리하여 개발하고 있습니다. +- 각 모듈에 대한 Convention은 다음과 같습니다. + - [fitapet-common](https://github.com/KCY-Fit-a-Pet/fit-a-pet-server/tree/develop/fitapet-common/Readme.md) : 독립적인 오픈 소스로 배포 가능한 수준의 공통 모듈. 모든 모듈이 의존한다. + - [fitapet-infra](https://github.com/KCY-Fit-a-Pet/fit-a-pet-server/tree/develop/fitapet-infra/Readme.md) : 데이터베이스, 인프라스트럭처, 클라우드 서비스와 연동하는 모듈. common 모듈에 의존한다. + - [fitapet-domain](https://github.com/KCY-Fit-a-Pet/fit-a-pet-server/tree/develop/fitapet-domain/Readme.md) : 도메인 로직을 담당하며, repository를 보호하는 모듈. common 모듈에 의존한다. + - [fitapet-app-external-api](https://github.com/KCY-Fit-a-Pet/fit-a-pet-server/tree/develop/fitapet-app-external-api/Readme.md) : Client와 연동하는 외부 API를 담당하는 모듈. common, infra, domain 모든 모듈에 의존한다. + ## ERD
diff --git a/fitapet-app-external-api/Readme.md b/fitapet-app-external-api/Readme.md new file mode 100644 index 00000000..ac083b09 --- /dev/null +++ b/fitapet-app-external-api/Readme.md @@ -0,0 +1 @@ +## API diff --git a/fitapet-common/Readme.md b/fitapet-common/Readme.md new file mode 100644 index 00000000..77640360 --- /dev/null +++ b/fitapet-common/Readme.md @@ -0,0 +1 @@ +## Common \ No newline at end of file diff --git a/fitapet-domain/Readme.md b/fitapet-domain/Readme.md new file mode 100644 index 00000000..a4b91e24 --- /dev/null +++ b/fitapet-domain/Readme.md @@ -0,0 +1 @@ +## Domain \ No newline at end of file diff --git a/fitapet-infra/Readme.md b/fitapet-infra/Readme.md new file mode 100644 index 00000000..6ea57db8 --- /dev/null +++ b/fitapet-infra/Readme.md @@ -0,0 +1 @@ +## Infrastructure