feat: attacker profiles — UUID model, API routes, list/detail frontend
Migrate Attacker model from IP-based to UUID-based primary key with
auto-migration for old schema. Add GET /attackers (paginated, search,
sort) and GET /attackers/{uuid} API routes. Rewrite Attackers.tsx as
a card grid with full threat info and create AttackerDetail.tsx as a
dedicated detail page with back navigation, stats, commands table,
and fingerprints.
This commit is contained in:
@@ -96,16 +96,36 @@ class BaseRepository(ABC):
|
||||
"""Retrieve all log rows with fields needed by the attacker profile worker."""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def get_max_log_id(self) -> int:
|
||||
"""Return the highest log ID, or 0 if the table is empty."""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def get_logs_after_id(self, last_id: int, limit: int = 500) -> list[dict[str, Any]]:
|
||||
"""Return logs with id > last_id, ordered by id ASC, up to limit."""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def get_all_bounties_by_ip(self) -> dict[str, list[dict[str, Any]]]:
|
||||
"""Retrieve all bounty rows grouped by attacker_ip."""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def get_bounties_for_ips(self, ips: set[str]) -> dict[str, list[dict[str, Any]]]:
|
||||
"""Retrieve bounty rows grouped by attacker_ip, filtered to only the given IPs."""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def upsert_attacker(self, data: dict[str, Any]) -> None:
|
||||
"""Insert or replace an attacker profile record."""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def get_attacker_by_uuid(self, uuid: str) -> Optional[dict[str, Any]]:
|
||||
"""Retrieve a single attacker profile by UUID."""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def get_attackers(
|
||||
self,
|
||||
|
||||
Reference in New Issue
Block a user