From c51a936cf999d73389795e67768ea1edde5b0484 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EC=8A=B9=EC=A4=80?= <105282117+sengjun0624@users.noreply.github.com> Date: Wed, 28 May 2025 20:02:05 +0900 Subject: [PATCH 1/3] [DEPLOY] v1.0.1 (#45) * :recycle: refactor: Update CorsFilter (#40) * :sparkles: Feat: Add Unified Log Service (#42) * :sparkles: feat: Add Unified Log Service * :recycle: refactor: Modify DateTime Type * :recycle: refactor: Add UnifiedLogResponseDto (#44) --- .../dev/admin/global/config/CorsConfig.java | 6 +- .../controller/UnifiedLogController.java | 42 ++++++++++ .../dto/request/UnifiedLogFilterRequest.java | 27 ++++++ .../dto/response/UnifiedLogResponseDto.java | 76 +++++++++++++++++ .../service/query/UnifiedLogQueryService.java | 11 +++ .../query/UnifiedLogQueryServiceImpl.java | 84 +++++++++++++++++++ 6 files changed, 243 insertions(+), 3 deletions(-) create mode 100644 src/main/java/dev/admin/unified_logs/controller/UnifiedLogController.java create mode 100644 src/main/java/dev/admin/unified_logs/dto/request/UnifiedLogFilterRequest.java create mode 100644 src/main/java/dev/admin/unified_logs/dto/response/UnifiedLogResponseDto.java create mode 100644 src/main/java/dev/admin/unified_logs/service/query/UnifiedLogQueryService.java create mode 100644 src/main/java/dev/admin/unified_logs/service/query/UnifiedLogQueryServiceImpl.java diff --git a/src/main/java/dev/admin/global/config/CorsConfig.java b/src/main/java/dev/admin/global/config/CorsConfig.java index 6aeb55b..721f9ec 100644 --- a/src/main/java/dev/admin/global/config/CorsConfig.java +++ b/src/main/java/dev/admin/global/config/CorsConfig.java @@ -11,8 +11,8 @@ @Configuration public class CorsConfig { - private static final String SERVER_URL = "https://xxxxx"; - private static final String FRONT_URL = "https://xxxxx"; + private static final String SERVER_URL = "https://admin.tokkit.site"; + private static final String USER_SERVER_URL = "https://www.tokkit.site"; private static final String FRONT_LOCALHOST_URL = "http://localhost:3000"; private static final String SERVER_LOCALHOST_URL = "http://localhost:8080"; @@ -23,7 +23,7 @@ public CorsFilter corsFilter() { CorsConfiguration config = new CorsConfiguration(); config.setAllowCredentials(true); config.addAllowedOrigin(SERVER_URL); - config.addAllowedOrigin(FRONT_URL); + config.addAllowedOrigin(USER_SERVER_URL); config.addAllowedOrigin(FRONT_LOCALHOST_URL); config.addAllowedOrigin(SERVER_LOCALHOST_URL); config.addAllowedHeader("*"); diff --git a/src/main/java/dev/admin/unified_logs/controller/UnifiedLogController.java b/src/main/java/dev/admin/unified_logs/controller/UnifiedLogController.java new file mode 100644 index 0000000..e2643e3 --- /dev/null +++ b/src/main/java/dev/admin/unified_logs/controller/UnifiedLogController.java @@ -0,0 +1,42 @@ +package dev.admin.unified_logs.controller; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.List; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.data.web.PageableDefault; +import org.springframework.format.annotation.DateTimeFormat; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import dev.admin.global.apiPayload.ApiResponse; +import dev.admin.unified_logs.dto.request.UnifiedLogFilterRequest; +import dev.admin.unified_logs.dto.response.UnifiedLogResponseDto; +import dev.admin.unified_logs.service.query.UnifiedLogQueryService; +import lombok.RequiredArgsConstructor; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/admin-api/unified-logs") +public class UnifiedLogController { + private final UnifiedLogQueryService unifiedLogQueryService; + + @GetMapping + public ApiResponse> getLogs( + @RequestParam(required = false) String traceId, + @RequestParam(required = false) Long userId, + @RequestParam(required = false) Long merchantId, + @RequestParam(required = false) List logTypes, + @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate from, + @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate to, + @PageableDefault(size = 20, sort = "timestamp", direction = Sort.Direction.DESC) Pageable pageable + ) { + UnifiedLogFilterRequest filter = new UnifiedLogFilterRequest(traceId, userId, merchantId, logTypes, from, to); + return ApiResponse.onSuccess(unifiedLogQueryService.getLogs(filter, pageable)); + } +} diff --git a/src/main/java/dev/admin/unified_logs/dto/request/UnifiedLogFilterRequest.java b/src/main/java/dev/admin/unified_logs/dto/request/UnifiedLogFilterRequest.java new file mode 100644 index 0000000..98fd65f --- /dev/null +++ b/src/main/java/dev/admin/unified_logs/dto/request/UnifiedLogFilterRequest.java @@ -0,0 +1,27 @@ +package dev.admin.unified_logs.dto.request; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.List; + +import io.swagger.v3.oas.annotations.media.Schema; + +public record UnifiedLogFilterRequest( + @Schema(description = "로그 traceId (선택)") + String traceId, + + @Schema(description = "사용자 ID (선택)") + Long userId, + + @Schema(description = "가맹점 ID (선택)") + Long merchantId, + + @Schema(description = "로그 타입 필터: LOGIN, ERROR, API, TRANSACTION") + List logTypes, + + @Schema(description = "조회 시작 시간 (ISO 8601)") + LocalDate from, + + @Schema(description = "조회 종료 시간 (ISO 8601)") + LocalDate to +) {} diff --git a/src/main/java/dev/admin/unified_logs/dto/response/UnifiedLogResponseDto.java b/src/main/java/dev/admin/unified_logs/dto/response/UnifiedLogResponseDto.java new file mode 100644 index 0000000..d6f5186 --- /dev/null +++ b/src/main/java/dev/admin/unified_logs/dto/response/UnifiedLogResponseDto.java @@ -0,0 +1,76 @@ +package dev.admin.unified_logs.dto.response; + +import java.time.LocalDateTime; + +import dev.admin.api_request_log.entity.ApiRequestLog; +import dev.admin.login_log.entity.LoginLog; +import dev.admin.system_error_log.entity.SystemErrorLog; +import dev.admin.transaction.entity.Transaction; + +public record UnifiedLogResponseDto( + Long id, + String logType, + String traceId, + Long userId, + Long merchantId, + LocalDateTime timestamp, + String summary, + String detail, + String statusOrSeverity +) { + public static UnifiedLogResponseDto fromLoginLog(LoginLog log) { + return new UnifiedLogResponseDto( + log.getId(), + "LOGIN", + log.getTraceId(), + log.getUserId(), + log.getMerchantId(), + log.getTimestamp(), + log.getEvent() + " - " + log.getIpAddress(), + log.getUserAgent(), + log.getSuccess() ? "SUCCESS" : "FAILURE" + ); + } + + public static UnifiedLogResponseDto fromApiRequestLog(ApiRequestLog log) { + return new UnifiedLogResponseDto( + log.getId(), + "API", + log.getTraceId(), + log.getUserId(), + log.getMerchantId(), + log.getTimestamp(), + log.getMethod() + " " + log.getEndpoint(), + log.getQueryParams() + "\n" + log.getRequestBody(), + String.valueOf(log.getResponseStatus()) + ); + } + + public static UnifiedLogResponseDto fromSystemErrorLog(SystemErrorLog log) { + return new UnifiedLogResponseDto( + log.getId(), + "ERROR", + log.getTraceId(), + log.getUserId(), + log.getMerchantId(), + log.getTimestamp(), + log.getEndpoint() + " - " + log.getErrorMessage(), + log.getStackTrace(), + log.getSeverity().toString() + ); + } + + public static UnifiedLogResponseDto fromTransactionLog(Transaction log) { + return new UnifiedLogResponseDto( + log.getId(), + "TRANSACTION", + log.getTraceId(), + null, + null, + log.getCreatedAt(), + log.getType() + " - " + log.getAmount(), + log.getDescription(), + log.getStatus().toString() + ); + } +} diff --git a/src/main/java/dev/admin/unified_logs/service/query/UnifiedLogQueryService.java b/src/main/java/dev/admin/unified_logs/service/query/UnifiedLogQueryService.java new file mode 100644 index 0000000..6dee036 --- /dev/null +++ b/src/main/java/dev/admin/unified_logs/service/query/UnifiedLogQueryService.java @@ -0,0 +1,11 @@ +package dev.admin.unified_logs.service.query; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; + +import dev.admin.unified_logs.dto.request.UnifiedLogFilterRequest; +import dev.admin.unified_logs.dto.response.UnifiedLogResponseDto; + +public interface UnifiedLogQueryService { + Page getLogs(UnifiedLogFilterRequest filter, Pageable pageable); +} diff --git a/src/main/java/dev/admin/unified_logs/service/query/UnifiedLogQueryServiceImpl.java b/src/main/java/dev/admin/unified_logs/service/query/UnifiedLogQueryServiceImpl.java new file mode 100644 index 0000000..e510746 --- /dev/null +++ b/src/main/java/dev/admin/unified_logs/service/query/UnifiedLogQueryServiceImpl.java @@ -0,0 +1,84 @@ +package dev.admin.unified_logs.service.query; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; + +import dev.admin.api_request_log.repository.ApiRequestLogRepository; +import dev.admin.login_log.repository.LoginLogRepository; +import dev.admin.system_error_log.repository.SystemErrorLogRepository; +import dev.admin.transaction.repository.TransactionRepository; +import dev.admin.unified_logs.dto.request.UnifiedLogFilterRequest; +import dev.admin.unified_logs.dto.response.UnifiedLogResponseDto; +import lombok.RequiredArgsConstructor; + +@Service +@RequiredArgsConstructor +public class UnifiedLogQueryServiceImpl implements UnifiedLogQueryService { + private final LoginLogRepository loginLogRepository; + private final ApiRequestLogRepository apiRequestLogRepository; + private final SystemErrorLogRepository systemErrorLogRepository; + private final TransactionRepository transactionLogRepository; + + @Override + public Page getLogs(UnifiedLogFilterRequest filter, Pageable pageable) { + List result = new ArrayList<>(); + + if (filter.logTypes() == null || filter.logTypes().contains("LOGIN")) { + result.addAll(loginLogRepository.findAll().stream() + .filter(log -> matchesFilter(log.getTraceId(), log.getUserId(), log.getMerchantId(), log.getTimestamp(), filter)) + .map(UnifiedLogResponseDto::fromLoginLog).toList()); + } + + if (filter.logTypes() == null || filter.logTypes().contains("API")) { + result.addAll(apiRequestLogRepository.findAll().stream() + .filter(log -> matchesFilter(log.getTraceId(), log.getUserId(), log.getMerchantId(), log.getTimestamp(), filter)) + .map(UnifiedLogResponseDto::fromApiRequestLog).toList()); + } + + if (filter.logTypes() == null || filter.logTypes().contains("ERROR")) { + result.addAll(systemErrorLogRepository.findAll().stream() + .filter(log -> matchesFilter(log.getTraceId(), log.getUserId(), log.getMerchantId(), log.getTimestamp(), filter)) + .map(UnifiedLogResponseDto::fromSystemErrorLog).toList()); + } + + if (filter.logTypes() == null || filter.logTypes().contains("TRANSACTION")) { + result.addAll(transactionLogRepository.findAll().stream() + .filter(log -> matchesFilter(log.getTraceId(), null, null, log.getCreatedAt(), filter)) + .map(UnifiedLogResponseDto::fromTransactionLog).toList()); + } + + result.sort(Comparator.comparing(UnifiedLogResponseDto::timestamp).reversed()); + + int start = (int) pageable.getOffset(); + int end = Math.min((start + pageable.getPageSize()), result.size()); + List pageContent = result.subList(start, end); + return new PageImpl<>(pageContent, pageable, result.size()); + } + + private boolean matchesFilter(String traceId, Long userId, Long merchantId, LocalDateTime timestamp, UnifiedLogFilterRequest filter) { + boolean match = true; + if (filter.traceId() != null) match &= filter.traceId().equals(traceId); + if (filter.userId() != null) match &= filter.userId().equals(userId); + if (filter.merchantId() != null) match &= filter.merchantId().equals(merchantId); + + if (filter.from() != null) { + LocalDateTime fromDateTime = filter.from().atStartOfDay(); + match &= !timestamp.isBefore(fromDateTime); + } + + if (filter.to() != null) { + LocalDateTime toDateTime = filter.to().plusDays(1).atStartOfDay().minusNanos(1); + match &= !timestamp.isAfter(toDateTime); + } + + return match; + } + +} From 750e937df0d63d3505392fe9d262f504df20ef50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9C=A4=EC=A3=BC?= <155442976+iamyuunzo@users.noreply.github.com> Date: Wed, 4 Jun 2025 17:17:15 +0900 Subject: [PATCH 2/3] =?UTF-8?q?REFACTOR:=20admin=20->=20admin-api=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20=EB=B0=8F=20TransactionType=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20(#51)=20(#52)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dev/admin/admin/controller/AdminAuthController.java | 2 +- src/main/java/dev/admin/global/config/SecurityConfig.java | 6 +++--- .../java/dev/admin/transaction/enums/TransactionType.java | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/dev/admin/admin/controller/AdminAuthController.java b/src/main/java/dev/admin/admin/controller/AdminAuthController.java index 90d7c08..35f9c4e 100644 --- a/src/main/java/dev/admin/admin/controller/AdminAuthController.java +++ b/src/main/java/dev/admin/admin/controller/AdminAuthController.java @@ -14,7 +14,7 @@ import org.springframework.web.bind.annotation.*; @RestController -@RequestMapping("/admin") +@RequestMapping("/admin-api") @RequiredArgsConstructor public class AdminAuthController { diff --git a/src/main/java/dev/admin/global/config/SecurityConfig.java b/src/main/java/dev/admin/global/config/SecurityConfig.java index 5e0e3e7..2d9dba6 100644 --- a/src/main/java/dev/admin/global/config/SecurityConfig.java +++ b/src/main/java/dev/admin/global/config/SecurityConfig.java @@ -37,12 +37,12 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { .requestMatchers("/test/**").permitAll() // ✅ 여기를 추가! .requestMatchers( - "/admin/login", - "/admin/logout", + "/admin-api/login", + "/admin-api/logout", "/swagger-ui.html", "/swagger-ui/**", "/v3/api-docs/**" - ).permitAll() .requestMatchers("/admin/me", "/admin-api/**").authenticated() + ).permitAll() .requestMatchers("/admin-api/me", "/admin-api/**").authenticated() .anyRequest().denyAll() ) .sessionManagement(sess -> sess.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) diff --git a/src/main/java/dev/admin/transaction/enums/TransactionType.java b/src/main/java/dev/admin/transaction/enums/TransactionType.java index 0a09734..ec33847 100644 --- a/src/main/java/dev/admin/transaction/enums/TransactionType.java +++ b/src/main/java/dev/admin/transaction/enums/TransactionType.java @@ -1,5 +1,5 @@ package dev.admin.transaction.enums; public enum TransactionType { - DEPOSIT, WITHDRAW, CONVERT, PURCHASE, REFUND, RECEIVE + DEPOSIT, WITHDRAW, CONVERT, PURCHASE, REFUND, RECEIVE,AUTO_CONVERT } From e264b0b484873844368bf5d2a4c9bf0f3875909f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EC=8A=B9=EC=A4=80?= <105282117+sengjun0624@users.noreply.github.com> Date: Wed, 4 Jun 2025 20:32:50 +0900 Subject: [PATCH 3/3] [DEPLOY] v1.0.4 (#55) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * REFACTOR: admin -> admin-api 수정 및 TransactionType 추가 (#51) * :recycle: refactor: HealthCheck Controller가 Security에 안걸리게 수정 (#54) --------- Co-authored-by: 조윤주 <155442976+iamyuunzo@users.noreply.github.com> --- .../java/dev/admin/global/config/SecurityConfig.java | 11 ++++++----- .../java/dev/admin/global/config/SwaggerConfig.java | 5 ++++- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/main/java/dev/admin/global/config/SecurityConfig.java b/src/main/java/dev/admin/global/config/SecurityConfig.java index 2d9dba6..d97c359 100644 --- a/src/main/java/dev/admin/global/config/SecurityConfig.java +++ b/src/main/java/dev/admin/global/config/SecurityConfig.java @@ -34,15 +34,16 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { return http .csrf(csrf -> csrf.disable()) .authorizeHttpRequests(auth -> auth - .requestMatchers("/test/**").permitAll() // ✅ 여기를 추가! + .requestMatchers("/admin-api/health").permitAll() // ✅ 여기를 추가! .requestMatchers( "/admin-api/login", "/admin-api/logout", - "/swagger-ui.html", - "/swagger-ui/**", - "/v3/api-docs/**" - ).permitAll() .requestMatchers("/admin-api/me", "/admin-api/**").authenticated() + "/admin-api/swagger-ui.html", + "/admin-api/swagger-ui/**", + "/admin-api/v3/api-docs/**" + ).permitAll() + .requestMatchers("/admin-api/me", "/admin-api/**").authenticated() .anyRequest().denyAll() ) .sessionManagement(sess -> sess.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) diff --git a/src/main/java/dev/admin/global/config/SwaggerConfig.java b/src/main/java/dev/admin/global/config/SwaggerConfig.java index fc3ee55..f998316 100644 --- a/src/main/java/dev/admin/global/config/SwaggerConfig.java +++ b/src/main/java/dev/admin/global/config/SwaggerConfig.java @@ -43,6 +43,9 @@ public OpenAPI openAPI() { Server localServer = new Server() .url("http://localhost:8080") .description("Local development server"); + Server prodServer = new Server() + .url("https://api.tokkit.site:8080") + .description("Local development server"); return new OpenAPI() .components(new Components() @@ -53,6 +56,6 @@ public OpenAPI openAPI() { .title("토킷(TOKKIT) ADMIN API 명세서") .description("토킷(TOKKIT) ADMIN API 명세서입니다.") .version("1.0.0")) - .servers(List.of(defaultServer, localServer)); + .servers(List.of(defaultServer, localServer,prodServer)); } }