Files
JiriUhlir 382825085f Hotovo. Přidal jsem správu Alertingu do appfactory-portal bez ORM:
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).
2026-06-08 06:57:12 +02:00

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]