Files
appfactory-portal/app/routes/incidents.py
T
2026-06-16 10:23:43 +02:00

149 lines
5.0 KiB
Python

import html
from urllib.parse import quote
from fastapi import APIRouter, Depends, Request
from fastapi.responses import HTMLResponse
from app.auth import require_user
from app.db.audit import log_audit_event
from app.db.incidents import get_open_incidents, get_recent_incidents
from app.templates.layout import page
router = APIRouter()
def format_duration(seconds) -> str:
if seconds is None or seconds == "":
return ""
try:
total = max(0, int(seconds))
except (TypeError, ValueError):
return ""
hours, remainder = divmod(total, 3600)
minutes, secs = divmod(remainder, 60)
if hours:
return f"{hours} h {minutes} min"
if minutes:
return f"{minutes} min {secs} s"
return f"{secs} s"
def render_incident_status(status: str | None) -> str:
value = status or ""
normalized = value.lower()
labels = {
"open": "otev\u0159en\u00fd",
"resolved": "vy\u0159e\u0161en\u00fd",
}
class_name = "pill pill-muted"
if normalized == "open":
class_name = "pill pill-danger"
elif normalized == "resolved":
class_name = "pill pill-success"
return f'<span class="{class_name}">{html.escape(labels.get(normalized, value))}</span>'
def render_open_incident_rows(incidents: list[dict]) -> str:
rows = ""
for incident in incidents:
service_id = incident.get("service_id", "") or ""
service_name = html.escape(incident.get("service_name") or service_id)
title = html.escape(incident.get("title", "") or "")
started_at = html.escape(incident.get("started_at", "") or "")
duration = format_duration(incident.get("duration_seconds"))
if not duration:
duration = "prob\u00edh\u00e1"
rows += f"""
<tr>
<td><a href="/portal/apps/{quote(service_id, safe='')}">{service_name}</a></td>
<td>{title}</td>
<td>{started_at}</td>
<td>{html.escape(duration)}</td>
</tr>
"""
if not rows:
rows = '<tr><td colspan="4">\u017d\u00e1dn\u00e9 aktivn\u00ed incidenty.</td></tr>'
return rows
def render_incident_history_rows(incidents: list[dict], include_service: bool = True) -> str:
rows = ""
for incident in incidents:
service_id = incident.get("service_id", "") or ""
service_name = html.escape(incident.get("service_name") or service_id)
title = html.escape(incident.get("title", "") or "")
started_at = html.escape(incident.get("started_at", "") or "")
ended_at = html.escape(incident.get("ended_at", "") or "")
duration = html.escape(format_duration(incident.get("duration_seconds")))
status = render_incident_status(incident.get("status"))
service_cell = f'<td><a href="/portal/apps/{quote(service_id, safe="")}">{service_name}</a></td>' if include_service else ""
rows += f"""
<tr>
{service_cell}
<td>{title}</td>
<td>{started_at}</td>
<td>{ended_at}</td>
<td>{duration}</td>
<td>{status}</td>
</tr>
"""
colspan = 6 if include_service else 5
if not rows:
rows = f'<tr><td colspan="{colspan}">Zat\u00edm nejsou evidovan\u00e9 \u017e\u00e1dn\u00e9 incidenty.</td></tr>'
return rows
@router.get("/incidents", response_class=HTMLResponse)
def incidents_page(request: Request, user=Depends(require_user)):
open_incidents = get_open_incidents()
recent_incidents = get_recent_incidents(limit=100)
log_audit_event(
user,
action="incidents.view",
target_type="incidents",
metadata={"open": len(open_incidents), "history": len(recent_incidents)},
)
return page(
"Incidenty",
f"""
<div class="card">
<h2><i class="fa-solid fa-triangle-exclamation" aria-hidden="true"></i> Incidenty</h2>
<p class="muted">P\u0159ehled incident\u016f vytv\u00e1\u0159en\u00fdch a uzav\u00edran\u00fdch monitorem.</p>
</div>
<div class="card">
<h2>Aktivn\u00ed incidenty</h2>
<table>
<tr>
<th>Slu\u017eba</th>
<th>N\u00e1zev</th>
<th>Za\u010d\u00e1tek</th>
<th>Trv\u00e1n\u00ed</th>
</tr>
<tbody data-live-table="incidents.active">{render_open_incident_rows(open_incidents)}</tbody>
</table>
</div>
<div class="card">
<h2>Historie incident\u016f</h2>
<table>
<tr>
<th>Slu\u017eba</th>
<th>N\u00e1zev</th>
<th>Za\u010d\u00e1tek</th>
<th>Konec</th>
<th>Trv\u00e1n\u00ed</th>
<th>Stav</th>
</tr>
<tbody data-live-table="incidents.recent">{render_incident_history_rows(recent_incidents)}</tbody>
</table>
</div>
<script src="/portal/static/operations-live.js"></script>
""",
user=user,
)