|
@@ -0,0 +1,82 @@
|
|
|
|
|
+package com.pharmacopoeia.security;
|
|
|
|
|
+
|
|
|
|
|
+import com.pharmacopoeia.service.AnalyticsService;
|
|
|
|
|
+import com.pharmacopoeia.util.IpUtils;
|
|
|
|
|
+import jakarta.servlet.FilterChain;
|
|
|
|
|
+import jakarta.servlet.ServletException;
|
|
|
|
|
+import jakarta.servlet.http.HttpServletRequest;
|
|
|
|
|
+import jakarta.servlet.http.HttpServletResponse;
|
|
|
|
|
+import org.jetbrains.annotations.NotNull;
|
|
|
|
|
+import org.springframework.stereotype.Component;
|
|
|
|
|
+import org.springframework.web.filter.OncePerRequestFilter;
|
|
|
|
|
+
|
|
|
|
|
+import java.io.IOException;
|
|
|
|
|
+import java.util.Map;
|
|
|
|
|
+
|
|
|
|
|
+@Component
|
|
|
|
|
+public class ApiTrackingFilter extends OncePerRequestFilter {
|
|
|
|
|
+
|
|
|
|
|
+ private final AnalyticsService analyticsService;
|
|
|
|
|
+
|
|
|
|
|
+ public ApiTrackingFilter(AnalyticsService analyticsService) {
|
|
|
|
|
+ this.analyticsService = analyticsService;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ protected void doFilterInternal(@NotNull HttpServletRequest request,
|
|
|
|
|
+ @NotNull HttpServletResponse response,
|
|
|
|
|
+ @NotNull FilterChain filterChain) throws ServletException, IOException {
|
|
|
|
|
+ String requestUri = request.getRequestURI();
|
|
|
|
|
+
|
|
|
|
|
+ if (!shouldTrack(requestUri)) {
|
|
|
|
|
+ filterChain.doFilter(request, response);
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ long startTime = System.currentTimeMillis();
|
|
|
|
|
+ String ip = IpUtils.getClientIp(request);
|
|
|
|
|
+ String userAgent = request.getHeader("User-Agent");
|
|
|
|
|
+ String method = request.getMethod();
|
|
|
|
|
+ String referrer = request.getHeader("Referer");
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ filterChain.doFilter(request, response);
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ long elapsedMs = System.currentTimeMillis() - startTime;
|
|
|
|
|
+ int status = response.getStatus();
|
|
|
|
|
+ String userKey = getCurrentUserKey();
|
|
|
|
|
+
|
|
|
|
|
+ Map<String, Object> eventData = Map.of(
|
|
|
|
|
+ "url", requestUri,
|
|
|
|
|
+ "method", method,
|
|
|
|
|
+ "status", status,
|
|
|
|
|
+ "elapsed_ms", elapsedMs
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ analyticsService.saveEvent(
|
|
|
|
|
+ userKey,
|
|
|
|
|
+ "api_call",
|
|
|
|
|
+ eventData,
|
|
|
|
|
+ requestUri,
|
|
|
|
|
+ referrer != null ? referrer : "",
|
|
|
|
|
+ ip,
|
|
|
|
|
+ userAgent != null ? userAgent : ""
|
|
|
|
|
+ );
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private boolean shouldTrack(String requestUri) {
|
|
|
|
|
+ // Don't track the analytics endpoint itself to avoid infinite loop
|
|
|
|
|
+ return !requestUri.startsWith("/api/v1/analytics/events");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private String getCurrentUserKey() {
|
|
|
|
|
+ var auth = org.springframework.security.core.context.SecurityContextHolder
|
|
|
|
|
+ .getContext().getAuthentication();
|
|
|
|
|
+ if (auth != null && auth.isAuthenticated()
|
|
|
|
|
+ && !"anonymousUser".equals(auth.getPrincipal())) {
|
|
|
|
|
+ return auth.getName();
|
|
|
|
|
+ }
|
|
|
|
|
+ return "anonymous";
|
|
|
|
|
+ }
|
|
|
|
|
+}
|