Skip to content

Commit

Permalink
Merge branch 'prgrms-be-devcourse:main' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
Preta3418 authored Nov 5, 2024
2 parents d33c17b + 7ec0da0 commit 1503876
Show file tree
Hide file tree
Showing 29 changed files with 384 additions and 209 deletions.
149 changes: 97 additions & 52 deletions .github/workflows/CICD.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Java & React CI/CD
name: Backend CI/CD Pipeline

on:
push:
Expand All @@ -14,43 +14,49 @@ jobs:
contents: read

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

# application-prod.properties 암호화
- name: add secrets into properties
run: |
echo "${{ secrets.APPLICATION_PROD }}" | base64 --decode >> ./src/main/resources/application-prod.properties
# 환경 설정
- name: Setup Gradle
uses: gradle/actions/setup-gradle@af1da67850ed9a4cedd57bfd976089dd991e2582

# 실행 권한 부여
- name: Grant execute permission for Gradlew
run: chmod +x gradlew

# 프로젝트 빌드 (clean bootJar로 빌드만 수행)
- name: Build with Gradle Wrapper
run: ./gradlew build

# Docker Hub 로그인
- name: Log in to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}

# Docker 이미지 빌드 및 푸시
- name: Build and push Docker image
run: |
docker build -t ${{ secrets.DOCKER_USERNAME }}/bitta-kotlin:latest .
docker push ${{ secrets.DOCKER_USERNAME }}/bitta-kotlin:latest
- uses: actions/checkout@v4

# JDK 설정
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'

# application-prod.properties 암호화
- name: Add secrets into properties
run: |
echo "${{ secrets.APPLICATION_PROD }}" | base64 --decode > ./src/main/resources/application-prod.properties
# Gradle 설정
- name: Setup Gradle
uses: gradle/gradle-build-action@v2

# Gradlew 실행 권한 부여
- name: Grant execute permission for Gradlew
run: chmod +x gradlew

# 프로젝트 빌드
- name: Build with Gradle Wrapper
run: ./gradlew build

# Docker Hub 로그인
- name: Log in to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}

# Docker 이미지 빌드 및 푸시
- name: Build and push Docker image
run: |
docker build -t ${{ secrets.DOCKER_USERNAME }}/bitta-kotlin:latest .
docker push ${{ secrets.DOCKER_USERNAME }}/bitta-kotlin:latest
# application-prod.properties 빌드 후 제거 - 안전성 확보
- name: Clean up application-prod.properties
run: rm ./src/main/resources/application-prod.properties

deploy:
if: github.repository == 'prgrms-be-devcourse/NBB1_2_3_Team10' && github.ref == 'refs/heads/release'
needs: build
Expand All @@ -60,22 +66,61 @@ jobs:

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

# EC2 서버 배포
- name: Deploy to EC2
uses: appleboy/[email protected]
with:
host: ${{ secrets.EC2_HOST }}
username: ${{ secrets.EC2_USER }}
key: ${{ secrets.EC2_SSH_KEY }}
script: |
echo "${{ secrets.DOCKER_PASSWORD }}" | docker login -u "${{ secrets.DOCKER_USERNAME }}" --password-stdin
docker pull ${{ secrets.DOCKER_USERNAME }}/bitta-kotlin:latest
docker stop my-container || true
docker rm my-container || true
docker run -d --name my-container -p 80:80 ${{ secrets.DOCKER_USERNAME }}/bitta-kotlin:latest
host: ${{ secrets.EC2_HOST }}
username: ${{ secrets.EC2_USER }}
key: ${{ secrets.EC2_SSH_KEY }}
script: |
echo "${{ secrets.DOCKER_PASSWORD }}" | docker login -u "${{ secrets.DOCKER_USERNAME }}" --password-stdin
docker pull ${{ secrets.DOCKER_USERNAME }}/bitta-kotlin:latest
# Blue-Green 배포: blue와 green 컨테이너 전환
CURRENT_CONTAINER=$(docker ps --filter "name=backend-blue" -q)
TARGET_CONTAINER="backend-green"
[ -z "$CURRENT_CONTAINER" ] && TARGET_CONTAINER="backend-blue"
# 새 컨테이너 실행
docker stop $TARGET_CONTAINER || true
docker rm $TARGET_CONTAINER || true
# 조건문을 사용해 컨테이너에 맞는 포트로 실행
if [ "$TARGET_CONTAINER" == "backend-blue" ]; then
docker run -d --name backend-blue -p 8081:8080 \
-e SPRING_PROFILES_ACTIVE=prod \
-e AWS_ACCESS_KEY_ID=${{ secrets.AWS_ACCESS_KEY_ID }} \
-e AWS_SECRET_ACCESS_KEY=${{ secrets.AWS_SECRET_ACCESS_KEY }} \
${{ secrets.DOCKER_USERNAME }}/bitta-kotlin:latest
else
docker run -d --name backend-green -p 8082:8080 \
-e SPRING_PROFILES_ACTIVE=prod \
-e AWS_ACCESS_KEY_ID=${{ secrets.AWS_ACCESS_KEY_ID }} \
-e AWS_SECRET_ACCESS_KEY=${{ secrets.AWS_SECRET_ACCESS_KEY }} \
${{ secrets.DOCKER_USERNAME }}/bitta-kotlin:latest
fi
# Nginx 리로드하여 트래픽 전환
sudo systemctl reload nginx
# Blue-Green 설정 Docker Compose 실행
- name: Deploy with Docker Compose on EC2
uses: appleboy/[email protected]
with:
host: ${{ secrets.EC2_HOST }}
username: ${{ secrets.EC2_USER }}
key: ${{ secrets.EC2_SSH_KEY }}
script: |
cd ~/bitta-project
docker-compose down
docker-compose up -d --no-deps
sudo nginx -s reload
# 테스트 단계
- name: Test Frontend Response
run: curl --fail http://${{ secrets.EC2_HOST }} || exit 1

