6aa5e69ee4
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
334 lines
11 KiB
Python
334 lines
11 KiB
Python
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
|
|
|
|
DEPLOYMENT_SUCCESS_STATUSES = ("ok", "success", "succeeded", "done", "deployed", "completed")
|
|
DEPLOYMENT_FAILED_STATUSES = ("failed", "failure", "error", "cancelled", "canceled")
|
|
DEPLOYMENT_RUNNING_STATUSES = ("running", "pending", "queued", "in_progress", "starting")
|
|
|
|
|
|
def _table_columns(con, table_name: str) -> set[str]:
|
|
rows = con.execute(f"PRAGMA table_info({table_name})").fetchall()
|
|
return {row["name"] for row in rows}
|
|
|
|
|
|
def get_operations_summary():
|
|
run_migrations()
|
|
con = get_connection()
|
|
|
|
workers = con.execute(
|
|
"""
|
|
SELECT last_seen_at
|
|
FROM workers
|
|
"""
|
|
).fetchall()
|
|
workers_online = sum(1 for worker in workers if is_worker_online(worker["last_seen_at"]))
|
|
|
|
running_jobs = con.execute(
|
|
"""
|
|
SELECT COUNT(*) AS count
|
|
FROM jobs
|
|
WHERE status = 'running'
|
|
"""
|
|
).fetchone()["count"]
|
|
queued_jobs = con.execute(
|
|
"""
|
|
SELECT COUNT(*) AS count
|
|
FROM jobs
|
|
WHERE status = 'queued'
|
|
"""
|
|
).fetchone()["count"]
|
|
failed_jobs_24h = con.execute(
|
|
"""
|
|
SELECT COUNT(*) AS count
|
|
FROM jobs
|
|
WHERE status = 'failed'
|
|
AND created_at >= datetime('now', '-24 hours')
|
|
"""
|
|
).fetchone()["count"]
|
|
|
|
deployment_columns = _table_columns(con, "deployments")
|
|
deployments_today = 0
|
|
if "created_at" in deployment_columns:
|
|
deployments_today = con.execute(
|
|
"""
|
|
SELECT COUNT(*) AS count
|
|
FROM deployments
|
|
WHERE date(created_at) = date('now')
|
|
"""
|
|
).fetchone()["count"]
|
|
elif "started_at" in deployment_columns:
|
|
deployments_today = con.execute(
|
|
"""
|
|
SELECT COUNT(*) AS count
|
|
FROM deployments
|
|
WHERE date(started_at) = date('now')
|
|
"""
|
|
).fetchone()["count"]
|
|
|
|
app_columns = _table_columns(con, "apps")
|
|
if "enabled" in app_columns:
|
|
active_applications = con.execute(
|
|
"""
|
|
SELECT COUNT(*) AS count
|
|
FROM apps
|
|
WHERE enabled = 1
|
|
OR LOWER(CAST(enabled AS TEXT)) IN ('1', 'true', 'yes', 'enabled')
|
|
"""
|
|
).fetchone()["count"]
|
|
else:
|
|
active_applications = con.execute(
|
|
"""
|
|
SELECT COUNT(*) AS count
|
|
FROM apps
|
|
WHERE LOWER(COALESCE(status, '')) NOT IN ('disabled', 'deleted', 'archived')
|
|
"""
|
|
).fetchone()["count"]
|
|
|
|
con.close()
|
|
return {
|
|
"workers_online": workers_online,
|
|
"running_jobs": running_jobs,
|
|
"queued_jobs": queued_jobs,
|
|
"failed_jobs_24h": failed_jobs_24h,
|
|
"deployments_today": deployments_today,
|
|
"active_applications": active_applications,
|
|
}
|
|
|
|
|
|
def get_recent_jobs(limit: int = 20):
|
|
run_migrations()
|
|
con = get_connection()
|
|
|
|
rows = con.execute(
|
|
"""
|
|
SELECT id, type, target_type, target_id, status, source, created_at
|
|
FROM jobs
|
|
ORDER BY id DESC
|
|
LIMIT ?
|
|
""",
|
|
(limit,),
|
|
).fetchall()
|
|
|
|
con.close()
|
|
return [dict(row) for row in rows]
|
|
|
|
|
|
def get_recent_deployments(limit: int = 20):
|
|
con = get_connection()
|
|
|
|
rows = con.execute(
|
|
"""
|
|
SELECT id, app_id, kind, status, started_at, finished_at, trigger_source
|
|
FROM deployments
|
|
ORDER BY id DESC
|
|
LIMIT ?
|
|
""",
|
|
(limit,),
|
|
).fetchall()
|
|
|
|
con.close()
|
|
return [dict(row) for row in rows]
|
|
|
|
|
|
def get_recent_audit_events(limit: int = 20):
|
|
con = get_connection()
|
|
|
|
rows = con.execute(
|
|
"""
|
|
SELECT id, username, action, target_type, target_id, source, created_at
|
|
FROM audit_events
|
|
ORDER BY id DESC
|
|
LIMIT ?
|
|
""",
|
|
(limit,),
|
|
).fetchall()
|
|
|
|
con.close()
|
|
return [dict(row) for row in rows]
|
|
|
|
|
|
def get_operations_snapshot():
|
|
run_migrations()
|
|
con = get_connection()
|
|
|
|
workers = con.execute(
|
|
"""
|
|
SELECT id, status, started_at, last_seen_at, current_job_id, metadata_json
|
|
FROM workers
|
|
ORDER BY last_seen_at DESC, id
|
|
LIMIT 20
|
|
"""
|
|
).fetchall()
|
|
recent_workers = []
|
|
workers_online = 0
|
|
for row in workers:
|
|
worker = dict(row)
|
|
worker["online"] = is_worker_online(worker.get("last_seen_at"))
|
|
workers_online += 1 if worker["online"] else 0
|
|
recent_workers.append(worker)
|
|
|
|
total_workers = con.execute("SELECT COUNT(*) AS count FROM workers").fetchone()["count"]
|
|
online_all = con.execute("SELECT last_seen_at FROM workers").fetchall()
|
|
workers_online = sum(1 for worker in online_all if is_worker_online(worker["last_seen_at"]))
|
|
|
|
app_columns = _table_columns(con, "apps")
|
|
if "enabled" in app_columns:
|
|
active_applications = con.execute(
|
|
"""
|
|
SELECT COUNT(*) AS count
|
|
FROM apps
|
|
WHERE enabled = 1
|
|
OR LOWER(CAST(enabled AS TEXT)) IN ('1', 'true', 'yes', 'enabled')
|
|
"""
|
|
).fetchone()["count"]
|
|
else:
|
|
active_applications = con.execute(
|
|
"""
|
|
SELECT COUNT(*) AS count
|
|
FROM apps
|
|
WHERE LOWER(COALESCE(status, '')) NOT IN ('disabled', 'deleted', 'archived')
|
|
"""
|
|
).fetchone()["count"]
|
|
problematic_applications = con.execute(
|
|
"""
|
|
SELECT COUNT(*) AS count
|
|
FROM apps
|
|
WHERE LOWER(COALESCE(status, '')) IN ('failed', 'error', 'disabled', 'deleted', 'archived')
|
|
"""
|
|
).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(
|
|
"""
|
|
SELECT
|
|
SUM(CASE WHEN status = 'queued' THEN 1 ELSE 0 END) AS queued,
|
|
SUM(CASE WHEN status = 'running' THEN 1 ELSE 0 END) AS running,
|
|
SUM(CASE WHEN status = 'failed' AND created_at >= datetime('now', '-24 hours') THEN 1 ELSE 0 END) AS failed_24h,
|
|
SUM(CASE WHEN status = 'success' AND created_at >= datetime('now', '-24 hours') THEN 1 ELSE 0 END) AS success_24h
|
|
FROM jobs
|
|
"""
|
|
).fetchone()
|
|
recent_jobs = con.execute(
|
|
"""
|
|
SELECT id, type, target_type, target_id, status, source, created_at, started_at, finished_at
|
|
FROM jobs
|
|
ORDER BY id DESC
|
|
LIMIT 20
|
|
"""
|
|
).fetchall()
|
|
recent_failed_jobs = con.execute(
|
|
"""
|
|
SELECT id, type, target_type, target_id, status, source, created_at, started_at, finished_at
|
|
FROM jobs
|
|
WHERE status = 'failed'
|
|
ORDER BY id DESC
|
|
LIMIT 10
|
|
"""
|
|
).fetchall()
|
|
|
|
deployments_summary = con.execute(
|
|
f"""
|
|
SELECT
|
|
SUM(CASE WHEN LOWER(COALESCE(status, '')) IN ({','.join('?' for _ in DEPLOYMENT_RUNNING_STATUSES)}) THEN 1 ELSE 0 END) AS running,
|
|
SUM(CASE WHEN LOWER(COALESCE(status, '')) IN ({','.join('?' for _ in DEPLOYMENT_FAILED_STATUSES)}) AND started_at >= datetime('now', '-24 hours') THEN 1 ELSE 0 END) AS failed_24h,
|
|
SUM(CASE WHEN LOWER(COALESCE(status, '')) IN ({','.join('?' for _ in DEPLOYMENT_SUCCESS_STATUSES)}) AND started_at >= datetime('now', '-24 hours') THEN 1 ELSE 0 END) AS success_24h
|
|
FROM deployments
|
|
""",
|
|
(*DEPLOYMENT_RUNNING_STATUSES, *DEPLOYMENT_FAILED_STATUSES, *DEPLOYMENT_SUCCESS_STATUSES),
|
|
).fetchone()
|
|
deployment_columns = _table_columns(con, "deployments")
|
|
deployments_today = 0
|
|
if "created_at" in deployment_columns:
|
|
deployments_today = con.execute(
|
|
"""
|
|
SELECT COUNT(*) AS count
|
|
FROM deployments
|
|
WHERE date(created_at) = date('now')
|
|
"""
|
|
).fetchone()["count"]
|
|
elif "started_at" in deployment_columns:
|
|
deployments_today = con.execute(
|
|
"""
|
|
SELECT COUNT(*) AS count
|
|
FROM deployments
|
|
WHERE date(started_at) = date('now')
|
|
"""
|
|
).fetchone()["count"]
|
|
recent_deployments = con.execute(
|
|
"""
|
|
SELECT id, app_id, kind, status, started_at, finished_at, trigger_source
|
|
FROM deployments
|
|
ORDER BY id DESC
|
|
LIMIT 20
|
|
"""
|
|
).fetchall()
|
|
recent_audit = con.execute(
|
|
"""
|
|
SELECT id, username, action, target_type, target_id, source, created_at
|
|
FROM audit_events
|
|
ORDER BY id DESC
|
|
LIMIT 20
|
|
"""
|
|
).fetchall()
|
|
recent_failed_deployments = con.execute(
|
|
f"""
|
|
SELECT id, app_id, kind, status, started_at, finished_at, trigger_source
|
|
FROM deployments
|
|
WHERE LOWER(COALESCE(status, '')) IN ({','.join('?' for _ in DEPLOYMENT_FAILED_STATUSES)})
|
|
ORDER BY id DESC
|
|
LIMIT 10
|
|
""",
|
|
DEPLOYMENT_FAILED_STATUSES,
|
|
).fetchall()
|
|
|
|
con.close()
|
|
return {
|
|
"type": "operations.snapshot",
|
|
"jobs": {
|
|
"queued": jobs_summary["queued"] or 0,
|
|
"running": jobs_summary["running"] or 0,
|
|
"failed_24h": jobs_summary["failed_24h"] or 0,
|
|
"success_24h": jobs_summary["success_24h"] or 0,
|
|
"recent": [dict(row) for row in recent_jobs],
|
|
"recent_failed": [dict(row) for row in recent_failed_jobs],
|
|
},
|
|
"apps": {
|
|
"active": active_applications,
|
|
"problematic": problematic_applications,
|
|
},
|
|
"health": {
|
|
"healthy": health_summary["healthy"],
|
|
"unhealthy": health_summary["unhealthy"],
|
|
"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),
|
|
"recent": recent_workers,
|
|
},
|
|
"deployments": {
|
|
"running": deployments_summary["running"] or 0,
|
|
"failed_24h": deployments_summary["failed_24h"] or 0,
|
|
"success_24h": deployments_summary["success_24h"] or 0,
|
|
"today": deployments_today,
|
|
"recent": [dict(row) for row in recent_deployments],
|
|
"recent_failed": [dict(row) for row in recent_failed_deployments],
|
|
},
|
|
"audit": {
|
|
"recent": [dict(row) for row in recent_audit],
|
|
},
|
|
}
|