Skip to content

Commit bfca4a9

Browse files
committed
2 parents 1ccb43f + 65e05fd commit bfca4a9

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+756
-175
lines changed

.github/workflows/release-workflow.yml

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ on:
44
push:
55
branches:
66
- release
7+
workflow_dispatch:
78

89
env:
910
REGISTRY: ghcr.io
@@ -49,7 +50,9 @@ jobs:
4950
uses: actions/checkout@v4
5051

5152
- name: Setting for Developent
52-
run: echo "${{ secrets.APPLICATION_DEV_YML }}" > src/main/resources/application-dev.yml
53+
run: |
54+
mkdir -p src/main/resources
55+
echo "${{ secrets.APPLICATION_DEV_YML }}" > src/main/resources/application-dev.yml
5356
5457
- name: Sign in github container registry
5558
uses: docker/login-action@v3
@@ -75,3 +78,24 @@ jobs:
7578
push: true
7679
tags: ${{ steps.meta.outputs.tags }}
7780
labels: ${{ steps.meta.outputs.labels }}
81+
build-args: |
82+
SPRING_PROFILES_ACTIVE=dev
83+
84+
deploy:
85+
name: EC2 자동 배포
86+
runs-on: ubuntu-latest
87+
needs: build-image
88+
89+
steps:
90+
- name: EC2에 SSH로 접속 후 배포
91+
uses: appleboy/[email protected]
92+
with:
93+
host: ${{ secrets.EC2_HOST }}
94+
username: ${{ secrets.EC2_USERNAME }}
95+
key: ${{ secrets.EC2_SSH_KEY }}
96+
port: ${{ secrets.EC2_PORT }}
97+
script: |
98+
cd ${{ secrets.EC2_DEPLOY_DIR }}
99+
docker compose pull
100+
docker compose down
101+
docker compose up -d

Dockerfile

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,8 @@ WORKDIR /app
2020

