Přidáno:
nová stránka /portal/incidents s title CSBot Services Portal - Incidenty menu Provoz -> Incidenty aktivní incidenty a historie posledních 100 incidentů barevné stavy: open červeně, resolved zeleně karta Aktivní incidenty a seznam Poslední incidenty na přehledu realtime napojení přes existující operations-live.js a snapshot sekce Incidenty služby v detailu služby, posledních 20 incidentů DB helpery pro service_incidents migrace/indexy pro lokální prostředí, kde tabulka ještě neexistuje
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
from app.db.database import get_connection
|
||||
from app.db.migrations import run_migrations
|
||||
|
||||
|
||||
def _incident_select() -> str:
|
||||
return """
|
||||
SELECT
|
||||
i.id,
|
||||
i.service_id,
|
||||
COALESCE(a.name, i.service_id) AS service_name,
|
||||
i.status,
|
||||
i.started_at,
|
||||
i.ended_at,
|
||||
CASE
|
||||
WHEN i.duration_seconds IS NOT NULL THEN i.duration_seconds
|
||||
WHEN LOWER(COALESCE(i.status, '')) = 'open' THEN CAST(strftime('%s', 'now') - strftime('%s', i.started_at) AS INTEGER)
|
||||
ELSE NULL
|
||||
END AS duration_seconds,
|
||||
i.title,
|
||||
i.description
|
||||
FROM service_incidents i
|
||||
LEFT JOIN apps a ON a.id = i.service_id
|
||||
"""
|
||||
|
||||
|
||||
def get_open_incidents(limit: int | None = None):
|
||||
run_migrations()
|
||||
con = get_connection()
|
||||
|
||||
sql = f"""
|
||||
{_incident_select()}
|
||||
WHERE LOWER(COALESCE(i.status, '')) = 'open'
|
||||
ORDER BY i.started_at DESC, i.id DESC
|
||||
"""
|
||||
params = ()
|
||||
if limit is not None:
|
||||
sql += " LIMIT ?"
|
||||
params = (limit,)
|
||||
|
||||
rows = con.execute(sql, params).fetchall()
|
||||
con.close()
|
||||
return [dict(row) for row in rows]
|
||||
|
||||
|
||||
def get_recent_incidents(limit: int = 100):
|
||||
run_migrations()
|
||||
con = get_connection()
|
||||
|
||||
rows = con.execute(
|
||||
f"""
|
||||
{_incident_select()}
|
||||
ORDER BY i.started_at DESC, i.id DESC
|
||||
LIMIT ?
|
||||
""",
|
||||
(limit,),
|
||||
).fetchall()
|
||||
|
||||
con.close()
|
||||
return [dict(row) for row in rows]
|
||||
|
||||
|
||||
def get_service_incidents(service_id: str, limit: int = 20):
|
||||
run_migrations()
|
||||
con = get_connection()
|
||||
|
||||
rows = con.execute(
|
||||
f"""
|
||||
{_incident_select()}
|
||||
WHERE i.service_id = ?
|
||||
ORDER BY i.started_at DESC, i.id DESC
|
||||
LIMIT ?
|
||||
""",
|
||||
(service_id, limit),
|
||||
).fetchall()
|
||||
|
||||
con.close()
|
||||
return [dict(row) for row in rows]
|
||||
|
||||
|
||||
def count_open_incidents() -> int:
|
||||
run_migrations()
|
||||
con = get_connection()
|
||||
|
||||
row = con.execute(
|
||||
"""
|
||||
SELECT COUNT(*) AS count
|
||||
FROM service_incidents
|
||||
WHERE LOWER(COALESCE(status, '')) = 'open'
|
||||
"""
|
||||
).fetchone()
|
||||
|
||||
con.close()
|
||||
return row["count"] if row else 0
|
||||
@@ -110,6 +110,20 @@ def run_migrations():
|
||||
)
|
||||
"""
|
||||
)
|
||||
con.execute(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS service_incidents (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
service_id TEXT NOT NULL,
|
||||
status TEXT NOT NULL,
|
||||
started_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
ended_at TEXT,
|
||||
duration_seconds INTEGER,
|
||||
title TEXT,
|
||||
description TEXT
|
||||
)
|
||||
"""
|
||||
)
|
||||
for statement in (
|
||||
"ALTER TABLE app_templates ADD COLUMN runtime TEXT",
|
||||
"ALTER TABLE app_templates ADD COLUMN description TEXT",
|
||||
@@ -123,6 +137,8 @@ def run_migrations():
|
||||
except Exception:
|
||||
pass
|
||||
con.execute("CREATE INDEX IF NOT EXISTS idx_app_variables_app_id ON app_variables(app_id)")
|
||||
con.execute("CREATE INDEX IF NOT EXISTS idx_service_incidents_status_started ON service_incidents(status, started_at)")
|
||||
con.execute("CREATE INDEX IF NOT EXISTS idx_service_incidents_service_started ON service_incidents(service_id, started_at)")
|
||||
|
||||
con.commit()
|
||||
con.close()
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
from app.db.database import get_connection
|
||||
from app.db.health import get_health_summary, get_problem_service_health
|
||||
from app.db.incidents import count_open_incidents, get_open_incidents, get_recent_incidents
|
||||
from app.db.migrations import run_migrations
|
||||
from app.db.workers import is_worker_online
|
||||
|
||||
@@ -200,6 +201,9 @@ def get_operations_snapshot():
|
||||
).fetchone()["count"]
|
||||
health_summary = get_health_summary()
|
||||
health_problems = get_problem_service_health()
|
||||
open_incidents_count = count_open_incidents()
|
||||
open_incidents = get_open_incidents(limit=10)
|
||||
recent_incidents = get_recent_incidents(limit=10)
|
||||
|
||||
jobs_summary = con.execute(
|
||||
"""
|
||||
@@ -305,6 +309,11 @@ def get_operations_snapshot():
|
||||
"unreachable": health_summary["unreachable"],
|
||||
"problems": health_problems,
|
||||
},
|
||||
"incidents": {
|
||||
"open": open_incidents_count,
|
||||
"active": open_incidents,
|
||||
"recent": recent_incidents,
|
||||
},
|
||||
"workers": {
|
||||
"online": workers_online,
|
||||
"offline": max(0, total_workers - workers_online),
|
||||
|
||||
Reference in New Issue
Block a user