- name: Test Backend Response
run: curl --fail http://${{ secrets.EC2_HOST }}/api/ || exit 1
17 changes: 6 additions & 11 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,17 +1,12 @@
# Use an official Gradle image to build the backend
FROM gradle:7.5.1-jdk11 AS build
# Use OpenJDK 17 image
FROM openjdk:17-jdk-alpine

# Set working directory
WORKDIR /app

# Copy and build the application
COPY . .
RUN gradle build -x test

# Use a lightweight JRE image for runtime
FROM openjdk:11-jre-slim
COPY --from=build /app/build/libs/*.jar /app/app.jar
# Copy the built jar file
COPY build/libs/*.jar /app.jar

# Set the entry point to run the application
EXPOSE 8080
CMD ["java", "-jar", "-Dspring.profiles.active=prod", "/app.jar"]

CMD ["java", "-jar", "-Dspring.profiles.active=prod", "/app.jar"]
30 changes: 20 additions & 10 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,19 +1,29 @@
version: '3'
services:
frontend-blue:
image: publicdeveh/bitta-front:latest
ports:
- "3001:3000"
environment:
- NODE_ENV=production

frontend-green:
image: publicdeveh/bitta-front:latest
ports:
- "3002:3000"
environment:
- NODE_ENV=production

backend-blue:
build:
context: .
dockerfile: ./Dockerfile
image: publicdeveh/bitta-kotlin:latest
ports:
- "8081:8080"
- "8081:8000"
environment:
- SPRING_PROFILES_ACTIVE=production
- SPRING_PROFILES_ACTIVE=prod

backend-green:
build:
context: .
dockerfile: ./Dockerfile
image: publicdeveh/bitta-kotlin:latest
ports:
- "8082:8080"
- "8082:8000"
environment:
- SPRING_PROFILES_ACTIVE=production
- SPRING_PROFILES_ACTIVE=prod
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,12 @@ package org.tenten.bittakotlin

import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
import org.springframework.data.web.config.EnableSpringDataWebSupport
import org.springframework.scheduling.annotation.EnableScheduling

@SpringBootApplication
@EnableScheduling
@EnableSpringDataWebSupport(pageSerializationMode = EnableSpringDataWebSupport.PageSerializationMode.VIA_DTO)
class BittaKotlinApplication

fun main(args: Array<String>) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -231,5 +231,12 @@ class ApplyController(
applyService.applyStatusUpdate(applyId, applyStatusUpdateDTO.applyStatus!!, profileId)
return ResponseEntity.ok("상태가 변경되었습니다")
}

@PostMapping("/calendar")
fun applyToCalendar(@RequestParam jobPostId: Long, @RequestParam profileId: Long): ResponseEntity<String> {
applyService.applyToCalendar(jobPostId, profileId)
return ResponseEntity.ok("캘린더가 등록되었습니다")
}

}

Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package org.tenten.bittakotlin.apply.service

import org.tenten.bittakotlin.apply.dto.ApplyDTO
import org.tenten.bittakotlin.apply.entity.Apply
import org.tenten.bittakotlin.apply.entity.ApplyStatus
import org.tenten.bittakotlin.calendar.entity.EventCalendar
import org.tenten.bittakotlin.profile.entity.Profile

interface ApplyService {
Expand All @@ -20,6 +22,12 @@ interface ApplyService {
fun getApplyCount(jobPostId: Long): Long

fun applyStatusUpdate(applyId: Long, applyStatus: ApplyStatus, profileId: Long)

fun setCalendar(apply: Apply?)

fun getCalendar(profileId: Long?): List<EventCalendar?>?

fun applyToCalendar(jobPostId: Long?, profileId: Long?)
}


Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,21 @@ import org.tenten.bittakotlin.apply.entity.Apply
import org.tenten.bittakotlin.apply.entity.ApplyStatus
import org.tenten.bittakotlin.apply.exception.ApplyException
import org.tenten.bittakotlin.apply.repository.ApplyRepository
import org.tenten.bittakotlin.calendar.entity.EventCalendar
import org.tenten.bittakotlin.calendar.repository.EventCalendarRepository
import org.tenten.bittakotlin.jobpost.exception.JobPostException
import org.tenten.bittakotlin.jobpost.repository.JobPostRepository
import org.tenten.bittakotlin.jobpost.util.JobPostProvider
import org.tenten.bittakotlin.profile.entity.Profile
import org.tenten.bittakotlin.profile.service.ProfileProvider

@Service
class ApplyServiceImpl(
private val applyRepository: ApplyRepository,
private val jobPostRepository: JobPostRepository,
// private val memberProvider: MemberProvider,
private val jobPostProvider: JobPostProvider
private val profileProvider: ProfileProvider,
private val jobPostProvider: JobPostProvider,
private val eventCalendarRepository: EventCalendarRepository
) : ApplyService {

private val log = LoggerFactory.getLogger(this::class.java)
Expand All @@ -39,6 +43,8 @@ class ApplyServiceImpl(
jobPost!!.plusApplyCount()
jobPostRepository.save(jobPost)

addCalendar(apply)

mapOf(
"message" to "${apply.profile!!.nickname}님 지원 완료",
"data" to entityToDto(apply)
Expand All @@ -49,6 +55,18 @@ class ApplyServiceImpl(
}
}

private fun addCalendar(apply: Apply) {
val jobPost = apply.jobPost
val event = EventCalendar(
profile = apply.profile,
title = jobPost!!.title,
startDate = jobPost.startDate,
endDate = jobPost.endDate,
auditionDate = jobPost.auditionDate
)
eventCalendarRepository.save(event)
}

@Transactional
override fun delete(id: Long) {
val apply = applyRepository.findById(id).orElseThrow { ApplyException.NOT_FOUND.get() }
Expand Down Expand Up @@ -104,10 +122,30 @@ class ApplyServiceImpl(
applyRepository.save(apply)
}

override fun setCalendar(apply: Apply?) {
val event = EventCalendar(
profile = apply!!.profile,
title = apply.jobPost!!.title,
startDate = apply.jobPost!!.startDate,
endDate = apply.jobPost!!.endDate,
auditionDate = apply.jobPost!!.auditionDate
)
eventCalendarRepository.save(event)
}

override fun getCalendar(profileId: Long?): List<EventCalendar?>? {
return eventCalendarRepository.findAllByProfileId(profileId!!)
}

@Transactional
override fun applyToCalendar(jobPostId: Long?, profileId: Long?) {
val apply = Apply()
}

private fun dtoToEntity(applyDTO: ApplyDTO): Apply {
return Apply(
id = applyDTO.id,
// profile = profileProvider.getById(applyDTO.profileId!!),
profile = profileProvider.getById(applyDTO.profileId!!),
jobPost = jobPostProvider.getById(applyDTO.jobPostId!!),
appliedAt = applyDTO.appliedAt
)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package org.tenten.bittakotlin.calendar.controller

import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
import org.tenten.bittakotlin.calendar.dto.EventCalendarDTO
import org.tenten.bittakotlin.calendar.service.EventCalendarService

@RestController
@RequestMapping("/api/v1/calendar")
class EventCalendarController(
private val eventCalendarService: EventCalendarService
) {

@GetMapping("/{profileId}")
fun getCalendar(@PathVariable profileId: Long): ResponseEntity<List<EventCalendarDTO>> {
val events = eventCalendarService.getEventCalendar(profileId)
return ResponseEntity.ok(events)
}
}
Loading

0 comments on commit 1503876

Please sign in to comment.