-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
183 changed files
with
6,170 additions
and
1,297 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
name: Backend Develop Deploy (CD) | ||
|
||
on: | ||
workflow_dispatch: | ||
inputs: | ||
branch: | ||
description: 'Branch Name' | ||
required: true | ||
|
||
jobs: | ||
build: | ||
name: Backend Deploy | ||
runs-on: shook-runner | ||
|
||
steps: | ||
- name: Log pwd | ||
shell: bash | ||
run: pwd | ||
- name: Log Branch Name | ||
shell: bash | ||
run: echo "${{ github.event.inputs.branch }}" | ||
- name: Deploy | ||
shell: bash | ||
run: bash /home/ubuntu/deploy.sh ${{ github.event.inputs.branch }} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
name: Frontend CI | ||
|
||
on: | ||
pull_request: | ||
branches: [main] | ||
types: [opened, synchronize, reopened] | ||
|
||
jobs: | ||
test: | ||
runs-on: ubuntu-latest | ||
env: | ||
working-directory: ./frontend | ||
node-version: '18.16.1' | ||
|
||
steps: | ||
- name: Checkout | ||
uses: actions/checkout@v3 | ||
|
||
- name: Use Node.js ${{ env.node-version }} | ||
uses: actions/setup-node@v3 | ||
with: | ||
node-version: ${{ env.node-version }} | ||
|
||
- name: Cache dependencies | ||
id: cache | ||
uses: actions/cache@v3 | ||
with: | ||
path: '**/node_modules' | ||
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} | ||
restore-keys: | | ||
${{ runner.os }}-node- | ||
- name: Install Dependencies | ||
if: steps.cache.outputs.cache-hit != 'true' | ||
run: npm ci | ||
working-directory: ${{ env.working-directory }} | ||
|
||
- name: ESLint | ||
if: always() | ||
run: npm run lint | ||
working-directory: ${{ env.working-directory }} | ||
|
||
- name: Test | ||
if: always() | ||
run: npm test | ||
working-directory: ${{ env.working-directory }} | ||
|
||
- name: Build | ||
if: always() | ||
run: npm run build | ||
working-directory: ${{ env.working-directory }} | ||
|
||
- name: Notify slack on CI fail | ||
if: failure() | ||
uses: 8398a7/action-slack@v3 | ||
with: | ||
status: ${{ job.status }} | ||
author_name: 프론트엔드 테스트 실패 알림 | ||
fields: repo, message, commit, author, action, eventName, ref, workflow, job, took | ||
env: | ||
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
71 changes: 71 additions & 0 deletions
71
backend/src/main/java/shook/shook/auth/jwt/application/TokenProvider.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
package shook.shook.auth.jwt.application; | ||
|
||
import io.jsonwebtoken.Claims; | ||
import io.jsonwebtoken.ExpiredJwtException; | ||
import io.jsonwebtoken.Jwts; | ||
import io.jsonwebtoken.MalformedJwtException; | ||
import io.jsonwebtoken.SignatureAlgorithm; | ||
import io.jsonwebtoken.security.Keys; | ||
import java.security.Key; | ||
import java.util.Base64; | ||
import java.util.Date; | ||
import org.springframework.beans.factory.annotation.Value; | ||
import org.springframework.stereotype.Component; | ||
import shook.shook.auth.jwt.exception.TokenException; | ||
|
||
@Component | ||
public class TokenProvider { | ||
|
||
private final long accessTokenValidTime; | ||
private final long refreshTokenValidTime; | ||
private final Key secretKey; | ||
|
||
public TokenProvider( | ||
@Value("${jwt.access-token-valid-time}") final long accessTokenValidTime, | ||
@Value("${jwt.refresh-token-valid-time}") final long refreshTokenValidTime, | ||
@Value("${jwt.secret-code}") final String secretCode | ||
) { | ||
this.accessTokenValidTime = accessTokenValidTime; | ||
this.refreshTokenValidTime = refreshTokenValidTime; | ||
this.secretKey = generateSecretKey(secretCode); | ||
} | ||
|
||
private Key generateSecretKey(final String secretCode) { | ||
final String encodedSecretCode = Base64.getEncoder().encodeToString(secretCode.getBytes()); | ||
return Keys.hmacShaKeyFor(encodedSecretCode.getBytes()); | ||
} | ||
|
||
public String createAccessToken(final long memberId) { | ||
return createToken(memberId, accessTokenValidTime); | ||
} | ||
|
||
public String createRefreshToken(final long memberId) { | ||
return createToken(memberId, refreshTokenValidTime); | ||
} | ||
|
||
private String createToken(final long memberId, final long validTime) { | ||
final Claims claims = Jwts.claims().setSubject("user"); | ||
claims.put("memberId", memberId); | ||
Date now = new Date(); | ||
return Jwts.builder() | ||
.setClaims(claims) | ||
.setIssuedAt(now) | ||
.setExpiration(new Date(now.getTime() + validTime)) | ||
.signWith(secretKey, SignatureAlgorithm.HS256) | ||
.compact(); | ||
} | ||
|
||
public Claims parseClaims(final String token) { | ||
try { | ||
return Jwts.parserBuilder() | ||
.setSigningKey(secretKey) | ||
.build() | ||
.parseClaimsJws(token) | ||
.getBody(); | ||
} catch (MalformedJwtException e) { | ||
throw new TokenException.NotIssuedTokenException(); | ||
} catch (ExpiredJwtException e) { | ||
throw new TokenException.ExpiredTokenException(); | ||
} | ||
} | ||
} |
18 changes: 18 additions & 0 deletions
18
backend/src/main/java/shook/shook/auth/jwt/exception/TokenException.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
package shook.shook.auth.jwt.exception; | ||
|
||
public class TokenException extends RuntimeException { | ||
|
||
public static class NotIssuedTokenException extends TokenException { | ||
|
||
public NotIssuedTokenException() { | ||
super(); | ||
} | ||
} | ||
|
||
public static class ExpiredTokenException extends TokenException { | ||
|
||
public ExpiredTokenException() { | ||
super(); | ||
} | ||
} | ||
} |
89 changes: 89 additions & 0 deletions
89
backend/src/main/java/shook/shook/auth/oauth/application/GoogleInfoProvider.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
package shook.shook.auth.oauth.application; | ||
|
||
import java.util.Objects; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.beans.factory.annotation.Value; | ||
import org.springframework.http.HttpEntity; | ||
import org.springframework.http.HttpHeaders; | ||
import org.springframework.http.HttpMethod; | ||
import org.springframework.stereotype.Component; | ||
import org.springframework.web.client.HttpClientErrorException; | ||
import org.springframework.web.client.HttpServerErrorException; | ||
import org.springframework.web.client.RestTemplate; | ||
import shook.shook.auth.oauth.application.dto.GoogleAccessTokenRequest; | ||
import shook.shook.auth.oauth.application.dto.GoogleAccessTokenResponse; | ||
import shook.shook.auth.oauth.application.dto.GoogleMemberInfoResponse; | ||
import shook.shook.auth.oauth.exception.OAuthException; | ||
|
||
@RequiredArgsConstructor | ||
@Component | ||
public class GoogleInfoProvider { | ||
|
||
private static final String TOKEN_PREFIX = "Bearer "; | ||
private static final String GRANT_TYPE = "authorization_code"; | ||
private static final String AUTHORIZATION_HEADER = "Authorization"; | ||
|
||
@Value("${oauth2.google.access-token-url}") | ||
private String GOOGLE_ACCESS_TOKEN_URL; | ||
|
||
@Value("${oauth2.google.member-info-url}") | ||
private String GOOGLE_MEMBER_INFO_URL; | ||
|
||
@Value("${oauth2.google.client-id}") | ||
private String GOOGLE_CLIENT_ID; | ||
|
||
@Value("${oauth2.google.client-secret}") | ||
private String GOOGLE_CLIENT_SECRET; | ||
|
||
@Value("${oauth2.google.redirect-uri}") | ||
private String LOGIN_REDIRECT_URL; | ||
|
||
private final RestTemplate restTemplate; | ||
|
||
public GoogleMemberInfoResponse getMemberInfo(final String accessToken) { | ||
try { | ||
final HttpHeaders headers = new HttpHeaders(); | ||
headers.set(AUTHORIZATION_HEADER, TOKEN_PREFIX + accessToken); | ||
final HttpEntity<Object> request = new HttpEntity<>(headers); | ||
|
||
final GoogleMemberInfoResponse responseEntity = restTemplate.exchange( | ||
GOOGLE_MEMBER_INFO_URL, | ||
HttpMethod.GET, | ||
request, | ||
GoogleMemberInfoResponse.class).getBody(); | ||
|
||
if (!Objects.requireNonNull(responseEntity).isVerifiedEmail()) { | ||
throw new OAuthException.InvalidEmailException(); | ||
} | ||
|
||
return responseEntity; | ||
} catch (HttpClientErrorException e) { | ||
throw new OAuthException.InvalidAccessTokenException(); | ||
} catch (HttpServerErrorException e) { | ||
throw new OAuthException.GoogleServerException(); | ||
} | ||
} | ||
|
||
public GoogleAccessTokenResponse getAccessToken(final String authorizationCode) { | ||
try { | ||
final GoogleAccessTokenRequest googleAccessTokenRequest = new GoogleAccessTokenRequest( | ||
authorizationCode, | ||
GOOGLE_CLIENT_ID, | ||
GOOGLE_CLIENT_SECRET, | ||
LOGIN_REDIRECT_URL, | ||
GRANT_TYPE); | ||
final HttpEntity<GoogleAccessTokenRequest> request = new HttpEntity<>( | ||
googleAccessTokenRequest); | ||
|
||
return Objects.requireNonNull(restTemplate.postForEntity( | ||
GOOGLE_ACCESS_TOKEN_URL, | ||
request, | ||
GoogleAccessTokenResponse.class).getBody()); | ||
|
||
} catch (HttpClientErrorException e) { | ||
throw new OAuthException.InvalidAuthorizationCodeException(); | ||
} catch (HttpServerErrorException e) { | ||
throw new OAuthException.GoogleServerException(); | ||
} | ||
} | ||
} |
36 changes: 36 additions & 0 deletions
36
backend/src/main/java/shook/shook/auth/oauth/application/OAuthService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
package shook.shook.auth.oauth.application; | ||
|
||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.stereotype.Service; | ||
import shook.shook.auth.jwt.application.TokenProvider; | ||
import shook.shook.auth.oauth.application.dto.GoogleAccessTokenResponse; | ||
import shook.shook.auth.oauth.application.dto.GoogleMemberInfoResponse; | ||
import shook.shook.auth.oauth.application.dto.LoginResponse; | ||
import shook.shook.member.application.MemberService; | ||
import shook.shook.member.domain.Email; | ||
import shook.shook.member.domain.Member; | ||
|
||
@RequiredArgsConstructor | ||
@Service | ||
public class OAuthService { | ||
|
||
private final MemberService memberService; | ||
private final GoogleInfoProvider googleInfoProvider; | ||
private final TokenProvider tokenProvider; | ||
|
||
public LoginResponse login(final String accessCode) { | ||
final GoogleAccessTokenResponse accessTokenResponse = | ||
googleInfoProvider.getAccessToken(accessCode); | ||
final GoogleMemberInfoResponse memberInfo = googleInfoProvider | ||
.getMemberInfo(accessTokenResponse.getAccessToken()); | ||
|
||
final String userEmail = memberInfo.getEmail(); | ||
final Member member = memberService.findByEmail(new Email(userEmail)) | ||
.orElseGet(() -> memberService.register(userEmail)); | ||
|
||
final long memberId = member.getId(); | ||
final String accessToken = tokenProvider.createAccessToken(memberId); | ||
final String refreshToken = tokenProvider.createRefreshToken(memberId); | ||
return new LoginResponse(accessToken, refreshToken); | ||
} | ||
} |
18 changes: 18 additions & 0 deletions
18
backend/src/main/java/shook/shook/auth/oauth/application/dto/GoogleAccessTokenRequest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
package shook.shook.auth.oauth.application.dto; | ||
|
||
import lombok.AccessLevel; | ||
import lombok.AllArgsConstructor; | ||
import lombok.Getter; | ||
import lombok.NoArgsConstructor; | ||
|
||
@NoArgsConstructor(access = AccessLevel.PRIVATE) | ||
@AllArgsConstructor | ||
@Getter | ||
public class GoogleAccessTokenRequest { | ||
|
||
private String code; | ||
private String clientId; | ||
private String clientSecret; | ||
private String redirectUri; | ||
private String grantType; | ||
} |
Oops, something went wrong.