Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
seogwoojin committed Nov 19, 2024
2 parents b0b17ce + 881ebb9 commit 76d8d11
Show file tree
Hide file tree
Showing 50 changed files with 689 additions and 409 deletions.
4 changes: 0 additions & 4 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,6 @@ REDIS_PORT=6379
JWT_ACCESS_SECRET=
JWT_REFRESH_SECRET=

ACCOUNT_API_URL=https://account.uoslife.com
ACCOUNT_API_ACCESS_ID=
ACCOUNT_API_ACCESS_SECRET=

PORTONE_API_IMP_KEY=
PORTONE_API_IMP_SECRET=

Expand Down
57 changes: 57 additions & 0 deletions .github/workflows/deploy_aws.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@

name: Deployment

run-name: Deploy by @${{ github.actor }}

on:
workflow_dispatch:

jobs:
build:

runs-on: ubuntu-latest
permissions:
contents: read

steps:
- uses: actions/checkout@v4
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'

- name: Cache Gradle Dependencies
uses: actions/cache@v4
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
restore-keys: |
${{ runner.os }}-gradle-
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4

- name: Build with Gradle Wrapper
run: ./gradlew build -x test

- name: Docker build & push to Docker hub
run: |
docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }}
docker build -t ${{ secrets.DOCKER_USERNAME }}/${{ secrets.DOCKER_REPO }}:0.1 .
docker push ${{ secrets.DOCKER_USERNAME }}/${{ secrets.DOCKER_REPO }}:0.1
- name: Docker image pull & Deploy to EC2
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.EC2_HOST }}
username: ubuntu
key: ${{ secrets.EC2_PRIVATE_KEY }}
script: |
sudo docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }}
sudo docker rm -f $(sudo docker ps -qa)
sudo docker rmi ${{ secrets.DOCKER_USERNAME }}/${{ secrets.DOCKER_REPO }}:0.1
sudo docker pull ${{ secrets.DOCKER_USERNAME }}/${{ secrets.DOCKER_REPO }}:0.1
sudo docker-compose up -d
sudo docker image prune -f
3 changes: 3 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,9 @@ dependencies {

implementation("me.paulschwarz:spring-dotenv:4.0.0")

// Thymeleaf
implementation("org.springframework.boot:spring-boot-starter-thymeleaf")

}

