Skip to content

Commit

Permalink
feat: deploy workflow
Browse files Browse the repository at this point in the history
  • Loading branch information
alstn113 committed Dec 9, 2024
1 parent f409557 commit cfe1f01
Show file tree
Hide file tree
Showing 63 changed files with 1,735 additions and 1,037 deletions.
97 changes: 97 additions & 0 deletions .github/workflows/server_cd.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
name: Server CD

on:
push:
branches:
- main
paths:
- 'server/**'

env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}/server

jobs:
build:
name: Build
runs-on: ubuntu-latest

permissions:
contents: read
packages: write

defaults:
run:
working-directory: server

steps:
- name: Checkout Repository
uses: actions/checkout@v4

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

- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4

- name: Build with Gradle
run: ./gradlew clean bootJar

- name: Log in to the Container registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Build and push Docker image
uses: docker/build-push-action@v6
with:
context: ./server
push: true
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
platforms: linux/amd64

deploy:
name: Deploy
runs-on: [self-hosted]
needs: build

defaults:
run:
working-directory: ./server

steps:
- name: Checkout Repository
uses: actions/checkout@v4

- name: Login to the Container registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Create .env file
env:
SECRET_CONTEXT: ${{ toJson(secrets) }}
run: |
echo "$SECRET_CONTEXT" | tr -d '{}' | tr ',' '\n' | sed -n 's/"\(.*\)":\(.*\)/\1=\2/p' > .env
- name: View
run: cat .env

- name: Compose Docker image
env:
DOCKER_APP_IMAGE: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}

run: docker compose -f compose.yml up -d

- name: Docker remove unused images
run: docker image prune -af

- name: Check running containers
run: docker ps -a
34 changes: 34 additions & 0 deletions .github/workflows/server_ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
name: Server CI

on:
pull_request:
types: [opened, synchronize, reopened]
branches:
- main
paths:
- 'server/**'

jobs:
build:
name: Build
runs-on: ubuntu-latest

defaults:
run:
working-directory: server

steps:
- name: Checkout Repository
uses: actions/checkout@v4

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

- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4

- name: Build with Gradle
run: ./gradlew build
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
# Fluffy

Fluffy는 시험 문제를 제작하고, 공유하고, 풀 수 있는 웹 서비스입니다.

### Stack

- server: spring, aws, supabase(postgreSQL)
- web: react, vite, tailwindcss, vercel
8 changes: 8 additions & 0 deletions server/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
OAUTH_GITHUB_CLIENT_ID=
OAUTH_GITHUB_CLIENT_SECRET=

DB_URL=
DB_USERNAME=
DB_PASSWORD=

JWT_SECRET_KEY=
4 changes: 3 additions & 1 deletion server/.gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
.DS_Store
src/main/resources/.env

.env.*
!.env.example

HELP.md
.gradle
Expand Down
10 changes: 10 additions & 0 deletions server/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
FROM openjdk:21-jdk-slim

WORKDIR /app

