rmi-backend/app/domains/alerts/models.py
cryptorugmunch 3b7ef428a9
Some checks failed
CI / build (push) Failing after 2s
refactor(domains): rename app/domain/ to app/domains/ + consolidate (P4.7)
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)
2026-07-06 23:08:17 +02:00

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")