382825085f
Nové SQL migrace pro alert_rules a alert_events v migrations.py (line 165). Nová ruční DB vrstva v alerting.py (line 7). Nové stránky a akce v routes/alerting.py (line 288):Alert pravidla, detail, vytvoření, úprava, smazání povolit/zakázat pravidlo Alert eventy a detail eventu s payload_json odkazy z job_id na detail úlohy detail pravidla s posledními eventy admin-only editor .sh alert skriptů v /opt/appfactory/workspace/appfactory-tools/alerts audit eventy podle zadání Menu Provoz → Alerting v layout.py (line 23). Router je zaregistrovaný v main.py (line 8).
249 lines
5.5 KiB
Python
249 lines
5.5 KiB
Python
from typing import Any
|
|
|
|
from app.db.database import get_connection
|
|
from app.db.migrations import run_migrations
|
|
|
|
|
|
def get_alert_rules():
|
|
run_migrations()
|
|
con = get_connection()
|
|
|
|
rows = con.execute(
|
|
"""
|
|
SELECT
|
|
r.id,
|
|
r.name,
|
|
r.description,
|
|
r.event_type,
|
|
r.service_id,
|
|
r.script_name,
|
|
COALESCE(r.is_enabled, 1) AS is_enabled,
|
|
r.created_at,
|
|
r.updated_at,
|
|
e.id AS last_event_id,
|
|
e.status AS last_event_status,
|
|
e.created_at AS last_event_at
|
|
FROM alert_rules r
|
|
LEFT JOIN alert_events e ON e.id = (
|
|
SELECT id
|
|
FROM alert_events
|
|
WHERE rule_id = r.id
|
|
ORDER BY created_at DESC, id DESC
|
|
LIMIT 1
|
|
)
|
|
ORDER BY r.name
|
|
"""
|
|
).fetchall()
|
|
|
|
con.close()
|
|
return [dict(row) for row in rows]
|
|
|
|
|
|
def get_alert_rule(rule_id: int):
|
|
run_migrations()
|
|
con = get_connection()
|
|
|
|
row = con.execute(
|
|
"""
|
|
SELECT
|
|
id,
|
|
name,
|
|
description,
|
|
event_type,
|
|
service_id,
|
|
script_name,
|
|
COALESCE(is_enabled, 1) AS is_enabled,
|
|
created_at,
|
|
updated_at
|
|
FROM alert_rules
|
|
WHERE id = ?
|
|
""",
|
|
(rule_id,),
|
|
).fetchone()
|
|
|
|
con.close()
|
|
return dict(row) if row else None
|
|
|
|
|
|
def create_alert_rule(metadata: dict[str, Any]) -> int:
|
|
run_migrations()
|
|
con = get_connection()
|
|
|
|
cur = con.execute(
|
|
"""
|
|
INSERT INTO alert_rules (
|
|
name,
|
|
description,
|
|
event_type,
|
|
service_id,
|
|
script_name,
|
|
is_enabled,
|
|
created_at,
|
|
updated_at
|
|
)
|
|
VALUES (?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
|
|
""",
|
|
(
|
|
metadata.get("name"),
|
|
metadata.get("description") or None,
|
|
metadata.get("event_type"),
|
|
metadata.get("service_id") or None,
|
|
metadata.get("script_name"),
|
|
1 if metadata.get("is_enabled") else 0,
|
|
),
|
|
)
|
|
rule_id = cur.lastrowid
|
|
con.commit()
|
|
con.close()
|
|
return rule_id
|
|
|
|
|
|
def update_alert_rule(rule_id: int, metadata: dict[str, Any]) -> None:
|
|
run_migrations()
|
|
con = get_connection()
|
|
|
|
con.execute(
|
|
"""
|
|
UPDATE alert_rules
|
|
SET name = ?,
|
|
description = ?,
|
|
event_type = ?,
|
|
service_id = ?,
|
|
script_name = ?,
|
|
is_enabled = ?,
|
|
updated_at = CURRENT_TIMESTAMP
|
|
WHERE id = ?
|
|
""",
|
|
(
|
|
metadata.get("name"),
|
|
metadata.get("description") or None,
|
|
metadata.get("event_type"),
|
|
metadata.get("service_id") or None,
|
|
metadata.get("script_name"),
|
|
1 if metadata.get("is_enabled") else 0,
|
|
rule_id,
|
|
),
|
|
)
|
|
|
|
con.commit()
|
|
con.close()
|
|
|
|
|
|
def set_alert_rule_enabled(rule_id: int, is_enabled: bool) -> None:
|
|
run_migrations()
|
|
con = get_connection()
|
|
|
|
con.execute(
|
|
"""
|
|
UPDATE alert_rules
|
|
SET is_enabled = ?,
|
|
updated_at = CURRENT_TIMESTAMP
|
|
WHERE id = ?
|
|
""",
|
|
(1 if is_enabled else 0, rule_id),
|
|
)
|
|
|
|
con.commit()
|
|
con.close()
|
|
|
|
|
|
def delete_alert_rule(rule_id: int) -> bool:
|
|
run_migrations()
|
|
con = get_connection()
|
|
|
|
cur = con.execute("DELETE FROM alert_rules WHERE id = ?", (rule_id,))
|
|
deleted = cur.rowcount == 1
|
|
con.commit()
|
|
con.close()
|
|
return deleted
|
|
|
|
|
|
def get_alert_events(limit: int = 200):
|
|
run_migrations()
|
|
con = get_connection()
|
|
|
|
rows = con.execute(
|
|
"""
|
|
SELECT
|
|
e.id,
|
|
e.rule_id,
|
|
r.name AS rule_name,
|
|
e.event_type,
|
|
e.service_id,
|
|
e.incident_id,
|
|
e.status,
|
|
e.job_id,
|
|
e.error_text,
|
|
e.payload_json,
|
|
e.created_at,
|
|
e.processed_at
|
|
FROM alert_events e
|
|
LEFT JOIN alert_rules r ON r.id = e.rule_id
|
|
ORDER BY e.created_at DESC, e.id DESC
|
|
LIMIT ?
|
|
""",
|
|
(limit,),
|
|
).fetchall()
|
|
|
|
con.close()
|
|
return [dict(row) for row in rows]
|
|
|
|
|
|
def get_alert_event(event_id: int):
|
|
run_migrations()
|
|
con = get_connection()
|
|
|
|
row = con.execute(
|
|
"""
|
|
SELECT
|
|
e.id,
|
|
e.rule_id,
|
|
r.name AS rule_name,
|
|
e.event_type,
|
|
e.service_id,
|
|
e.incident_id,
|
|
e.status,
|
|
e.job_id,
|
|
e.error_text,
|
|
e.payload_json,
|
|
e.created_at,
|
|
e.processed_at
|
|
FROM alert_events e
|
|
LEFT JOIN alert_rules r ON r.id = e.rule_id
|
|
WHERE e.id = ?
|
|
""",
|
|
(event_id,),
|
|
).fetchone()
|
|
|
|
con.close()
|
|
return dict(row) if row else None
|
|
|
|
|
|
def get_rule_alert_events(rule_id: int, limit: int = 20):
|
|
run_migrations()
|
|
con = get_connection()
|
|
|
|
rows = con.execute(
|
|
"""
|
|
SELECT
|
|
id,
|
|
rule_id,
|
|
event_type,
|
|
service_id,
|
|
incident_id,
|
|
status,
|
|
job_id,
|
|
error_text,
|
|
created_at,
|
|
processed_at
|
|
FROM alert_events
|
|
WHERE rule_id = ?
|
|
ORDER BY created_at DESC, id DESC
|
|
LIMIT ?
|
|
""",
|
|
(rule_id, limit),
|
|
).fetchall()
|
|
|
|
con.close()
|
|
return [dict(row) for row in rows]
|