diff --git a/marketplace-build/.env b/marketplace-build/.env index 60614280..c5e3d31e 100644 --- a/marketplace-build/.env +++ b/marketplace-build/.env @@ -13,4 +13,6 @@ MARKET_GITHUB_OAUTH_APP_CLIENT_SECRET= MARKET_JWT_SECRET_KEY= MARKET_CORS_ALLOWED_ORIGIN=* MARKET_MONGO_LOG_LEVEL=DEBUG -MARKET_LOG_PATH=logs \ No newline at end of file +MARKET_LOG_PATH=logs +MARKET_CLICK_LIMIT= +MARKET_LIMITED_REQUEST_PATHS= diff --git a/marketplace-build/dev/.env b/marketplace-build/dev/.env index 09f4215a..5508256c 100644 --- a/marketplace-build/dev/.env +++ b/marketplace-build/dev/.env @@ -13,4 +13,6 @@ MARKET_GITHUB_OAUTH_APP_CLIENT_SECRET= MARKET_JWT_SECRET_KEY= MARKET_CORS_ALLOWED_ORIGIN=* MARKET_MONGO_LOG_LEVEL=DEBUG -MARKET_LOG_PATH=logs \ No newline at end of file +MARKET_LOG_PATH=logs +MARKET_CLICK_LIMIT= +MARKET_LIMITED_REQUEST_PATHS= diff --git a/marketplace-build/dev/docker-compose.yml b/marketplace-build/dev/docker-compose.yml index fdc6c6b7..37f33c63 100644 --- a/marketplace-build/dev/docker-compose.yml +++ b/marketplace-build/dev/docker-compose.yml @@ -38,6 +38,8 @@ services: - MARKET_CORS_ALLOWED_ORIGIN=${MARKET_CORS_ALLOWED_ORIGIN} - MARKET_MONGO_LOG_LEVEL=${MARKET_MONGO_LOG_LEVEL} - MARKET_LOG_PATH=${MARKET_LOG_PATH} + - MARKET_CLICK_LIMIT=${MARKET_CLICK_LIMIT} + - MARKET_LIMITED_REQUEST_PATHS=${MARKET_LIMITED_REQUEST_PATHS} build: context: ../../marketplace-service dockerfile: Dockerfile @@ -53,4 +55,4 @@ volumes: networks: marketplace-network: - external: true \ No newline at end of file + external: true diff --git a/marketplace-build/docker-compose.yml b/marketplace-build/docker-compose.yml index 49c421c6..a8f8e184 100644 --- a/marketplace-build/docker-compose.yml +++ b/marketplace-build/docker-compose.yml @@ -38,6 +38,8 @@ services: - MARKET_CORS_ALLOWED_ORIGIN=${MARKET_CORS_ALLOWED_ORIGIN} - MARKET_MONGO_LOG_LEVEL=${MARKET_MONGO_LOG_LEVEL} - MARKET_LOG_PATH=${MARKET_LOG_PATH} + - MARKET_CLICK_LIMIT=${MARKET_CLICK_LIMIT} + - MARKET_LIMITED_REQUEST_PATHS=${MARKET_LIMITED_REQUEST_PATHS} build: context: ../marketplace-service dockerfile: Dockerfile @@ -53,4 +55,4 @@ volumes: networks: marketplace-network: - external: true \ No newline at end of file + external: true diff --git a/marketplace-build/release/.env b/marketplace-build/release/.env index fd7ac701..02feece4 100644 --- a/marketplace-build/release/.env +++ b/marketplace-build/release/.env @@ -14,4 +14,6 @@ MARKET_GITHUB_OAUTH_APP_CLIENT_SECRET= MARKET_JWT_SECRET_KEY= MARKET_CORS_ALLOWED_ORIGIN=* MARKET_MONGO_LOG_LEVEL=DEBUG -MARKET_LOG_PATH=logs \ No newline at end of file +MARKET_LOG_PATH=logs +MARKET_CLICK_LIMIT= +MARKET_LIMITED_REQUEST_PATHS= diff --git a/marketplace-build/release/docker-compose.yml b/marketplace-build/release/docker-compose.yml index f483fba1..e64dabd5 100644 --- a/marketplace-build/release/docker-compose.yml +++ b/marketplace-build/release/docker-compose.yml @@ -36,6 +36,8 @@ services: - MARKET_CORS_ALLOWED_ORIGIN=${MARKET_CORS_ALLOWED_ORIGIN} - MARKET_MONGO_LOG_LEVEL=${MARKET_MONGO_LOG_LEVEL} - MARKET_LOG_PATH=${MARKET_LOG_PATH} + - MARKET_CLICK_LIMIT=${MARKET_CLICK_LIMIT} + - MARKET_LIMITED_REQUEST_PATHS=${MARKET_LIMITED_REQUEST_PATHS} networks: - marketplace-network diff --git a/marketplace-service/src/main/java/com/axonivy/market/config/RateLimitFilter.java b/marketplace-service/src/main/java/com/axonivy/market/config/LimitCallingConfig.java similarity index 70% rename from marketplace-service/src/main/java/com/axonivy/market/config/RateLimitFilter.java rename to marketplace-service/src/main/java/com/axonivy/market/config/LimitCallingConfig.java index 2cebca00..1ae034e7 100644 --- a/marketplace-service/src/main/java/com/axonivy/market/config/RateLimitFilter.java +++ b/marketplace-service/src/main/java/com/axonivy/market/config/LimitCallingConfig.java @@ -2,25 +2,30 @@ import io.github.bucket4j.Bandwidth; import io.github.bucket4j.Bucket; -import io.github.bucket4j.Refill; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; +import lombok.extern.log4j.Log4j2; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import org.springframework.web.filter.OncePerRequestFilter; import java.io.IOException; import java.time.Duration; +import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +@Log4j2 @Component -public class RateLimitFilter extends OncePerRequestFilter { - private static final String REQUEST_PATH = "/api"; - @Value("${market.allowed.download-capacity}") +public class LimitCallingConfig extends OncePerRequestFilter { + private static final String REQUEST_HEADER = "X-Forwarded-For"; + @Value("${market.allowed.click-capacity}") private int capacity; + + @Value("${market.limited.request-paths}") + private List requestPaths; private final Map buckets = new ConcurrentHashMap<>(); @Override @@ -29,15 +34,13 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse String clientIp = getClientIp(request); String apiPath = request.getRequestURI(); - - if (apiPath.contains(REQUEST_PATH)) { + if (requestPaths.stream().anyMatch(path -> apiPath.matches(path + ".*"))) { Bucket bucket = buckets.computeIfAbsent(clientIp, this::createNewBucket); if (bucket.tryConsume(1)) { - System.out.println("Request allowed for IP: " + clientIp + ". Remaining tokens: " + bucket.getAvailableTokens()); + log.warn("Request allowed for IP: {}. Remaining tokens: {}", clientIp, bucket.getAvailableTokens()); filterChain.doFilter(request, response); } else { - System.out.println("Too many requests from IP: " + clientIp); response.setStatus(HttpServletResponse.SC_BAD_GATEWAY); response.getWriter().write("Too many requests. Please try again later."); } @@ -47,12 +50,15 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse } private Bucket createNewBucket(String clientIp) { - Bandwidth limit = Bandwidth.classic(capacity, Refill.greedy(10, Duration.ofMinutes(2))); + Bandwidth limit = Bandwidth.builder() + .capacity(capacity) + .refillGreedy(capacity, Duration.ofMinutes(1)) + .build(); return Bucket.builder().addLimit(limit).build(); } private String getClientIp(HttpServletRequest request) { - String forwardedFor = request.getHeader("X-Forwarded-For"); + String forwardedFor = request.getHeader(REQUEST_HEADER); if (forwardedFor != null && !forwardedFor.isEmpty()) { return forwardedFor.split(",")[0]; } diff --git a/marketplace-service/src/main/resources/application.properties b/marketplace-service/src/main/resources/application.properties index a0ab0a26..5b1fbf5c 100644 --- a/marketplace-service/src/main/resources/application.properties +++ b/marketplace-service/src/main/resources/application.properties @@ -17,4 +17,6 @@ jwt.secret=${MARKET_JWT_SECRET_KEY} jwt.expiration=365 logging.level.org.springframework.data.mongodb.core.MongoTemplate=${MARKET_MONGO_LOG_LEVEL} spring.jackson.serialization.indent_output=true -loggable.log-path=${MARKET_LOG_PATH} \ No newline at end of file +loggable.log-path=${MARKET_LOG_PATH} +market.allowed.click-capacity=${MARKET_CLICK_LIMIT} +market.limited.request-paths=${MARKET_LIMITED_REQUEST_PATHS} \ No newline at end of file