Some checks failed
Tests / Backend Tests (Python) (3.10) (push) Has been cancelled
Tests / Backend Tests (Python) (3.11) (push) Has been cancelled
Tests / Backend Tests (Python) (3.12) (push) Has been cancelled
Tests / Frontend Tests (JS) (push) Has been cancelled
Tests / Integration Tests (push) Has been cancelled
Tests / All Tests Passed (push) Has been cancelled
166 lines
5.6 KiB
Python
166 lines
5.6 KiB
Python
"""CRUD operations for Docker alerts."""
|
|
from datetime import datetime
|
|
from typing import List, Optional
|
|
|
|
from sqlalchemy import select, func
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from app.models.docker_alert import DockerAlert
|
|
|
|
|
|
class DockerAlertRepository:
|
|
"""Repository for Docker alert CRUD operations."""
|
|
|
|
def __init__(self, session: AsyncSession):
|
|
self.session = session
|
|
|
|
async def get(self, alert_id: int) -> Optional[DockerAlert]:
|
|
"""Get an alert by its ID."""
|
|
result = await self.session.execute(
|
|
select(DockerAlert).where(DockerAlert.id == alert_id)
|
|
)
|
|
return result.scalar_one_or_none()
|
|
|
|
async def get_open_alert(self, host_id: str, container_name: str) -> Optional[DockerAlert]:
|
|
"""Get an open alert for a specific container."""
|
|
result = await self.session.execute(
|
|
select(DockerAlert).where(
|
|
DockerAlert.host_id == host_id,
|
|
DockerAlert.container_name == container_name,
|
|
DockerAlert.state == "open"
|
|
)
|
|
)
|
|
return result.scalar_one_or_none()
|
|
|
|
async def list_alerts(
|
|
self,
|
|
host_id: Optional[str] = None,
|
|
state: Optional[str] = None,
|
|
severity: Optional[str] = None,
|
|
limit: int = 100,
|
|
offset: int = 0
|
|
) -> List[DockerAlert]:
|
|
"""List alerts with optional filters."""
|
|
query = select(DockerAlert)
|
|
|
|
if host_id:
|
|
query = query.where(DockerAlert.host_id == host_id)
|
|
if state:
|
|
query = query.where(DockerAlert.state == state)
|
|
if severity:
|
|
query = query.where(DockerAlert.severity == severity)
|
|
|
|
query = query.order_by(DockerAlert.opened_at.desc()).limit(limit).offset(offset)
|
|
result = await self.session.execute(query)
|
|
return list(result.scalars().all())
|
|
|
|
async def count_alerts(
|
|
self,
|
|
host_id: Optional[str] = None,
|
|
state: Optional[str] = None
|
|
) -> dict:
|
|
"""Count alerts by state."""
|
|
base_query = select(DockerAlert)
|
|
if host_id:
|
|
base_query = base_query.where(DockerAlert.host_id == host_id)
|
|
|
|
# Count open
|
|
open_query = select(func.count(DockerAlert.id)).where(DockerAlert.state == "open")
|
|
if host_id:
|
|
open_query = open_query.where(DockerAlert.host_id == host_id)
|
|
|
|
# Count acknowledged
|
|
ack_query = select(func.count(DockerAlert.id)).where(DockerAlert.state == "acknowledged")
|
|
if host_id:
|
|
ack_query = ack_query.where(DockerAlert.host_id == host_id)
|
|
|
|
# Count total
|
|
total_query = select(func.count(DockerAlert.id))
|
|
if host_id:
|
|
total_query = total_query.where(DockerAlert.host_id == host_id)
|
|
|
|
open_result = await self.session.execute(open_query)
|
|
ack_result = await self.session.execute(ack_query)
|
|
total_result = await self.session.execute(total_query)
|
|
|
|
return {
|
|
"total": total_result.scalar() or 0,
|
|
"open": open_result.scalar() or 0,
|
|
"acknowledged": ack_result.scalar() or 0
|
|
}
|
|
|
|
async def count_open_by_host(self, host_id: str) -> int:
|
|
"""Count open alerts for a host."""
|
|
result = await self.session.execute(
|
|
select(func.count(DockerAlert.id)).where(
|
|
DockerAlert.host_id == host_id,
|
|
DockerAlert.state == "open"
|
|
)
|
|
)
|
|
return result.scalar() or 0
|
|
|
|
async def create(
|
|
self,
|
|
host_id: str,
|
|
container_name: str,
|
|
severity: str = "warning",
|
|
message: Optional[str] = None
|
|
) -> DockerAlert:
|
|
"""Create a new alert."""
|
|
alert = DockerAlert(
|
|
host_id=host_id,
|
|
container_name=container_name,
|
|
severity=severity,
|
|
state="open",
|
|
message=message,
|
|
opened_at=datetime.utcnow()
|
|
)
|
|
self.session.add(alert)
|
|
return alert
|
|
|
|
async def acknowledge(
|
|
self,
|
|
alert_id: int,
|
|
acknowledged_by: str
|
|
) -> Optional[DockerAlert]:
|
|
"""Acknowledge an alert."""
|
|
alert = await self.get(alert_id)
|
|
if alert and alert.state == "open":
|
|
alert.state = "acknowledged"
|
|
alert.acknowledged_at = datetime.utcnow()
|
|
alert.acknowledged_by = acknowledged_by
|
|
return alert
|
|
return None
|
|
|
|
async def close(self, alert_id: int) -> Optional[DockerAlert]:
|
|
"""Close an alert."""
|
|
alert = await self.get(alert_id)
|
|
if alert and alert.state in ("open", "acknowledged"):
|
|
alert.state = "closed"
|
|
alert.closed_at = datetime.utcnow()
|
|
return alert
|
|
return None
|
|
|
|
async def close_for_container(self, host_id: str, container_name: str) -> int:
|
|
"""Close all open alerts for a container."""
|
|
result = await self.session.execute(
|
|
select(DockerAlert).where(
|
|
DockerAlert.host_id == host_id,
|
|
DockerAlert.container_name == container_name,
|
|
DockerAlert.state.in_(["open", "acknowledged"])
|
|
)
|
|
)
|
|
alerts = result.scalars().all()
|
|
count = 0
|
|
for alert in alerts:
|
|
alert.state = "closed"
|
|
alert.closed_at = datetime.utcnow()
|
|
count += 1
|
|
return count
|
|
|
|
async def update_last_notified(self, alert_id: int) -> None:
|
|
"""Update the last notified timestamp."""
|
|
alert = await self.get(alert_id)
|
|
if alert:
|
|
alert.last_notified_at = datetime.utcnow()
|