"신규 신청 쿨다운 로직 제거 - Repository/Service/Controller 코드 정리 - 불필요한 HTML 스크립트 제거"
eapim-portal CI / build (push) Has been cancelled
eapim-portal Test / test (push) Has been cancelled

This commit is contained in:
Rinjae
2026-08-26 09:43:00 +09:00
parent b58893d098
commit 2bc2a3d6e3
3 changed files with 0 additions and 62 deletions
@@ -16,7 +16,6 @@ import com.eactive.apim.portal.apps.auth.twofactor.StepUpProtectedPaths;
import com.eactive.apim.portal.apps.auth.twofactor.TwoFactorProperties;
import com.eactive.apim.portal.apps.auth.twofactor.TwoFactorService;
import com.eactive.apim.portal.common.exception.UserErrorMessageResolver;
import com.eactive.apim.portal.common.security.WriteRateLimitService;
import com.eactive.apim.portal.common.user.PortalAuthenticatedUser;
import com.eactive.apim.portal.common.util.ApiServiceHelper;
import com.eactive.apim.portal.common.util.SecurityUtil;
@@ -89,7 +88,6 @@ public class MyAppController {
private final FileTypeDetector fileTypeDetector;
private final TwoFactorService twoFactorService;
private final TwoFactorProperties twoFactorProperties;
private final WriteRateLimitService writeRateLimitService;
private static final long MAX_APP_ICON_BYTES = 2L * 1024 * 1024; // 2MB
@@ -532,18 +530,9 @@ public class MyAppController {
@RequestParam(value = "clear", required = false, defaultValue = "false") boolean clear,
@ModelAttribute("apiKeyRegistration") ApiKeyRegistrationDTO registration,
SessionStatus sessionStatus,
RedirectAttributes redirectAttributes,
Model model) {
PortalAuthenticatedUser user = SecurityUtil.getPortalAuthenticatedUser();
// 재신청 쿨다운이면 입력을 다 하고 막히지 않도록 진입 시점에 되돌린다(최종 차단은 step2 가 한다).
long cooldownMinutes = writeRateLimitService.newAppRequestCooldownRemainingMinutes();
if (cooldownMinutes > 0) {
sessionStatus.setComplete();
redirectAttributes.addFlashAttribute("error", writeRateLimitService.cooldownMessage(cooldownMinutes));
return new ModelAndView("redirect:/clients");
}
// 명시적으로 요청된 경우에만 세션 초기화 (새 등록 시작)
// 페이지 새로고침이나 뒤로가기 시에는 세션 데이터를 보존
if (clear) {
@@ -728,14 +717,6 @@ public class MyAppController {
return new ModelAndView("redirect:/clients/register/step1");
}
// 무제한 신청 차단 — 신규 신청은 1시간에 1건으로 고정한다(PortalProperty 무관).
long cooldownMinutes = writeRateLimitService.newAppRequestCooldownRemainingMinutes();
if (cooldownMinutes > 0) {
sessionStatus.setComplete();
redirectAttributes.addFlashAttribute("error", writeRateLimitService.cooldownMessage(cooldownMinutes));
return new ModelAndView("redirect:/clients");
}
PortalAuthenticatedUser user = SecurityUtil.getPortalAuthenticatedUser();
try {
@@ -1,8 +1,5 @@
package com.eactive.apim.portal.common.security;
import com.eactive.apim.portal.apprequest.entity.AppRequest;
import com.eactive.apim.portal.apprequest.entity.AppRequestType;
import com.eactive.apim.portal.apprequest.repository.AppRequestRepository;
import com.eactive.apim.portal.apps.community.partnership.repository.PartnershipApplicationRepository;
import com.eactive.apim.portal.apps.community.qna.repository.InquiryRepository;
import com.eactive.apim.portal.common.entity.Auditable;
@@ -20,7 +17,6 @@ import java.time.Duration;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
/**
* 작성 요청 빈도 제한(무제한 요청 차단).
@@ -40,9 +36,6 @@ public class WriteRateLimitService {
/** 빈도 판정 기준 구간 */
private static final Duration WINDOW = Duration.ofMinutes(1);
/** 클라이언트/API 신규 이용신청 재신청 금지 시간. PortalProperty 와 무관한 고정값. */
public static final Duration NEW_APP_REQUEST_COOLDOWN = Duration.ofHours(1);
/** 빈도 제한 대상. 대상별로 카운터가 독립이다. */
public enum WriteTarget {
INQUIRY("Q&A 문의"),
@@ -64,7 +57,6 @@ public class WriteRateLimitService {
private final InquiryRepository inquiryRepository;
private final InquiryCommentRepository inquiryCommentRepository;
private final PartnershipApplicationRepository partnershipApplicationRepository;
private final AppRequestRepository appRequestRepository;
/** 현재 로그인 사용자 기준으로 허용 빈도를 넘었는지 */
public boolean isExceeded(WriteTarget target) {
@@ -105,35 +97,6 @@ public class WriteRateLimitService {
+ "건까지 가능합니다. 잠시 후 다시 시도해 주세요.";
}
/**
* 클라이언트/API 신규 이용신청 재신청까지 남은 시간(분). 쿨다운이 아니면 0.
* 1분 미만 남았어도 안내 문구를 위해 최소 1을 돌려준다.
*/
@Transactional(readOnly = true)
public long newAppRequestCooldownRemainingMinutes() {
String userId = currentUserId();
if (userId == null || userId.isEmpty()) {
return 0;
}
Optional<AppRequest> latest = appRequestRepository
.findFirstByApproval_Requester_IdAndTypeOrderByCreatedDateDesc(userId, AppRequestType.NEW);
if (!latest.isPresent() || latest.get().getCreatedDate() == null) {
return 0;
}
LocalDateTime available = latest.get().getCreatedDate().plus(NEW_APP_REQUEST_COOLDOWN);
LocalDateTime now = LocalDateTime.now();
if (!available.isAfter(now)) {
return 0;
}
return Math.max(1, Duration.between(now, available).toMinutes());
}
/** 쿨다운 안내 문구 */
public String cooldownMessage(long remainingMinutes) {
return "클라이언트/API 이용신청은 1시간에 1건까지 가능합니다. 약 " + remainingMinutes + "분 후 다시 시도해 주세요.";
}
/** 감사 필드 createdBy 와 동일한 식별자(PortalUser.id). 비인증이면 null. */
private static String currentUserId() {
PortalAuthenticatedUser user = SecurityUtil.getPortalAuthenticatedUser();
@@ -146,12 +146,6 @@
</th:block>
<th:block layout:fragment="contentScript">
<!-- 신청 쿨다운 등으로 되돌아온 경우의 안내 -->
<script th:if="${error}" th:inline="javascript">
document.addEventListener('DOMContentLoaded', function () {
customPopups.showAlert([[${ error }]]);
});
</script>
<script th:inline="javascript">
document.addEventListener('DOMContentLoaded', function () {