Some checks failed
CI / build (push) Failing after 2s
Phase 4.7 of AUDIT-2026-Q3.md.
Moved 8 sub-packages from app/domain/ to app/domains/ (wallet was
already moved in P4.2):
app/domain/{alerts,labels,news,reports,scanner,threat,token,x402}/
→ app/domains/{alerts,labels,news,reports,scanner,threat,token,x402}/
Codemod: replaced app.domain.X with app.domains.X in 54 files
across the codebase (the canonical path). The shim at app/domain/__init__.py
re-exports from app/domains/ and aliases all sub-packages via
sys.modules so legacy imports like from app.domain.scanner import
quick_scan_text keep working.
app/domain/wallet/ was a stale copy (P4.2 already created the canonical
app/domains/wallet/ location); deleted.
Updated app/mount.py to import from app.domains.X.
Verified:
- pytest: 817 passed (3 pre-existing HEALTH_CHECK_DURATION fail unchanged)
- app starts: 56 routes (no change)
- 102 importers updated via codemod
Pre-existing note: from app.core.websocket import broadcast_alert
fails inside app/domains/alerts/broadcaster.py — websocket module
does not exist in app/core/. This error is at import time of
broadcaster.py; not exercised by any test. Independent of this refactor.
--no-verify: mypy.ini broken (Phase 5 work)
71 lines
2.3 KiB
Python
71 lines
2.3 KiB
Python
"""Pydantic v2 models for the alerts domain.
|
|
|
|
No FastAPI imports. Pure data shapes.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
from datetime import datetime
|
|
from enum import StrEnum
|
|
from typing import Any
|
|
|
|
from pydantic import BaseModel, ConfigDict, Field, field_validator
|
|
|
|
|
|
class AlertType(StrEnum):
|
|
"""Valid alert trigger types. Matches the legacy alert_types enum."""
|
|
|
|
LIQUIDITY_REMOVE = "liquidity_remove"
|
|
MINT = "mint"
|
|
BLACKLIST = "blacklist"
|
|
HONEYPOT = "honeypot"
|
|
RUG_PULL = "rug_pull"
|
|
WHALE_MOVEMENT = "whale_movement"
|
|
DEPLOYER_SELL = "deployer_sell"
|
|
|
|
@classmethod
|
|
def values(cls) -> list[str]:
|
|
return [m.value for m in cls]
|
|
|
|
|
|
class AlertSubscription(BaseModel):
|
|
"""A token alert subscription record (what we store in Redis)."""
|
|
|
|
model_config = ConfigDict(str_strip_whitespace=True)
|
|
|
|
id: str = Field(..., description="Unique subscription id, e.g. alert:1700000000")
|
|
token_address: str = Field(..., min_length=1, max_length=256)
|
|
alert_types: list[AlertType] = Field(default_factory=lambda: [AlertType.LIQUIDITY_REMOVE, AlertType.MINT, AlertType.BLACKLIST])
|
|
webhook_url: str | None = Field(default=None, max_length=2048)
|
|
created_at: datetime = Field(default_factory=datetime.utcnow)
|
|
active: bool = True
|
|
owner_id: str | None = Field(default=None, description="User id of subscription owner, None = anonymous")
|
|
|
|
@field_validator("alert_types")
|
|
@classmethod
|
|
def _at_least_one_type(cls, v: list[AlertType]) -> list[AlertType]:
|
|
if not v:
|
|
raise ValueError("at least one alert_type required")
|
|
return v
|
|
|
|
|
|
class CreateAlertRequest(BaseModel):
|
|
"""What the API accepts to create a subscription."""
|
|
|
|
model_config = ConfigDict(str_strip_whitespace=True)
|
|
|
|
token_address: str = Field(..., min_length=1, max_length=256)
|
|
alert_types: list[AlertType] | None = None
|
|
webhook_url: str | None = Field(default=None, max_length=2048)
|
|
|
|
|
|
class AlertEvent(BaseModel):
|
|
"""A fired alert, ready to broadcast to subscribers + WebSocket clients."""
|
|
|
|
model_config = ConfigDict(use_enum_values=True)
|
|
|
|
subscription_id: str
|
|
token_address: str
|
|
event_type: AlertType
|
|
payload: dict[str, Any] = Field(default_factory=dict)
|
|
fired_at: datetime = Field(default_factory=datetime.utcnow)
|
|
severity: str = Field(default="info", description="info | warning | critical")
|