Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
2ae1eaa
[CHORE] HttpClientException 처리 (#160)
coli-geonwoo May 13, 2025
7764ce5
[CHORE] 의회식 토론 api 제거 (#172)
coli-geonwoo May 14, 2025
09d22f6
[TEST] DTO Null, Blank E2E Test (#174)
unifolio0 May 18, 2025
35e66ec
[FIX] '토론 테이블 변경 API' 동시성 문제 (#173)
leegwichan May 19, 2025
4fae84f
[REFACTOR] MappedSuperClass 제거 (#177)
coli-geonwoo May 19, 2025
ce816d9
[CHORE] DataDog을 통한 로그 수집 (#178)
leegwichan May 20, 2025
2c25f12
[CHORE] DataDog을 통한 로그 수집 (#179)
leegwichan May 28, 2025
ec33f58
[FEAT] MultipartException 처리 (#180)
leegwichan May 28, 2025
1217bc7
[REFACTOR] 타임 박스 삭제&저장 동시성 이슈 리팩토링 (#184)
leegwichan May 31, 2025
c9778c3
[FIX] modified_at 미갱신 문제 해결 (#187)
coli-geonwoo Jun 12, 2025
8002da9
[REFACTOR] 테이블 도메인-엔티티 분리 (#182)
coli-geonwoo Jun 27, 2025
51b9fd0
chore : 코드레빗 설정파일 추가 (#189)
leegwichan Jul 1, 2025
4c3baf5
[FEAT] 투표 API 기초 엔티티 세팅 (#196)
coli-geonwoo Jul 10, 2025
ded82b5
[CHORE] 커스텀 메트릭 지표 논의 및 구현 (#197)
leegwichan Jul 11, 2025
91ebb70
feat : 커스텀 토론 타임 박스 도메인 구현
leegwichan Jul 14, 2025
1d35c2d
Revert "feat : 커스텀 토론 타임 박스 도메인 구현"
leegwichan Jul 14, 2025
28b2660
[FEAT] 종소리 커스텀 기능 (#193)
unifolio0 Jul 16, 2025
b25a821
[REFACTOR] 타임박스 도메인 - 엔티티 분리 / 도메인 구현 (#198)
leegwichan Jul 16, 2025
5268721
[REFACTOR] 타임박스 도메인, 엔티티 분리 - 파일 네이밍 수정 외 3개 (#200)
leegwichan Jul 17, 2025
cbdcbd3
[REFACTOR] 타임박스 도메인 - 엔티티 분리 / 서비스 로직 개선 (#201)
leegwichan Jul 22, 2025
ca1c4c1
[FEAT] 투표 개최자 API 구현 (#203)
coli-geonwoo Jul 26, 2025
7a8c985
[FEAT] 투표자 API 구현 (#205)
coli-geonwoo Jul 30, 2025
69d839d
chore: 워크플로우 수동 실행 허용
leegwichan Jul 31, 2025
77ad284
[CHORE] 정상 배포 확인을 위한 헬스 체크 기능 구현 (#211)
leegwichan Jul 31, 2025
4812031
[FEAT] 종소리 관련 API V2 제작 (#206)
unifolio0 Aug 1, 2025
659991c
[HOTFIX] CustomizeTimeBoxEntity 에서 time 이 nullable 하지 않도록 수정 (#212)
leegwichan Aug 1, 2025
ad15ffb
[REFACTOR] CustomizeTimeBoxEntities 제거 (#214)
leegwichan Aug 10, 2025
dac8e5e
chore: dev와 main충돌 해결
coli-geonwoo Sep 9, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions .coderabbit.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
language: "ko-KR"
early_access: false
reviews:
profile: "chill"
request_changes_workflow: false
high_level_summary: true
poem: false
review_status: true
collapse_walkthrough: false
auto_review:
enabled: true
drafts: false
base_branches:
- "develop"
chat:
auto_reply: true
18 changes: 14 additions & 4 deletions .github/workflows/Dev_CD.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
name: dev-cd

on:
workflow_dispatch:
push:
branches:
- "develop"
Expand All @@ -10,10 +11,8 @@ jobs:
runs-on: ubuntu-latest

steps:
- name: Checkout Develop Branch
- name: Checkout Branch
uses: actions/checkout@v4
with:
ref: "develop"

- name: Setting dev-secret.yml
run: |
Expand Down Expand Up @@ -66,7 +65,18 @@ jobs:
uses: actions/download-artifact@v4
with:
name: deploy-scripts
path: ~/app/scripts
path: ~/app/scripts/

- name: Replace application to latest
run: sudo sh ~/app/scripts/replace-new-version.sh

- name: Health Check
run: sh ~/app/scripts/health-check.sh

- name: Send Discord Alert on Failure
if: failure()
run: |
curl -H "Content-Type: application/json" \
-X POST \
-d "{\"content\":\":x: [DEV] 배포 실패! 확인이 필요합니다.\n링크: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}\"}" \
${{ secrets.DISCORD_WEB_HOOK }}
16 changes: 13 additions & 3 deletions .github/workflows/Prod_CD.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
name: prod-cd

on:
workflow_dispatch:
push:
branches:
- "main"
Expand All @@ -12,8 +13,6 @@ jobs:
steps:
- name: Checkout Develop Branch
uses: actions/checkout@v4
with:
ref: "main"

- name: Setting prod-secret.yml
run: |
Expand Down Expand Up @@ -66,7 +65,18 @@ jobs:
uses: actions/download-artifact@v4
with:
name: deploy-scripts
path: ~/app/scripts
path: ~/app/scripts/

- name: Replace application to latest
run: sudo sh ~/app/scripts/replace-new-version.sh

- name: Health Check
run: sh ~/app/scripts/health-check.sh

- name: Send Discord Alert on Failure
if: failure()
run: |
curl -H "Content-Type: application/json" \
-X POST \
-d "{\"content\":\":warning: :warning: [PROD] 배포 실패! :warning: :warning: \n링크: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}\"}" \
${{ secrets.DISCORD_WEB_HOOK }}
31 changes: 31 additions & 0 deletions scripts/dev/health-check.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#!/bin/bash

# 최대 반복 횟수
MAX_RETRIES=60

# 성공 상태 코드와 요청 URL
SUCCESS_STATUS=200
HEALTH_CHECK_URL="http://localhost:8083/monitoring/health"

# 반복 시작
i=1
while [ "$i" -le "$MAX_RETRIES" ]; do
# HTTP 요청 보내기
RESPONSE_STATUS=$(curl -s -o /dev/null -w "%{http_code}" "$HEALTH_CHECK_URL")
echo "[TRY $i] StatusCode : $RESPONSE_STATUS "
# 상태 코드 확인
if [ "$RESPONSE_STATUS" -eq "$SUCCESS_STATUS" ]; then
echo "Success: Received $SUCCESS_STATUS status code on attempt $i."
exit 0
fi

# 2초 대기
sleep 2

# 반복 변수 증가
i=$((i + 1))
done

# 실패 메시지
echo "Failure: Did not receive $SUCCESS_STATUS status code within $MAX_RETRIES attempts."
exit 1
31 changes: 31 additions & 0 deletions scripts/prod/health-check.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#!/bin/bash

# 최대 반복 횟수
MAX_RETRIES=60

# 성공 상태 코드와 요청 URL
SUCCESS_STATUS=200
HEALTH_CHECK_URL="http://localhost:8083/monitoring/health"

# 반복 시작
i=1
while [ "$i" -le "$MAX_RETRIES" ]; do
# HTTP 요청 보내기
RESPONSE_STATUS=$(curl -s -o /dev/null -w "%{http_code}" "$HEALTH_CHECK_URL")
echo "[TRY $i] StatusCode : $RESPONSE_STATUS "
# 상태 코드 확인
if [ "$RESPONSE_STATUS" -eq "$SUCCESS_STATUS" ]; then
echo "Success: Received $SUCCESS_STATUS status code on attempt $i."
exit 0
fi

# 2초 대기
sleep 2

# 반복 변수 증가
i=$((i + 1))
done

# 실패 메시지
echo "Failure: Did not receive $SUCCESS_STATUS status code within $MAX_RETRIES attempts."
exit 1
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.debatetimer.exception.custom.DTInitializationException;
import com.debatetimer.exception.errorcode.InitializationErrorCode;
import io.micrometer.core.annotation.Timed;
import java.util.Arrays;
import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
Expand Down Expand Up @@ -34,6 +35,7 @@ private JDA initializeJda(String token) {
}
}

@Timed(value = "discord.send_error_message")
public void sendErrorMessage(Throwable throwable) {
TextChannel channel = jda.getTextChannelById(properties.getChannelId());
String errorMessage = throwable.toString();
Expand Down
3 changes: 3 additions & 0 deletions src/main/java/com/debatetimer/client/oauth/OAuthClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import com.debatetimer.dto.member.MemberInfo;
import com.debatetimer.dto.member.OAuthToken;
import com.debatetimer.exception.handler.OAuthClientErrorHandler;
import io.micrometer.core.annotation.Timed;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.http.HttpStatusCode;
import org.springframework.http.MediaType;
Expand All @@ -24,6 +25,7 @@ public OAuthClient(RestClient.Builder restClientBuilder, OAuthProperties oauthPr
this.oauthProperties = oauthProperties;
}

@Timed(value = "google.request_token")
public OAuthToken requestToken(MemberCreateRequest request) {
return restClient.post()
.uri("https://oauth2.googleapis.com/token")
Expand All @@ -34,6 +36,7 @@ public OAuthToken requestToken(MemberCreateRequest request) {
.body(OAuthToken.class);
}

@Timed(value = "google.request_member_info")
public MemberInfo requestMemberInfo(OAuthToken response) {
return restClient.get()
.uri("https://www.googleapis.com/oauth2/v3/userinfo")
Expand Down
33 changes: 33 additions & 0 deletions src/main/java/com/debatetimer/config/ErrorDecoderConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.debatetimer.config;

import com.debatetimer.exception.decoder.H2ErrorDecoder;
import com.debatetimer.exception.decoder.MySqlErrorDecoder;
import com.debatetimer.exception.decoder.RepositoryErrorDecoder;
import lombok.NoArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;

@NoArgsConstructor(access = lombok.AccessLevel.PRIVATE)
public class ErrorDecoderConfig {

@Profile({"dev", "prod"})
@Configuration
public static class MySqlErrorDecoderConfig {

@Bean
public RepositoryErrorDecoder mySqlErrorDecoder() {
return new MySqlErrorDecoder();
}
}

@Profile({"test", "local"})
@Configuration
public static class H2ErrorDecoderConfig {

@Bean
public RepositoryErrorDecoder h2ErrorDecoder() {
return new H2ErrorDecoder();
}
}
}
17 changes: 17 additions & 0 deletions src/main/java/com/debatetimer/config/MonitoringConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.debatetimer.config;

import io.micrometer.core.aop.TimedAspect;
import io.micrometer.core.instrument.MeterRegistry;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MonitoringConfig {

@Bean
@ConditionalOnMissingBean(TimedAspect.class)
public TimedAspect timedAspect(MeterRegistry registry) {
return new TimedAspect(registry);
}
}
49 changes: 49 additions & 0 deletions src/main/java/com/debatetimer/controller/poll/PollController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package com.debatetimer.controller.poll;

import com.debatetimer.controller.auth.AuthMember;
import com.debatetimer.domain.member.Member;
import com.debatetimer.dto.poll.response.PollCreateResponse;
import com.debatetimer.dto.poll.response.PollInfoResponse;
import com.debatetimer.service.poll.PollService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequiredArgsConstructor
public class PollController {

private final PollService pollService;

@PostMapping("/api/polls/{tableId}")
@ResponseStatus(HttpStatus.CREATED)
public PollCreateResponse createPoll(
@AuthMember Member member,
@PathVariable(name = "tableId") long tableId
) {
return pollService.create(tableId, member);
}

@GetMapping("/api/polls/{pollId}")
@ResponseStatus(HttpStatus.OK)
public PollInfoResponse getPollInfo(
@AuthMember Member member,
@PathVariable(name = "pollId") long pollId
) {
return pollService.getPollInfo(pollId, member);
}

@PatchMapping("/api/polls/{pollId}")
@ResponseStatus(HttpStatus.OK)
public PollInfoResponse finishPoll(
@AuthMember Member member,
@PathVariable(name = "pollId") long pollId
) {
return pollService.finishPoll(pollId, member);
}
}
37 changes: 37 additions & 0 deletions src/main/java/com/debatetimer/controller/poll/VoteController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.debatetimer.controller.poll;

import com.debatetimer.dto.poll.request.VoteRequest;
import com.debatetimer.dto.poll.response.VoteCreateResponse;
import com.debatetimer.dto.poll.response.VoterPollInfoResponse;
import com.debatetimer.service.poll.VoteService;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequiredArgsConstructor
public class VoteController {

private final VoteService voteService;

@GetMapping("/api/polls/{pollId}/votes")
@ResponseStatus(HttpStatus.OK)
public VoterPollInfoResponse getVotersPollInfo(@PathVariable long pollId) {
return voteService.getVoterPollInfo(pollId);
}

@PostMapping("/api/polls/{pollId}/votes")
@ResponseStatus(HttpStatus.CREATED)
public VoteCreateResponse votePoll(
@PathVariable long pollId,
@RequestBody @Valid VoteRequest voteRequest
) {
return voteService.vote(pollId, voteRequest);
}
}
8 changes: 0 additions & 8 deletions src/main/java/com/debatetimer/domain/Stance.java

This file was deleted.

28 changes: 28 additions & 0 deletions src/main/java/com/debatetimer/domain/customize/Agenda.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.debatetimer.domain.customize;

import com.debatetimer.exception.custom.DTClientErrorException;
import com.debatetimer.exception.errorcode.ClientErrorCode;
import lombok.Getter;

@Getter
public class Agenda {

public static final int AGENDA_MAX_LENGTH = 255;

private final String value;

public Agenda(String value) {
validate(value);
this.value = value;
}

private void validate(String value) {
if (value == null) {
return;
}

if (value.length() > AGENDA_MAX_LENGTH) {
throw new DTClientErrorException(ClientErrorCode.INVALID_AGENDA_LENGTH);
}
}
}
Loading