Add web auth, infinite-scroll, agent admission and review fixes across agent/server/web
This commit is contained in:
@@ -53,13 +53,7 @@ async def _blacklist_row(session: AsyncSession, cred: AgentCredential) -> AgentB
|
||||
return res.scalar_one_or_none()
|
||||
|
||||
|
||||
async def reject_if_blocked(
|
||||
session: AsyncSession,
|
||||
cred: AgentCredential,
|
||||
agent_id: str,
|
||||
hostname: str,
|
||||
observed: datetime | None = None,
|
||||
) -> None:
|
||||
async def reject_if_blocked(session: AsyncSession, cred: AgentCredential) -> None:
|
||||
row = await _blacklist_row(session, cred)
|
||||
if row is None:
|
||||
return
|
||||
@@ -108,7 +102,7 @@ async def upsert_agent_heartbeat(
|
||||
session: AsyncSession, hb: HeartbeatRequest, cred: AgentCredential
|
||||
) -> str:
|
||||
observed = to_utc(hb.observed_at)
|
||||
await reject_if_blocked(session, cred, hb.agent_id, hb.hostname, observed)
|
||||
await reject_if_blocked(session, cred)
|
||||
|
||||
features = hb.features.model_dump()
|
||||
res = await session.execute(select(Agent).where(Agent.accepted_key_hash == cred.key_hash))
|
||||
@@ -330,7 +324,6 @@ async def ingest_event(
|
||||
incident_key=incident_key,
|
||||
)
|
||||
await session.execute(stmt)
|
||||
metrics.events_total.labels(result="accepted").inc()
|
||||
else:
|
||||
metrics.events_total.labels(result="unchanged").inc()
|
||||
|
||||
@@ -366,6 +359,9 @@ async def ingest_event(
|
||||
return False
|
||||
|
||||
transition, inc = await _apply_incident(session, agent_id, ev, incident_key)
|
||||
# Count accepted only after _apply_incident: an incident_key collision raises
|
||||
# 400 and the savepoint rollback discards the Event insert above.
|
||||
metrics.events_total.labels(result="accepted").inc()
|
||||
if transition and inc is not None and ev.notifications_enabled:
|
||||
event_type = "resolved" if ev.status == "ok" else "firing"
|
||||
await _enqueue_outbox(
|
||||
|
||||
@@ -208,9 +208,16 @@ async def tick(
|
||||
await session.commit()
|
||||
if not rows:
|
||||
return 0
|
||||
await asyncio.gather(
|
||||
*(_process_row(sm, registry, r, settings.notifier_max_attempts) for r in rows)
|
||||
)
|
||||
|
||||
# Bound concurrent DB sessions: each _process_row opens its own session, so an
|
||||
# unbounded gather over a large batch can exhaust the connection pool.
|
||||
sem = asyncio.Semaphore(settings.notifier_concurrency)
|
||||
|
||||
async def _guarded(row: dict) -> None:
|
||||
async with sem:
|
||||
await _process_row(sm, registry, row, settings.notifier_max_attempts)
|
||||
|
||||
await asyncio.gather(*(_guarded(r) for r in rows))
|
||||
return len(rows)
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import httpx
|
||||
|
||||
|
||||
def classify_response(status: int) -> tuple[bool, bool]:
|
||||
"""Return (ok, permanent_failure)."""
|
||||
@@ -10,9 +8,3 @@ def classify_response(status: int) -> tuple[bool, bool]:
|
||||
if 400 <= status < 500 and status not in (408, 425, 429):
|
||||
return False, True
|
||||
return False, False
|
||||
|
||||
|
||||
def classify_exception(exc: Exception) -> tuple[bool, bool]:
|
||||
if isinstance(exc, httpx.TimeoutException | httpx.NetworkError):
|
||||
return False, False
|
||||
return False, False
|
||||
|
||||
Reference in New Issue
Block a user