from app.db.database import get_connection 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"])) 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() 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() 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() 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], }, "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, "recent": [dict(row) for row in recent_deployments], }, "audit": { "recent": [dict(row) for row in recent_audit], }, }