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).
This commit is contained in:
@@ -0,0 +1,248 @@
|
||||
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]
|
||||
@@ -160,6 +160,43 @@ def run_migrations():
|
||||
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.execute(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS alert_rules (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL,
|
||||
description TEXT,
|
||||
event_type TEXT NOT NULL,
|
||||
service_id TEXT,
|
||||
script_name TEXT NOT NULL,
|
||||
is_enabled INTEGER NOT NULL DEFAULT 1,
|
||||
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
)
|
||||
"""
|
||||
)
|
||||
con.execute(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS alert_events (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
rule_id INTEGER,
|
||||
event_type TEXT NOT NULL,
|
||||
service_id TEXT,
|
||||
incident_id INTEGER,
|
||||
status TEXT,
|
||||
job_id INTEGER,
|
||||
error_text TEXT,
|
||||
payload_json TEXT,
|
||||
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
processed_at TEXT
|
||||
)
|
||||
"""
|
||||
)
|
||||
con.execute("CREATE INDEX IF NOT EXISTS idx_alert_rules_event_enabled ON alert_rules(event_type, is_enabled)")
|
||||
con.execute("CREATE INDEX IF NOT EXISTS idx_alert_rules_service ON alert_rules(service_id)")
|
||||
con.execute("CREATE INDEX IF NOT EXISTS idx_alert_events_rule_created ON alert_events(rule_id, created_at)")
|
||||
con.execute("CREATE INDEX IF NOT EXISTS idx_alert_events_created ON alert_events(created_at)")
|
||||
con.execute("CREATE INDEX IF NOT EXISTS idx_alert_events_job_id ON alert_events(job_id)")
|
||||
|
||||
con.commit()
|
||||
con.close()
|
||||
|
||||
Reference in New Issue
Block a user