import re from ..constants import (ALERTS_AI_PER_MINUTE, ALERTS_BUNDLE_SECONDS, ALERTS_DEFAULT_DELAY_MINUTES, ALERTS_HOST_DELAY_MINUTES, ALERTS_RECOVERY_ENABLED, ALERTS_REMINDER_HOURS, ALERTS_SEND_LIMIT, DEFAULT_AI_PER_MINUTE, DEFAULT_BUNDLE_SECONDS, DEFAULT_DELAY_MINUTES, DEFAULT_HOST_DELAY_MINUTES, DEFAULT_POLL_INTERVAL, DEFAULT_REMINDER_HOURS, DEFAULT_SEND_LIMIT, MAX_DELAY_MINUTES, MAX_REMINDER_HOURS, MAX_SEND_LIMIT, MIN_POLL_INTERVAL, MIN_SEND_LIMIT, POLL_INTERVAL, UI_BASE_URL) from .sections import Action, Field, Section SLUG = "alerting" BASELINE_PATH = f"/settings/{SLUG}/baseline" BASE_URL_MAX = 200 BASE_URL_RE = re.compile(r"^https?://[^\s/?#]+(/[^\s?#]*)?$") MAX_POLL_INTERVAL = 3600 MAX_BUNDLE_SECONDS = 3600 MAX_AI_PER_MINUTE = 600 LIMITS = ( (POLL_INTERVAL, "Abfrageintervall", MIN_POLL_INTERVAL, MAX_POLL_INTERVAL), (ALERTS_DEFAULT_DELAY_MINUTES, "Standard-Verzögerung", 0, MAX_DELAY_MINUTES), (ALERTS_REMINDER_HOURS, "Erinnerung", 0, MAX_REMINDER_HOURS), (ALERTS_BUNDLE_SECONDS, "Bündelungsfenster", 0, MAX_BUNDLE_SECONDS), (ALERTS_AI_PER_MINUTE, "KI-Analysen pro Minute", 0, MAX_AI_PER_MINUTE), (ALERTS_HOST_DELAY_MINUTES, "Host-Verzögerung", 0, MAX_DELAY_MINUTES), (ALERTS_SEND_LIMIT, "Sendelimit", MIN_SEND_LIMIT, MAX_SEND_LIMIT), ) def strip_trailing_slash(value: str) -> str: return value.rstrip("/") def validate(values: dict) -> list[str]: errors = [] for key, label, low, high in LIMITS: raw = values.get(key, "").strip() if not raw: continue if not raw.isdecimal() or not low <= int(raw) <= high: errors.append(f"{label} muss eine ganze Zahl zwischen {low} und {high} sein.") base_url = values.get(UI_BASE_URL, "").strip() if base_url and (len(base_url) > BASE_URL_MAX or not BASE_URL_RE.match(base_url.rstrip("/"))): errors.append("Basis-URL der Oberfläche muss mit http:// oder https:// beginnen und darf keine " "Leerzeichen, Parameter oder Anker enthalten.") return errors SECTION = Section( slug=SLUG, title="Alarmierung", fields=( Field(POLL_INTERVAL, "Abfrageintervall in Sekunden", default=str(DEFAULT_POLL_INTERVAL)), Field(ALERTS_DEFAULT_DELAY_MINUTES, "Standard-Verzögerung in Minuten (Problem wird erst nach dieser Zeit gemeldet, 0 = sofort; " "Regeln können das überschreiben)", default=str(DEFAULT_DELAY_MINUTES)), Field(ALERTS_RECOVERY_ENABLED, "Recovery-Meldungen senden (wieder OK)", kind="checkbox"), Field(ALERTS_REMINDER_HOURS, "Standard-Erinnerung für offene CRIT-Probleme in Stunden (0 = aus; Regeln überschreiben)", default=str(DEFAULT_REMINDER_HOURS)), Field(ALERTS_BUNDLE_SECONDS, "Bündelungsfenster pro Host in Sekunden", default=str(DEFAULT_BUNDLE_SECONDS)), Field(ALERTS_AI_PER_MINUTE, "Max. KI-Analysen pro Minute (0 = keine)", default=str(DEFAULT_AI_PER_MINUTE)), Field(ALERTS_HOST_DELAY_MINUTES, "Host-Verzögerung in Minuten (Host DOWN/UNREACH wird erst nach dieser Zeit gemeldet, 0 = sofort)", default=str(DEFAULT_HOST_DELAY_MINUTES), hint="Regel-Verzögerungen gelten nicht für Host-Alarme. Solange ein Host nicht UP ist, werden " "Service-Alarme dieses Hosts unterdrückt."), Field(ALERTS_SEND_LIMIT, "Sendelimit: Nachrichten pro Minute und Ziel (1 bis 60)", default=str(DEFAULT_SEND_LIMIT), hint="Überschuss wird nicht einzeln gesendet, sondern als eine Sammelmeldung pro Minute und Ziel; " "alle Alarme bleiben im Verlauf."), Field(UI_BASE_URL, "Basis-URL dieser Oberfläche (optional)", placeholder="https://checkbot.example.de", transform=strip_trailing_slash, hint="Wenn gesetzt, enthält jede Alarmnachricht einen Link \"Details\" auf den Verlauf."), ), validate=validate, optional=True, actions=(Action("Baseline neu setzen", BASELINE_PATH),), )