feat(heartbeats): ping endpoint with silence alerts, menu screen and digest block

This commit is contained in:
hh committed 2026-10-14 05:20:53 +02:00
1 parent 6ef321dc9f
commit 69e7ad79ac
14 files changed
+473 -17

No files matched your search

+88
View File
@@ -0,0 +1,88 @@
import aiosqlite
from utils.db.models import Heartbeat, HeartbeatStatus
from utils.format import now
_COLUMNS = (
"id, name, status, text, period, created_at, "
"last_ping_at, last_ok_at, is_overdue, alerted_at"
)
def _row_to_heartbeat(row: aiosqlite.Row) -> Heartbeat:
return Heartbeat(
id=row["id"],
name=row["name"],
status=HeartbeatStatus(row["status"]),
text=row["text"],
period=row["period"],
created_at=row["created_at"],
last_ping_at=row["last_ping_at"],
last_ok_at=row["last_ok_at"],
is_overdue=bool(row["is_overdue"]),
alerted_at=row["alerted_at"],
)
class HeartbeatRepository:
def __init__(self, conn: aiosqlite.Connection) -> None:
self._conn = conn
async def all(self) -> list[Heartbeat]:
cursor = await self._conn.execute(
f"SELECT {_COLUMNS} FROM heartbeats ORDER BY name" # noqa: S608
)
return [_row_to_heartbeat(row) for row in await cursor.fetchall()]
async def get(self, heartbeat_id: int) -> Heartbeat | None:
cursor = await self._conn.execute(
f"SELECT {_COLUMNS} FROM heartbeats WHERE id = ?", # noqa: S608
(heartbeat_id,),
)
row = await cursor.fetchone()
return _row_to_heartbeat(row) if row else None
async def by_name(self, name: str) -> Heartbeat | None:
cursor = await self._conn.execute(
f"SELECT {_COLUMNS} FROM heartbeats WHERE name = ?", # noqa: S608
(name,),
)
row = await cursor.fetchone()
return _row_to_heartbeat(row) if row else None
async def record(
self, name: str, *, status: HeartbeatStatus, text: str, period: int
) -> Heartbeat:
stamp = now().isoformat()
ok_at = stamp if status is HeartbeatStatus.OK else None
await self._conn.execute(
"INSERT INTO heartbeats "
"(name, status, text, period, created_at, last_ping_at, last_ok_at) "
"VALUES (?, ?, ?, ?, ?, ?, ?) "
"ON CONFLICT(name) DO UPDATE SET status = excluded.status, "
"text = excluded.text, period = excluded.period, "
"last_ping_at = excluded.last_ping_at, "
"last_ok_at = COALESCE(excluded.last_ok_at, last_ok_at), "
"is_overdue = CASE WHEN excluded.last_ok_at IS NULL "
"THEN is_overdue ELSE 0 END, "
"alerted_at = CASE WHEN excluded.last_ok_at IS NULL "
"THEN alerted_at ELSE NULL END",
(name, status.value, text, period, stamp, stamp, ok_at),
)
await self._conn.commit()
heartbeat = await self.by_name(name)
assert heartbeat is not None
return heartbeat
async def mark_overdue(self, heartbeat_id: int, last_ping_at: str) -> bool:
cursor = await self._conn.execute(
"UPDATE heartbeats SET is_overdue = 1, alerted_at = ? "
"WHERE id = ? AND last_ping_at = ?",
(now().isoformat(), heartbeat_id, last_ping_at),
)
await self._conn.commit()
return cursor.rowcount > 0
async def delete(self, heartbeat_id: int) -> None:
await self._conn.execute("DELETE FROM heartbeats WHERE id = ?", (heartbeat_id,))
await self._conn.commit()