feat: add event watcher

This commit is contained in:
h
2026-05-30 01:54:49 +02:00
parent c40e720163
commit f0afb7ec5b
11 changed files with 176 additions and 14 deletions
+2 -1
View File
@@ -2,7 +2,7 @@ from datetime import datetime
from enum import StrEnum
from typing import Any
from sqlalchemy import BigInteger, Column, DateTime, LargeBinary, func
from sqlalchemy import BigInteger, Column, DateTime, LargeBinary, Text, func
from sqlalchemy.dialects.postgresql import JSONB
from sqlmodel import Field, SQLModel
@@ -471,6 +471,7 @@ class Alert(SQLModel, table=True):
default_factory=dict, sa_column=Column(JSONB, nullable=False)
)
seen: bool = False
dedup_key: str | None = Field(default=None, sa_column=Column(Text, nullable=True))
created_at: datetime = Field(
sa_column=Column(
DateTime(timezone=True), nullable=False, server_default=func.now()
+27 -6
View File
@@ -6,6 +6,9 @@ import asyncpg
from utils.read.models import AlertView, Page, WatchView
WATCHES_CHANGED_CHANNEL = "watches_changed"
ALERTS_CHANGED_CHANNEL = "alerts_changed"
_WATCH_COLS = "id, account_id, kind, params, enabled, created_at, updated_at"
_ALERT_COLS = "id, account_id, watch_id, ts, payload, seen, created_at"
@@ -55,6 +58,7 @@ async def create_watch(
json.dumps(params),
enabled,
)
await pool.execute(f"NOTIFY {WATCHES_CHANGED_CHANNEL}")
return _to_watch(row)
@@ -68,12 +72,18 @@ async def update_watch(
json.dumps(params),
enabled,
)
return _to_watch(row) if row else None
if row is None:
return None
await pool.execute(f"NOTIFY {WATCHES_CHANGED_CHANNEL}")
return _to_watch(row)
async def delete_watch(pool: asyncpg.Pool, watch_id: int) -> bool:
result = await pool.execute("DELETE FROM watches WHERE id = $1", watch_id)
return result.endswith("1")
if not result.endswith("1"):
return False
await pool.execute(f"NOTIFY {WATCHES_CHANGED_CHANNEL}")
return True
async def list_alerts(
@@ -95,16 +105,27 @@ async def list_alerts(
async def insert_alert(
pool: asyncpg.Pool, account_id: int, watch_id: int, payload: dict[str, Any]
) -> AlertView:
pool: asyncpg.Pool,
account_id: int,
watch_id: int,
payload: dict[str, Any],
*,
dedup_key: str | None = None,
) -> AlertView | None:
row = await pool.fetchrow(
"INSERT INTO alerts (account_id, watch_id, ts, payload) " # noqa: S608
f"VALUES ($1, $2, $3, $4::jsonb) RETURNING {_ALERT_COLS}",
"INSERT INTO alerts (account_id, watch_id, ts, payload, seen, dedup_key) " # noqa: S608
"VALUES ($1, $2, $3, $4::jsonb, false, $5) "
"ON CONFLICT (watch_id, dedup_key) WHERE dedup_key IS NOT NULL "
f"DO NOTHING RETURNING {_ALERT_COLS}",
account_id,
watch_id,
datetime.now(UTC),
json.dumps(payload),
dedup_key,
)
if row is None:
return None
await pool.execute(f"NOTIFY {ALERTS_CHANGED_CHANNEL}")
return _to_alert(row)