ARG JAR_FILE=/build/libs/*.jar
COPY ${JAR_FILE} app.jar

EXPOSE 8080

ENTRYPOINT ["java", "-jar", "-Dspring.profiles.active=${SPRING_PROFILES_ACTIVE}", "app.jar"]
1 change: 1 addition & 0 deletions server/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ dependencies {

// db
runtimeOnly 'com.h2database:h2'
runtimeOnly 'org.postgresql:postgresql'

// lombok
compileOnly 'org.projectlombok:lombok'
Expand Down
12 changes: 12 additions & 0 deletions server/compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
services:
app:
container_name: fluffy-app
image: ${DOCKER_APP_IMAGE}
ports:
- '8080:8080'
env_file:
- .env
environment:
TZ: Asia/Seoul
SPRING_PROFILES_ACTIVE: prod
restart: always
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public class ExamSummaryDto {
private String description;
private ExamStatus status;
private AuthorDto author;
private Long questionCount;
private int questionCount;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;

Expand All @@ -27,7 +27,7 @@ public ExamSummaryDto(
String description,
ExamStatus status,
AuthorDto author,
Long questionCount,
int questionCount,
LocalDateTime createdAt,
LocalDateTime updatedAt
) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,44 +31,41 @@ public class ExamRepositoryImpl implements ExamRepositoryCustom {
@Override
public List<ExamSummaryDto> findPublishedSummaries() {
return queryFactory
.select(new QExamSummaryDto(
.selectDistinct(new QExamSummaryDto(
exam.id,
exam.title.value,
exam.description.value,
exam.status,
AUTHOR_PROJECTION,
question.count(),
exam.questionGroup.questions.size(), // postgreSQL group by 이슈로 인함.
exam.createdAt,
exam.updatedAt
))
.from(exam)
.leftJoin(member).on(exam.memberId.eq(member.id))
.leftJoin(exam.questionGroup.questions, question)
.where(exam.status.eq(ExamStatus.PUBLISHED))
// TODO: 시험을 풀 수 있는 시간 내에만 조회되도록 수정
.groupBy(exam.id)
.orderBy(exam.updatedAt.desc())
.fetch();
}

@Override
public List<ExamSummaryDto> findMySummaries(ExamStatus status, Long memberId) {
return queryFactory
.select(new QExamSummaryDto(
.selectDistinct(new QExamSummaryDto(
exam.id,
exam.title.value,
exam.description.value,
exam.status,
AUTHOR_PROJECTION,
question.count(),
exam.questionGroup.questions.size(),
exam.createdAt,
exam.updatedAt
))
.from(exam)
.leftJoin(member).on(exam.memberId.eq(member.id))
.leftJoin(exam.questionGroup.questions, question)
.where(exam.memberId.eq(memberId), exam.status.eq(status))
.groupBy(exam.id)
.orderBy(exam.updatedAt.desc())
.fetch();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,64 @@
package com.fluffy.global.exception;

import java.util.Map;
import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ProblemDetail;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.servlet.resource.NoResourceFoundException;

@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {

@ExceptionHandler(NoResourceFoundException.class)
public ProblemDetail handleNoResourceFoundException(NoResourceFoundException e) {
log.warn("[No Resource Found Exception]", e);

return ProblemDetail.forStatusAndDetail(HttpStatus.NOT_FOUND, "요청한 리소스를 찾을 수 없습니다.");
}

@ExceptionHandler(MethodArgumentTypeMismatchException.class)
public ProblemDetail handleMethodArgumentTypeMismatchException(MethodArgumentTypeMismatchException e) {
log.warn("[Method Argument Type Mismatch Exception]", e);

return ProblemDetail.forStatusAndDetail(HttpStatus.BAD_REQUEST, "잘못된 요청 타입입니다.");
}

@ExceptionHandler(MethodArgumentNotValidException.class)
public ProblemDetail handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
log.warn("[Method Argument Not Valid Exception]", e);

Map<String, String> errors = e.getBindingResult().getFieldErrors().stream()
.collect(Collectors.toMap(FieldError::getField, FieldError::getDefaultMessage));

ProblemDetail problemDetail = ProblemDetail.forStatusAndDetail(HttpStatus.BAD_REQUEST, "잘못된 요청입니다.");
problemDetail.setProperty("errors", errors);

return problemDetail;
}

@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
public ProblemDetail handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e) {
log.warn("[Http Request Method Not Supported Exception]", e);

return ProblemDetail.forStatusAndDetail(HttpStatus.METHOD_NOT_ALLOWED, "지원하지 않는 HTTP 메소드입니다.");
}

@ExceptionHandler(HttpMessageNotReadableException.class)
public ProblemDetail handleHttpMessageNotReadableException(HttpMessageNotReadableException e) {
log.warn("[Http Message Not Readable Exception]", e);

return ProblemDetail.forStatusAndDetail(HttpStatus.BAD_REQUEST, "요청을 읽을 수 없습니다.");
}

@ExceptionHandler(BadRequestException.class)
public ProblemDetail handleBadRequestException(BadRequestException e) {
log.warn("[Bad Request Exception]", e);
Expand Down
Empty file.
7 changes: 4 additions & 3 deletions server/src/main/resources/application-local.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@ spring:
enabled: true
datasource:
url: jdbc:h2:mem:database
driver-class-name: org.h2.Driver
jpa:
open-in-view: false
show-sql: true
properties:
hibernate:
format_sql: true
highlight_sql: true
hibernate:
ddl-auto: create-drop

api-host: http://localhost:8080
client-host: http://localhost:5173
Expand All @@ -30,8 +31,8 @@ auth:
maxAge: 86400 # 1 day
oauth2:
github:
client-id: ${GITHUB_CLIENT_ID}
client-secret: ${GITHUB_CLIENT_SECRET}
client-id: ${OAUTH_GITHUB_CLIENT_ID}
client-secret: ${OAUTH_GITHUB_CLIENT_SECRET}
redirect-uri: ${api-host}/api/v1/auth/oauth2/callback/github
client-uri: ${client-host}

Expand Down
41 changes: 41 additions & 0 deletions server/src/main/resources/application-prod.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
spring:
datasource:
url: ${DB_URL}
username: ${DB_USERNAME}
password: ${DB_PASSWORD}
jpa:
open-in-view: false
show-sql: true
properties:
hibernate:
format_sql: true
highlight_sql: true
hibernate:
ddl-auto: validate

api-host: https://api.fluffy.run
client-host: https://www.fluffy.run

auth:
jwt:
secret-key: ${JWT_SECRET_KEY}
expiration-time: 86400000 # 1 day
cookie:
access-token-key: access_token
httpOnly: true
secure: true
domain: .fluffy.run
path: /
maxAge: 86400 # 1 day
oauth2:
github:
client-id: ${OAUTH_GITHUB_CLIENT_ID}
client-secret: ${OAUTH_GITHUB_CLIENT_SECRET}
redirect-uri: ${api-host}/api/v1/auth/oauth2/callback/github
client-uri: ${client-host}

logging:
level:
org.springframework.orm.jpa: DEBUG
org.springframework.orm.transaction: DEBUG
org.hibernate.orm.jdbc.bind: trace
Loading

0 comments on commit cfe1f01

Please sign in to comment.