tasks.withType<KotlinCompile> {
Expand Down
47 changes: 30 additions & 17 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
version: '3'

services:
postgres:
image: postgres
restart: always
ports:
- "5432:5432"
container_name: postgres
environment:
POSTGRES_USER: server-meeting
POSTGRES_PASSWORD: server-meeting
POSTGRES_DB: server-meeting
# postgres:
# image: postgres
# restart: always
# ports:
# - "5432:5432"
# container_name: postgres
# environment:
# POSTGRES_USER: server-meeting
# POSTGRES_PASSWORD: server-meeting
# POSTGRES_DB: server-meeting

redis:
image: redis
Expand All @@ -21,15 +21,28 @@ services:
- "6379:6379"

application:
build:
context: .
dockerfile: Dockerfile
image: ${DOCKER_ID}/${DOCKER_IMAGE}:0.1
ports:
- "8081:8081"
environment:
SPRING_DATASOURCE_URL: jdbc:postgresql://postgres:5432/server-meeting?stringtype=unspecified
SPRING_DATASOURCE_USERNAME: server-meeting
SPRING_DATASOURCE_PASSWORD: server-meeting
SPRING_DATASOURCE_URL: ${DATABASE_URL}
SPRING_DATASOURCE_USERNAME: ${DATABASE_USERNAME}
SPRING_DATASOURCE_PASSWORD: ${DATABASE_PASSWORD}

REDIS_HOST: redis
REDIS_PORT: 6379

JWT_ACCESS_SECRET: ${JWT_ACCESS_SECRET}
JWT_REFRESH_SECRET: ${JWT_REFRESH_SECRET}

AWS_SES_ACCESS_KEY: ${AWS_SES_ACCESS_KEY}
AWS_SES_SECRET_KEY: ${AWS_SES_SECRET_KEY}

COOKIE_DOMAIN: ${COOKIE_DOMAIN}
COOKIE_SECURE: ${COOKIE_SECURE}

PORTONE_API_IMP_KEY: ${PORTONE_API_IMP_KEY}
PORTONE_API_IMP_SECRET: ${PORTONE_API_IMP_SECRET}

depends_on:
- postgres
- redis
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,13 @@ import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
import org.springframework.cloud.openfeign.EnableFeignClients
import org.springframework.data.jpa.repository.config.EnableJpaAuditing
import org.springframework.scheduling.annotation.EnableAsync

@EnableFeignClients @SpringBootApplication @EnableJpaAuditing class ServerMeetingApplication
@EnableFeignClients
@SpringBootApplication
@EnableJpaAuditing
@EnableAsync
class ServerMeetingApplication

fun main(args: Array<String>) {
runApplication<ServerMeetingApplication>(*args)
Expand Down
19 changes: 19 additions & 0 deletions src/main/kotlin/uoslife/servermeeting/global/config/AsyncConfig.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package uoslife.servermeeting.global.config

import java.util.concurrent.Executor
import org.springframework.context.annotation.Configuration
import org.springframework.scheduling.annotation.AsyncConfigurer
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor

@Configuration
class AsyncConfig : AsyncConfigurer {
override fun getAsyncExecutor(): Executor {
val executor = ThreadPoolTaskExecutor()
executor.corePoolSize = 5
executor.maxPoolSize = 10
executor.queueCapacity = 50
executor.setThreadNamePrefix("CustomAsyncExecutor-")
executor.initialize()
return executor
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@ import org.springframework.context.annotation.Configuration
import org.springframework.security.authentication.AuthenticationManager
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.config.annotation.web.builders.WebSecurity
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer
import org.springframework.security.config.http.SessionCreationPolicy
import org.springframework.security.web.SecurityFilterChain
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter
Expand Down Expand Up @@ -45,6 +43,17 @@ class SecurityConfig(
} // 인증, 인가가 되지 않은 요청 발생시
.authorizeHttpRequests {
it.requestMatchers(CorsUtils::isPreFlightRequest)
.permitAll()
.requestMatchers(
"/swagger-ui/**",
"/meeting/actuator/health/**",
"/api/user/isDuplicatedKakaoTalkId",
"/api/payment/refund/match",
"/api/payment/portone-webhook",
"/api/auth/reissue",
"/api/verification/send-email",
"/api/verification/verify-email"
) // 토큰 검사 미실시 리스트
.permitAll()
.requestMatchers("/api/**")
.hasRole("USER")
Expand Down Expand Up @@ -92,20 +101,4 @@ class SecurityConfig(
): AuthenticationManager {
return authConfig.authenticationManager
}

@Bean
fun webSecurityCustomizer(): WebSecurityCustomizer {
// 토큰 검사 미실시 리스트
return WebSecurityCustomizer { web: WebSecurity ->
web.ignoring()
.requestMatchers("/swagger-ui/**")
.requestMatchers("/meeting/actuator/health/**")
.requestMatchers("/api/user/isDuplicatedKakaoTalkId") // 카카오톡 중복 확인
.requestMatchers("/api/payment/refund/match")
.requestMatchers("/api/payment/portone-webhook")
.requestMatchers("/api/auth/reissue")
.requestMatchers("/api/verification/send-email")
.requestMatchers("/api/verification/verify-email")
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ enum class ErrorCode(val code: String, val message: String, var status: Int) {
"UserInfo is not completed.",
HttpStatus.PRECONDITION_FAILED.value()
),
USER_TEAM_NOT_FOUND("U11", "UserTeam is not Found.", HttpStatus.BAD_REQUEST.value()),

// User - Token
INVALID_TOKEN("T01", "Token is not valid.", HttpStatus.UNAUTHORIZED.value()),
Expand Down Expand Up @@ -85,6 +86,11 @@ enum class ErrorCode(val code: String, val message: String, var status: Int) {
"Only Team Leader Can Get Match.",
HttpStatus.BAD_REQUEST.value()
),
ONLY_TEAM_LEADER_CAN_CREATE_PAYMENT(
"M10",
"Only Team Leader can Create Payment.",
HttpStatus.BAD_REQUEST.value()
),

// External API
EXTERNAL_API_FAILED(
Expand All @@ -97,6 +103,11 @@ enum class ErrorCode(val code: String, val message: String, var status: Int) {
PAYMENT_NOT_FOUND("P01", "Payment is not Found.", HttpStatus.BAD_REQUEST.value()),
PAYMENT_INVALID("P03", "Payment is Invalid.", HttpStatus.BAD_REQUEST.value()),
USER_ALREADY_HAVE_PAYMENT("P04", "User already have Payment.", HttpStatus.BAD_REQUEST.value()),
PAYMENT_NOT_REFUND(
"P05",
"User Payment refund is not completed.",
HttpStatus.PRECONDITION_FAILED.value()
),

// Email Verification
EMAIL_INVALID_FORMAT("E01", "Invalid email format.", HttpStatus.BAD_REQUEST.value()),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ package uoslife.servermeeting.global.external
import org.springframework.cloud.openfeign.FeignClient
import org.springframework.web.bind.annotation.*
import uoslife.servermeeting.global.config.FeginClientConfig
import uoslife.servermeeting.meetingteam.dto.request.PortOneRequestDto
import uoslife.servermeeting.meetingteam.dto.response.PortOneResponseDto
import uoslife.servermeeting.payment.dto.request.PortOneRequestDto
import uoslife.servermeeting.payment.dto.response.PortOneResponseDto

@FeignClient(
name = "uoslife-meeting-payment-api",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,44 +1,44 @@
package uoslife.servermeeting.global.external

import feign.auth.BasicAuthRequestInterceptor
import org.springframework.beans.factory.annotation.Value
import org.springframework.cloud.openfeign.FeignClient
import org.springframework.context.annotation.Bean
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.RequestHeader
import org.springframework.web.bind.annotation.RequestParam

@FeignClient(name = "uoslife-account-api-user", url = "\${account.url}")
interface UserClient {
@GetMapping("/v1/users/me")
fun getAuthenticatedUserProfile(
@RequestHeader("Authorization") token: String,
): UOSLIFEUserProfileResponse?
}

@FeignClient(
name = "uoslife-account-api-server",
url = "\${account.url}",
configuration = [ServerClient.BasicAuthConfiguration::class]
)
interface ServerClient {
@GetMapping("/v1/users/{userId}")
fun getUserProfile(@PathVariable("userId") userId: Long): UOSLIFEUserProfileResponse

@GetMapping("/v1/users/{userId}/devices")
fun getUserDevices(@PathVariable("userId") userId: Long): List<UOSLIFEUserDeviceResponse>

@GetMapping("/v1/push-tokens")
fun getUserPushTokens(@RequestParam("userIds") userId: List<Long>): List<String>

class BasicAuthConfiguration(
@Value("\${account.access.id}") private val accessKeyId: String,
@Value("\${account.access.secret}") private val accessKeySecret: String,
) {
@Bean
fun basicAuthRequestInterceptor(): BasicAuthRequestInterceptor {
return BasicAuthRequestInterceptor(accessKeyId, accessKeySecret)
}
}
}
// package uoslife.servermeeting.global.external
//
// import feign.auth.BasicAuthRequestInterceptor
// import org.springframework.beans.factory.annotation.Value
// import org.springframework.cloud.openfeign.FeignClient
// import org.springframework.context.annotation.Bean
// import org.springframework.web.bind.annotation.GetMapping
// import org.springframework.web.bind.annotation.PathVariable
// import org.springframework.web.bind.annotation.RequestHeader
// import org.springframework.web.bind.annotation.RequestParam
//
// @FeignClient(name = "uoslife-account-api-user", url = "\${account.url}")
// interface UserClient {
// @GetMapping("/v1/users/me")
// fun getAuthenticatedUserProfile(
// @RequestHeader("Authorization") token: String,
// ): UOSLIFEUserProfileResponse?
// }
//
// @FeignClient(
// name = "uoslife-account-api-server",
// url = "\${account.url}",
// configuration = [ServerClient.BasicAuthConfiguration::class]
// )
// interface ServerClient {
// @GetMapping("/v1/users/{userId}")
// fun getUserProfile(@PathVariable("userId") userId: Long): UOSLIFEUserProfileResponse
//
// @GetMapping("/v1/users/{userId}/devices")
// fun getUserDevices(@PathVariable("userId") userId: Long): List<UOSLIFEUserDeviceResponse>
//
// @GetMapping("/v1/push-tokens")
// fun getUserPushTokens(@RequestParam("userIds") userId: List<Long>): List<String>
//
// class BasicAuthConfiguration(
// @Value("\${account.access.id}") private val accessKeyId: String,
// @Value("\${account.access.secret}") private val accessKeySecret: String,
// ) {
// @Bean
// fun basicAuthRequestInterceptor(): BasicAuthRequestInterceptor {
// return BasicAuthRequestInterceptor(accessKeyId, accessKeySecret)
// }
// }
// }
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import uoslife.servermeeting.match.dto.response.MatchInformationResponse
import uoslife.servermeeting.match.dto.response.MatchedMeetingTeamInformationGetResponse
import uoslife.servermeeting.match.entity.Match
import uoslife.servermeeting.match.exception.MatchNotFoundException
import uoslife.servermeeting.meetingteam.dao.UserTeamDao
import uoslife.servermeeting.meetingteam.entity.MeetingTeam
import uoslife.servermeeting.meetingteam.entity.enums.TeamType
import uoslife.servermeeting.meetingteam.entity.enums.TeamType.SINGLE
Expand All @@ -25,14 +26,15 @@ import uoslife.servermeeting.user.exception.UserNotFoundException
@Transactional(readOnly = true)
class MatchingService(
private val matchedDao: MatchedDao,
private val userTeamDao: UserTeamDao,
private val userDao: UserDao,
private val singleMeetingService: SingleMeetingService,
private val tripleMeetingService: TripleMeetingService,
) {
@Transactional
fun getMatchedMeetingTeamByType(userId: Long, teamType: TeamType): MatchInformationResponse {
val userTeam =
userDao.findUserWithMeetingTeam(userId, teamType) ?: throw UserNotFoundException()
userTeamDao.findUserWithMeetingTeam(userId, teamType) ?: throw UserNotFoundException()
val meetingTeam = userTeam.team ?: throw MeetingTeamNotFoundException()

if (!userTeam.isLeader) throw OnlyTeamLeaderCanGetMatchException()
Expand Down
Loading

0 comments on commit 76d8d11

Please sign in to comment.