-
Notifications
You must be signed in to change notification settings - Fork 0
feat : 서버 점검 상태 확인 API 및 필터를 통해 점검 시 API 호출 차단 로직 추가 #75
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
87abd5d
feat: Maintenance 필터 추가
kon28289 0c752c9
feat: Maintenance 관련 예외 추가
kon28289 39b39e5
feat: Maintenance 도메인 추가
kon28289 c805535
feat: Maintenance dto 추가
kon28289 9d70db0
feat: Maintenance 도메인 상태 관리 로직 추가
kon28289 b967345
test: Maintenance 테스트 코드 추가
kon28289 6095515
docs: 스웨거 문서화
kon28289 6b3e190
fix: getRequestURI를 getServletPath로 변경
kon28289 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
64 changes: 64 additions & 0 deletions
64
src/main/java/com/gpt/geumpumtabackend/global/maintenance/MaintenanceFilter.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,64 @@ | ||
| package com.gpt.geumpumtabackend.global.maintenance; | ||
|
|
||
| import com.fasterxml.jackson.databind.ObjectMapper; | ||
| import com.gpt.geumpumtabackend.global.exception.ExceptionType; | ||
| import com.gpt.geumpumtabackend.global.response.ResponseUtil; | ||
| import com.gpt.geumpumtabackend.maintenance.service.MaintenanceService; | ||
| import jakarta.servlet.FilterChain; | ||
| import jakarta.servlet.ServletException; | ||
| import jakarta.servlet.http.HttpServletRequest; | ||
| import jakarta.servlet.http.HttpServletResponse; | ||
| import lombok.RequiredArgsConstructor; | ||
| import org.springframework.http.MediaType; | ||
| import org.springframework.stereotype.Component; | ||
| import org.springframework.util.AntPathMatcher; | ||
| import org.springframework.web.filter.OncePerRequestFilter; | ||
|
|
||
| import java.io.IOException; | ||
| import java.util.List; | ||
|
|
||
| @Component | ||
| @RequiredArgsConstructor | ||
| public class MaintenanceFilter extends OncePerRequestFilter { | ||
|
|
||
| private static final List<String> WHITELIST = List.of( | ||
| "/api/v1/maintenance/status", | ||
| "/actuator/health", | ||
| "/swagger-ui", | ||
| "/swagger-ui/", | ||
| "/swagger-ui/**", | ||
| "/v3/api-docs", | ||
| "/v3/api-docs/**", | ||
| "/error" | ||
| ); | ||
|
|
||
| private final MaintenanceService maintenanceService; | ||
| private final ObjectMapper objectMapper; | ||
| private final AntPathMatcher pathMatcher = new AntPathMatcher(); | ||
|
|
||
| @Override | ||
| protected void doFilterInternal( | ||
| HttpServletRequest request, | ||
| HttpServletResponse response, | ||
| FilterChain filterChain | ||
| ) throws ServletException, IOException { | ||
| String requestUri = request.getServletPath(); | ||
|
|
||
| if (isWhitelisted(requestUri) || !maintenanceService.isMaintenanceInProgress()) { | ||
Juhye0k marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| filterChain.doFilter(request, response); | ||
| return; | ||
| } | ||
|
|
||
| response.setStatus(ExceptionType.MAINTENANCE_IN_PROGRESS.getStatus().value()); | ||
| response.setContentType(MediaType.APPLICATION_JSON_VALUE); | ||
| response.setCharacterEncoding("UTF-8"); | ||
| objectMapper.writeValue( | ||
| response.getWriter(), | ||
| ResponseUtil.createFailureResponse(ExceptionType.MAINTENANCE_IN_PROGRESS) | ||
| ); | ||
| } | ||
|
|
||
| private boolean isWhitelisted(String requestUri) { | ||
| return WHITELIST.stream().anyMatch(pattern -> pathMatcher.match(pattern, requestUri)); | ||
| } | ||
| } | ||
63 changes: 63 additions & 0 deletions
63
src/main/java/com/gpt/geumpumtabackend/maintenance/api/MaintenanceApi.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| package com.gpt.geumpumtabackend.maintenance.api; | ||
|
|
||
| import com.gpt.geumpumtabackend.global.config.swagger.SwaggerApiFailedResponse; | ||
| import com.gpt.geumpumtabackend.global.config.swagger.SwaggerApiResponses; | ||
| import com.gpt.geumpumtabackend.global.config.swagger.SwaggerApiSuccessResponse; | ||
| import com.gpt.geumpumtabackend.global.exception.ExceptionType; | ||
| import com.gpt.geumpumtabackend.global.response.ResponseBody; | ||
| import com.gpt.geumpumtabackend.maintenance.dto.request.MaintenanceStatusUpdateRequest; | ||
| import com.gpt.geumpumtabackend.maintenance.dto.response.MaintenanceStatusResponse; | ||
| import io.swagger.v3.oas.annotations.Operation; | ||
| import io.swagger.v3.oas.annotations.media.Content; | ||
| import io.swagger.v3.oas.annotations.media.Schema; | ||
| import io.swagger.v3.oas.annotations.responses.ApiResponse; | ||
| import io.swagger.v3.oas.annotations.tags.Tag; | ||
| import jakarta.validation.Valid; | ||
| import org.springframework.http.ResponseEntity; | ||
| import org.springframework.security.access.prepost.PreAuthorize; | ||
| import org.springframework.web.bind.annotation.GetMapping; | ||
| import org.springframework.web.bind.annotation.PatchMapping; | ||
| import org.springframework.web.bind.annotation.RequestBody; | ||
|
|
||
| @Tag(name = "서비스 상태 API", description = "서비스 점검 상태 조회 및 변경 API") | ||
| public interface MaintenanceApi { | ||
|
|
||
| @Operation( | ||
| summary = "점검 상태 조회", | ||
| description = "현재 서비스 점검 상태와 안내 메시지를 조회합니다." | ||
| ) | ||
| @ApiResponse(content = @Content(schema = @Schema(implementation = MaintenanceStatusResponse.class))) | ||
| @SwaggerApiResponses( | ||
| success = @SwaggerApiSuccessResponse( | ||
| response = MaintenanceStatusResponse.class, | ||
| description = "점검 상태 조회 성공" | ||
| ) | ||
| ) | ||
| @GetMapping("/status") | ||
| ResponseEntity<ResponseBody<MaintenanceStatusResponse>> getCurrentStatus(); | ||
|
|
||
| @Operation( | ||
| summary = "점검 상태 변경", | ||
| description = """ | ||
| ADMIN 권한으로 서비스 점검 상태를 변경합니다. | ||
| - status: NORMAL 또는 MAINTENANCE | ||
| - message: 점검 안내 문구 (선택) | ||
| """ | ||
| ) | ||
| @ApiResponse(content = @Content(schema = @Schema(implementation = MaintenanceStatusResponse.class))) | ||
| @SwaggerApiResponses( | ||
| success = @SwaggerApiSuccessResponse( | ||
| response = MaintenanceStatusResponse.class, | ||
| description = "점검 상태 변경 성공" | ||
| ), | ||
| errors = { | ||
| @SwaggerApiFailedResponse(ExceptionType.NEED_AUTHORIZED), | ||
| @SwaggerApiFailedResponse(ExceptionType.ACCESS_DENIED) | ||
| } | ||
| ) | ||
| @PatchMapping("/status") | ||
| @PreAuthorize("isAuthenticated() and hasRole('ADMIN')") | ||
| ResponseEntity<ResponseBody<MaintenanceStatusResponse>> updateStatus( | ||
| @RequestBody @Valid MaintenanceStatusUpdateRequest request | ||
| ); | ||
| } |
42 changes: 42 additions & 0 deletions
42
src/main/java/com/gpt/geumpumtabackend/maintenance/controller/MaintenanceController.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| package com.gpt.geumpumtabackend.maintenance.controller; | ||
|
|
||
| import com.gpt.geumpumtabackend.global.response.ResponseBody; | ||
| import com.gpt.geumpumtabackend.global.response.ResponseUtil; | ||
| import com.gpt.geumpumtabackend.maintenance.api.MaintenanceApi; | ||
| import com.gpt.geumpumtabackend.maintenance.dto.request.MaintenanceStatusUpdateRequest; | ||
| import com.gpt.geumpumtabackend.maintenance.dto.response.MaintenanceStatusResponse; | ||
| import com.gpt.geumpumtabackend.maintenance.service.MaintenanceService; | ||
| import jakarta.validation.Valid; | ||
| import lombok.RequiredArgsConstructor; | ||
| import org.springframework.http.ResponseEntity; | ||
| import org.springframework.security.access.prepost.PreAuthorize; | ||
| import org.springframework.web.bind.annotation.GetMapping; | ||
| import org.springframework.web.bind.annotation.PatchMapping; | ||
| import org.springframework.web.bind.annotation.RequestBody; | ||
| import org.springframework.web.bind.annotation.RequestMapping; | ||
| import org.springframework.web.bind.annotation.RestController; | ||
|
|
||
| @RestController | ||
| @RequiredArgsConstructor | ||
| @RequestMapping("/api/v1/maintenance") | ||
| public class MaintenanceController implements MaintenanceApi { | ||
|
|
||
| private final MaintenanceService maintenanceService; | ||
|
|
||
| @GetMapping("/status") | ||
| public ResponseEntity<ResponseBody<MaintenanceStatusResponse>> getCurrentStatus() { | ||
| return ResponseEntity.ok(ResponseUtil.createSuccessResponse( | ||
| maintenanceService.getCurrentStatus() | ||
| )); | ||
| } | ||
|
|
||
| @PatchMapping("/status") | ||
| @PreAuthorize("isAuthenticated() and hasRole('ADMIN')") | ||
| public ResponseEntity<ResponseBody<MaintenanceStatusResponse>> updateStatus( | ||
| @RequestBody @Valid MaintenanceStatusUpdateRequest request | ||
| ) { | ||
| return ResponseEntity.ok(ResponseUtil.createSuccessResponse( | ||
| maintenanceService.updateStatus(request) | ||
| )); | ||
| } | ||
| } |
44 changes: 44 additions & 0 deletions
44
src/main/java/com/gpt/geumpumtabackend/maintenance/domain/Maintenance.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| package com.gpt.geumpumtabackend.maintenance.domain; | ||
|
|
||
| import com.gpt.geumpumtabackend.global.base.BaseEntity; | ||
| import jakarta.persistence.Column; | ||
| import jakarta.persistence.Entity; | ||
| import jakarta.persistence.EnumType; | ||
| import jakarta.persistence.Enumerated; | ||
| import jakarta.persistence.Id; | ||
| import lombok.AccessLevel; | ||
| import lombok.Getter; | ||
| import lombok.NoArgsConstructor; | ||
|
|
||
| @Entity | ||
| @Getter | ||
| @NoArgsConstructor(access = AccessLevel.PROTECTED) | ||
| public class Maintenance extends BaseEntity { | ||
kon28289 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| public static final Long DEFAULT_ID = 1L; | ||
|
|
||
| @Id | ||
| private Long id; | ||
|
|
||
| @Enumerated(EnumType.STRING) | ||
| @Column(nullable = false) | ||
| private ServiceStatus status; | ||
|
|
||
| @Column(length = 255) | ||
| private String message; | ||
|
|
||
| private Maintenance(Long id, ServiceStatus status, String message) { | ||
| this.id = id; | ||
| this.status = status; | ||
| this.message = message; | ||
| } | ||
|
|
||
| public static Maintenance initialize(ServiceStatus status, String message) { | ||
| return new Maintenance(DEFAULT_ID, status, message); | ||
| } | ||
|
|
||
| public void update(ServiceStatus status, String message) { | ||
| this.status = status; | ||
| this.message = message; | ||
| } | ||
| } | ||
15 changes: 15 additions & 0 deletions
15
src/main/java/com/gpt/geumpumtabackend/maintenance/domain/ServiceStatus.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| package com.gpt.geumpumtabackend.maintenance.domain; | ||
|
|
||
| import lombok.Getter; | ||
|
|
||
| @Getter | ||
| public enum ServiceStatus { | ||
| NORMAL("정상"), | ||
| MAINTENANCE("점검중"); | ||
|
|
||
| private final String status; | ||
|
|
||
| ServiceStatus(String status) { | ||
| this.status = status; | ||
| } | ||
| } |
10 changes: 10 additions & 0 deletions
10
...java/com/gpt/geumpumtabackend/maintenance/dto/request/MaintenanceStatusUpdateRequest.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| package com.gpt.geumpumtabackend.maintenance.dto.request; | ||
|
|
||
| import com.gpt.geumpumtabackend.maintenance.domain.ServiceStatus; | ||
| import jakarta.validation.constraints.NotNull; | ||
|
|
||
| public record MaintenanceStatusUpdateRequest( | ||
| @NotNull ServiceStatus status, | ||
| String message | ||
| ) { | ||
| } |
16 changes: 16 additions & 0 deletions
16
...ain/java/com/gpt/geumpumtabackend/maintenance/dto/response/MaintenanceStatusResponse.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| package com.gpt.geumpumtabackend.maintenance.dto.response; | ||
|
|
||
| import com.gpt.geumpumtabackend.maintenance.domain.Maintenance; | ||
| import com.gpt.geumpumtabackend.maintenance.domain.ServiceStatus; | ||
|
|
||
| public record MaintenanceStatusResponse( | ||
| ServiceStatus status, | ||
| String message | ||
| ) { | ||
| public static MaintenanceStatusResponse from(Maintenance maintenance) { | ||
| return new MaintenanceStatusResponse( | ||
| maintenance.getStatus(), | ||
| maintenance.getMessage() | ||
| ); | ||
| } | ||
| } |
7 changes: 7 additions & 0 deletions
7
src/main/java/com/gpt/geumpumtabackend/maintenance/repository/MaintenanceRepository.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| package com.gpt.geumpumtabackend.maintenance.repository; | ||
|
|
||
| import com.gpt.geumpumtabackend.maintenance.domain.Maintenance; | ||
| import org.springframework.data.jpa.repository.JpaRepository; | ||
|
|
||
| public interface MaintenanceRepository extends JpaRepository<Maintenance, Long> { | ||
| } |
41 changes: 41 additions & 0 deletions
41
src/main/java/com/gpt/geumpumtabackend/maintenance/service/MaintenanceService.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| package com.gpt.geumpumtabackend.maintenance.service; | ||
|
|
||
| import com.gpt.geumpumtabackend.maintenance.domain.Maintenance; | ||
| import com.gpt.geumpumtabackend.maintenance.domain.ServiceStatus; | ||
| import com.gpt.geumpumtabackend.maintenance.dto.request.MaintenanceStatusUpdateRequest; | ||
| import com.gpt.geumpumtabackend.maintenance.dto.response.MaintenanceStatusResponse; | ||
| import com.gpt.geumpumtabackend.maintenance.repository.MaintenanceRepository; | ||
| import lombok.RequiredArgsConstructor; | ||
| import org.springframework.stereotype.Service; | ||
| import org.springframework.transaction.annotation.Transactional; | ||
|
|
||
| @Service | ||
| @RequiredArgsConstructor | ||
| @Transactional(readOnly = true) | ||
| public class MaintenanceService { | ||
|
|
||
| private final MaintenanceRepository maintenanceRepository; | ||
|
|
||
| @Transactional | ||
| public MaintenanceStatusResponse updateStatus(MaintenanceStatusUpdateRequest request) { | ||
| Maintenance maintenance = maintenanceRepository.findById(Maintenance.DEFAULT_ID) | ||
| .orElseGet(() -> Maintenance.initialize(request.status(), request.message())); | ||
|
|
||
| maintenance.update(request.status(), request.message()); | ||
|
|
||
| return MaintenanceStatusResponse.from(maintenanceRepository.save(maintenance)); | ||
| } | ||
|
|
||
| public MaintenanceStatusResponse getCurrentStatus() { | ||
| Maintenance maintenance = maintenanceRepository.findById(Maintenance.DEFAULT_ID) | ||
| .orElseGet(() -> Maintenance.initialize( | ||
| ServiceStatus.NORMAL, | ||
| null | ||
| )); | ||
| return MaintenanceStatusResponse.from(maintenance); | ||
| } | ||
|
|
||
| public boolean isMaintenanceInProgress() { | ||
| return getCurrentStatus().status() == ServiceStatus.MAINTENANCE; | ||
| } | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.