gendesign/tradein-mvp/backend/app/schemas/audit.py
bot-backend 780af144c3
All checks were successful
CI Trade-In / changes (pull_request) Successful in 9s
CI / changes (pull_request) Successful in 9s
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / backend-tests (pull_request) Successful in 1m0s
feat(tradein/admin): audit + analytics read API over user_events
2026-07-13 23:22:35 +03:00

146 lines
4.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Pydantic-схемы admin read API над `user_events` (Feature 2 audit + Feature 3 analytics).
`user_events` (migration `184_user_events.sql`) — unified append-only событийный лог
(login/IP audit + behavior analytics), admin-read-only. Эти схемы описывают ответы
`GET /api/v1/admin/audit/*` и `GET /api/v1/admin/analytics` (см. app/api/v1/audit.py).
"""
from __future__ import annotations
from datetime import date, datetime
from pydantic import BaseModel, ConfigDict
class AccountSummary(BaseModel):
"""Одна строка списка `GET /audit/accounts` — сводка по одному username."""
model_config = ConfigDict(from_attributes=True)
username: str
first_seen_at: datetime
last_seen_at: datetime
distinct_ips: int
distinct_devices: int
login_count: int
request_count: int
search_count: int
class AccountIpEntry(BaseModel):
"""Одна строка `ips` в drilldown `GET /audit/accounts/{username}`."""
model_config = ConfigDict(from_attributes=True)
ip_address: str | None = None
event_count: int
first_seen: datetime
last_seen: datetime
class AccountDeviceEntry(BaseModel):
"""Одна строка `devices` в drilldown `GET /audit/accounts/{username}`."""
model_config = ConfigDict(from_attributes=True)
user_agent: str | None = None
event_count: int
first_seen: datetime
last_seen: datetime
class AccountSearchEntry(BaseModel):
"""Одна строка `searches` в drilldown — недавний estimate_request."""
model_config = ConfigDict(from_attributes=True)
address: str | None = None
area_m2: str | None = None
rooms: str | None = None
estimate_id: str | None = None
ip_address: str | None = None
created_at: datetime
class AccountActivityEntry(BaseModel):
"""Одна строка `recent_activity` в drilldown — сырое событие."""
model_config = ConfigDict(from_attributes=True)
event_type: str
path: str | None = None
method: str | None = None
ip_address: str | None = None
created_at: datetime
class AccountDrilldown(BaseModel):
"""Полный ответ `GET /audit/accounts/{username}` — 4 списка.
Неизвестный username НЕ 404 — это просто пустой отчёт (все списки == []).
"""
ips: list[AccountIpEntry]
devices: list[AccountDeviceEntry]
searches: list[AccountSearchEntry]
recent_activity: list[AccountActivityEntry]
class AnalyticsSummary(BaseModel):
"""Верхнеуровневые счётчики дашборда `GET /analytics`."""
model_config = ConfigDict(from_attributes=True)
total_events: int
distinct_users: int
events_last_24h: int
active_users_last_24h: int
class AnalyticsDailyPoint(BaseModel):
"""Одна точка time-series `daily` — события/пользователи за день."""
model_config = ConfigDict(from_attributes=True)
day: date
events: int
users: int
class AnalyticsTopSearch(BaseModel):
"""Одна строка `top_searches` — самый частый искомый адрес."""
model_config = ConfigDict(from_attributes=True)
address: str | None = None
n: int
class AnalyticsTopPath(BaseModel):
"""Одна строка `top_paths` — самый частый API-путь."""
model_config = ConfigDict(from_attributes=True)
path: str | None = None
n: int
class AnalyticsByAccount(BaseModel):
"""Одна строка `by_account` — сводка активности по username."""
model_config = ConfigDict(from_attributes=True)
username: str
events: int
searches: int
last_seen: datetime
class AnalyticsDashboard(BaseModel):
"""Полный ответ `GET /analytics` — бандл для дашборда Feature 3."""
summary: AnalyticsSummary
daily: list[AnalyticsDailyPoint]
top_searches: list[AnalyticsTopSearch]
top_paths: list[AnalyticsTopPath]
by_account: list[AnalyticsByAccount]