diff --git a/tradein-mvp/backend/app/core/request_audit.py b/tradein-mvp/backend/app/core/request_audit.py index faa929a5..a217cbfd 100644 --- a/tradein-mvp/backend/app/core/request_audit.py +++ b/tradein-mvp/backend/app/core/request_audit.py @@ -40,14 +40,19 @@ class RequestAuditMiddleware(BaseHTTPMiddleware): ua = request.headers.get("user-agent") # Общий behavior/activity-поток — каждый authenticated API-запрос. - schedule_event( - event_type="api_request", - username=username, - ip=ip, - user_agent=ua, - path=path, - method=request.method, - ) + # /api/v1/admin/* исключаем: это ops-действия (просмотр самих + # дашбордов аудита/аналитики), а не поведение пилота — иначе + # запросы дашборда зашумляют top_paths и счётчики активности. + # login ниже логируем всегда (вход админа с IP — валидный аудит). + if not path.startswith("/api/v1/admin/"): + schedule_event( + event_type="api_request", + username=username, + ip=ip, + user_agent=ua, + path=path, + method=request.method, + ) # Дедуплицированный login/IP-audit сигнал — максимум раз в день # на (юзер, IP, устройство). diff --git a/tradein-mvp/backend/tests/test_request_audit.py b/tradein-mvp/backend/tests/test_request_audit.py index 074502f8..62bbf525 100644 --- a/tradein-mvp/backend/tests/test_request_audit.py +++ b/tradein-mvp/backend/tests/test_request_audit.py @@ -114,3 +114,27 @@ def test_audit_failure_does_not_break_response(client: TestClient) -> None: assert resp.status_code == 200 assert resp.json() == {"ok": True} + + +def test_admin_path_excluded_from_api_request_but_login_kept() -> None: + """/api/v1/admin/* — ops-действия (просмотр дашбордов аудита/аналитики), не + поведение пилота: api_request НЕ пишем (иначе дашборд зашумляет активность), + а login (вход админа с IP) остаётся валидным аудитом.""" + app = FastAPI() + app.add_middleware(RequestAuditMiddleware) + + @app.get("/api/v1/admin/analytics") + def analytics() -> dict[str, bool]: + return {"ok": True} + + with ( + patch("app.core.request_audit.schedule_event") as mock_schedule, + patch("app.core.request_audit.should_log_login", return_value=True), + ): + resp = TestClient(app).get( + "/api/v1/admin/analytics", headers={"X-Authenticated-User": "admin"} + ) + + assert resp.status_code == 200 + event_types = [c.kwargs["event_type"] for c in mock_schedule.call_args_list] + assert event_types == ["login"]