"신규 신청 쿨다운 로직 제거 - Repository/Service/Controller 코드 정리 - 불필요한 HTML 스크립트 제거"
This commit is contained in:
@@ -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 () {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user