2121
COPY --from=builder /libs/build/libs/*.jar app.jar
2222

23-
ENTRYPOINT ["java", "-Dspring.profiles.active=dev", "-jar", "app.jar"]
23+
ARG SPRING_PROFILES_ACTIVE
24+
ENV SPRING_PROFILES_ACTIVE=${SPRING_PROFILES_ACTIVE}
25+
26+
ENTRYPOINT ["java", "-Dspring.profiles.active=${SPRING_PROFILES_ACTIVE}", "-jar", "app.jar"]
2427

build.gradle

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ dependencies {
3030
implementation 'org.springframework.boot:spring-boot-starter-security'
3131
implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'
3232
implementation 'org.springframework.boot:spring-boot-starter-validation'
33+
implementation 'org.springframework.boot:spring-boot-starter-cache'
3334
testImplementation 'org.springframework.security:spring-security-test'
3435

3536
compileOnly 'org.projectlombok:lombok'
@@ -61,6 +62,10 @@ dependencies {
6162

6263
// commons-lang3
6364
implementation 'org.apache.commons:commons-lang3:3.18.0'
65+
// caffeine
66+
implementation 'com.github.ben-manes.caffeine:caffeine'
67+
68+
implementation 'org.springframework.retry:spring-retry:2.0.12'
6469
}
6570

6671
tasks.named('test') {

src/main/java/com/oronaminc/join/answer/dto/AnswerCreateResponse.java

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,13 @@
1111
@Builder
1212
@Schema(description = "WebSocket STOMP 통신 답변 응답 DTO")
1313
public record AnswerCreateResponse(
14-
//TODO: QuestionCreateResponse와 유사-> 둘중 하나만?
1514
@Schema(description = "답변이 생성될 질문 ID")
1615
Long questionId,
1716
@Schema(description = "답변 생성/삭제/수정 상태", example = "CREATE")
1817
EventType event,
1918
@Schema(description = "답변 ID", example = "11")
2019
Long answerId,
2120
@Schema(description = "답변 내용", example = "답변입니다.")
22-
@NotBlank(message = "답변 내용을 입력해주시기 바랍니다.")
23-
@Size(max = 300, message = "답변 내용은 최대 300자까지 입력할 수 있습니다.")
2421
String content,
2522
@Schema(description = "답변 내용에 대한 공감 수", example = "23")
2623
int emojiCount,

src/main/java/com/oronaminc/join/answer/dto/AnswerRequest.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66

77
@Schema(description = "답변 생성/수정 요청 DTO")
88
public record AnswerRequest(
9-
//TODO: 빈값 or " " (space) 처리
109
@NotBlank(message = "답변 내용을 입력해주시기 바랍니다.")
1110
@Size(max = 300, message = "답변 내용은 최대 300자까지 입력할 수 있습니다.")
1211
@Schema(description = "답변 내용", example = "답변입니다.")

src/main/java/com/oronaminc/join/answer/service/AnswerService.java

Lines changed: 10 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
11
package com.oronaminc.join.answer.service;
22

3-
import static com.oronaminc.join.global.exception.ErrorCode.BADREQUEST_DUPLICATION_ANSWER;
43

54
import com.oronaminc.join.answer.dao.AnswerRepository;
65
import com.oronaminc.join.answer.domain.Answer;
7-
import com.oronaminc.join.answer.dto.AnswerRequest;
86
import com.oronaminc.join.answer.dto.AnswerGetResponse;
7+
import com.oronaminc.join.answer.dto.AnswerRequest;
98
import com.oronaminc.join.answer.mapper.AnswerMapper;
9+
import com.oronaminc.join.answer.util.PermissionValidator;
1010
import com.oronaminc.join.emoji.domain.TargetType;
1111
import com.oronaminc.join.emoji.service.EmojiReader;
12-
import com.oronaminc.join.global.exception.ErrorException;
1312
import com.oronaminc.join.member.domain.Member;
1413
import com.oronaminc.join.member.service.MemberReader;
1514
import com.oronaminc.join.participant.service.ParticipantService;
@@ -28,32 +27,25 @@
2827
public class AnswerService {
2928

3029
private final AnswerRepository answerRepository;
31-
private final ParticipantService participantService;
3230
private final QuestionReader questionReader;
3331
private final MemberReader memberReader;
3432
private final AnswerReader answerReader;
3533
private final RoomReader roomReader;
3634
private final EmojiReader emojiReader;
35+
private final PermissionValidator permissionValidator;
3736

3837
@Transactional
3938
public Answer create(Long roomId, Long memberId, Long questionId,
4039
AnswerRequest request) {
4140

4241
Member member = memberReader.getById(memberId);
4342
Room room = roomReader.getById(roomId);
44-
Question question = questionReader.getByIdAndRoomId(questionId, roomId);
45-
46-
participantService.validateParticipant(member.getId(), room.getId());
47-
48-
if (answerReader.existsByQuestionIdAndMemberId(question.getId(), member.getId())) {
49-
throw new ErrorException(BADREQUEST_DUPLICATION_ANSWER);
50-
}
51-
43+
Question question = questionReader.getByIdAndRoomId(questionId, room.getId());
44+
permissionValidator.validateAnswerCreatePermission(room.getId(), member.getId(), question);
5245
Answer answer = AnswerMapper.toEntity(question, member, request);
5346

54-
answerRepository.save(answer);
47+
return answerRepository.save(answer);
5548

56-
return answer;
5749
}
5850

5951
@Transactional
@@ -71,17 +63,17 @@ public AnswerGetResponse getAnswer(Long roomId, Long questionId, Long memberId)
7163
}
7264

7365
@Transactional
74-
public Answer update(Long answerId, AnswerRequest request) {
75-
Answer answer = answerReader.getById(answerId);
66+
public Answer update(Long answerId, Long memberId, AnswerRequest request) {
67+
Answer answer = permissionValidator.validateAnswerUpdatePermission(answerId, memberId);
7668

7769
answer.updataContent(request.content());
7870

7971
return answer;
8072
}
8173

8274
@Transactional
83-
public void delete(Long answerId) {
84-
Answer answer = answerReader.getById(answerId);
75+
public void delete(Long answerId, Long memberId) {
76+
Answer answer = permissionValidator.validateAnswerDeletePermission(answerId, memberId);
8577
answerRepository.delete(answer);
8678
}
8779

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package com.oronaminc.join.answer.util;
2+
3+
import com.oronaminc.join.global.exception.ErrorCode;
4+
import lombok.Getter;
5+
6+
7+
@Getter
8+
public enum PermissionType {
9+
CREATE, DELETE;
10+
11+
public ErrorCode toErrorCode() {
12+
return switch (this) {
13+
case CREATE -> ErrorCode.UNAUTHORIZED_ROLE_ANSWER;
14+
case DELETE -> ErrorCode.UNAUTHORIZED_DELETE_ANSWER;
15+
};
16+
}
17+
18+
}
Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
package com.oronaminc.join.answer.util;
22

3-
import static com.oronaminc.join.global.exception.ErrorCode.UNAUTHORIZED_DELETE_ANSWER;
43
import static com.oronaminc.join.global.exception.ErrorCode.UNAUTHORIZED_EDIT_ANSWER;
5-
import static com.oronaminc.join.global.exception.ErrorCode.UNAUTHORIZED_ROLE_ANSWER;
64

75
import com.oronaminc.join.answer.domain.Answer;
86
import com.oronaminc.join.answer.service.AnswerReader;
@@ -22,40 +20,40 @@ public class PermissionValidator {
2220
private final ParticipantReader participantReader;
2321
private final AnswerReader answerReader;
2422

25-
public void validateAnswerPermission(Long roomId, Long memberId) {
26-
// TODO: 생성시 조건 ( null 이면 안됨, " " 안됨, 팀원이나 발표자, 질문 작성자 본인만 생성가능 )
27-
Participant participant = participantReader.getByRoomIdAndMemberId(roomId, memberId);
28-
ParticipantType type = participant.getParticipantType();
29-
30-
if (type == ParticipantType.GUEST) {
31-
throw new ErrorException(UNAUTHORIZED_ROLE_ANSWER);
32-
}
23+
public void validateAnswerCreatePermission(Long roomId, Long memberId, Question question) {
24+
validatePermission(roomId, memberId, question, PermissionType.CREATE);
3325
}
3426

35-
public void validateAnswerUpdatePermission(Long answerId, Long memberId) {
27+
public Answer validateAnswerUpdatePermission(Long answerId, Long memberId) {
3628
Answer answer = answerReader.getById(answerId);
3729

3830
if (!answer.getMember().getId().equals(memberId)) {
3931
throw new ErrorException(UNAUTHORIZED_EDIT_ANSWER);
4032
}
33+
34+
return answer;
4135
}
4236

43-
public void validateAnswerDeletePermission(Long answerId, Long memberId) {
37+
public Answer validateAnswerDeletePermission(Long answerId, Long memberId) {
4438
Answer answer = answerReader.getById(answerId);
45-
4639
Room room = answer.getQuestion().getRoom();
4740
Question question = answer.getQuestion();
4841

49-
Participant participant = participantReader.getByRoomIdAndMemberId(room.getId(), memberId);
50-
ParticipantType type = participant.getParticipantType();
42+
validatePermission(room.getId(), memberId, question, PermissionType.DELETE);
43+
44+
return answer;
45+
}
5146

47+
private void validatePermission(Long roomId, Long memberId, Question question,
48+
PermissionType permissionType) {
49+
Participant participant = participantReader.getByRoomIdAndMemberId(roomId, memberId);
50+
ParticipantType type = participant.getParticipantType();
5251
boolean isQuestionWriter = question.getMember().getId().equals(memberId);
5352

5453
if (!(isQuestionWriter || type == ParticipantType.TEAM
5554
|| type == ParticipantType.PRESENTER)) {
56-
throw new ErrorException(UNAUTHORIZED_DELETE_ANSWER);
55+
throw new ErrorException(permissionType.toErrorCode());
5756
}
58-
5957
}
6058

6159
}

src/main/java/com/oronaminc/join/document/api/DocumentController.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,14 @@
77
import com.oronaminc.join.member.security.MemberDetails;
88
import io.swagger.v3.oas.annotations.Operation;
99
import io.swagger.v3.oas.annotations.responses.ApiResponse;
10+
import io.swagger.v3.oas.annotations.tags.Tag;
1011
import jakarta.validation.Valid;
1112
import lombok.RequiredArgsConstructor;
1213
import org.springframework.http.HttpStatus;
1314
import org.springframework.security.core.annotation.AuthenticationPrincipal;
1415
import org.springframework.web.bind.annotation.*;
1516

17+
@Tag(name = "발표자료")
1618
@RestController
1719
@RequiredArgsConstructor
1820
@RequestMapping("/api/documents")
@@ -37,6 +39,6 @@ public DocumentResponse generatePresignedUrl(
3739
@AuthenticationPrincipal MemberDetails memberDetails
3840
) {
3941
String memberRole = memberDetails.getRole();
40-
return documentService.generatePresignedUrl(documentRequest, memberRole);
42+
return documentService.generateUploadPresignedUrl(documentRequest, memberRole);
4143
}
4244
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package com.oronaminc.join.document.event;
2+
3+
public record DocumentCreateEvent(String objectKey, String fileName) { }

0 commit comments

Comments
 (0)