feat: global notification policy severity to channels (notif-22)
Singleton notification_policy table, NOTIFY_MIN_SEVERITY/CHANNELS env, central dispatch gate, Settings policy UI, migration 007. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,130 @@
|
||||
"""Global notification policy: severity ≥ min → selected channels."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.config import get_settings
|
||||
from app.models.notification_channel import NotificationChannel
|
||||
from app.models.notification_policy import POLICY_ROW_ID, NotificationPolicy
|
||||
from app.services.notification_settings import (
|
||||
CHANNEL_EMAIL,
|
||||
CHANNEL_TELEGRAM,
|
||||
CHANNEL_WEBHOOK,
|
||||
VALID_SEVERITIES,
|
||||
)
|
||||
|
||||
DEFAULT_CHANNELS = frozenset({CHANNEL_TELEGRAM, CHANNEL_WEBHOOK, CHANNEL_EMAIL})
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class NotificationPolicyConfig:
|
||||
min_severity: str
|
||||
use_telegram: bool
|
||||
use_webhook: bool
|
||||
use_email: bool
|
||||
source: str # env | db
|
||||
|
||||
def allows_channel(self, channel: str) -> bool:
|
||||
if channel == CHANNEL_TELEGRAM:
|
||||
return self.use_telegram
|
||||
if channel == CHANNEL_WEBHOOK:
|
||||
return self.use_webhook
|
||||
if channel == CHANNEL_EMAIL:
|
||||
return self.use_email
|
||||
return False
|
||||
|
||||
|
||||
def _parse_channels_csv(value: str) -> tuple[bool, bool, bool]:
|
||||
parts = {p.strip().lower() for p in value.split(",") if p.strip()}
|
||||
return (
|
||||
CHANNEL_TELEGRAM in parts or "tg" in parts,
|
||||
CHANNEL_WEBHOOK in parts,
|
||||
CHANNEL_EMAIL in parts or "mail" in parts or "smtp" in parts,
|
||||
)
|
||||
|
||||
|
||||
def _policy_from_env() -> NotificationPolicyConfig:
|
||||
settings = get_settings()
|
||||
use_tg, use_wh, use_em = _parse_channels_csv(settings.notify_channels)
|
||||
min_sev = settings.notify_min_severity.strip() or "warning"
|
||||
if min_sev not in VALID_SEVERITIES:
|
||||
min_sev = "warning"
|
||||
return NotificationPolicyConfig(
|
||||
min_severity=min_sev,
|
||||
use_telegram=use_tg,
|
||||
use_webhook=use_wh,
|
||||
use_email=use_em,
|
||||
source="env",
|
||||
)
|
||||
|
||||
|
||||
def get_effective_notification_policy(db: Session | None = None) -> NotificationPolicyConfig:
|
||||
if db is None:
|
||||
from app.database import SessionLocal
|
||||
|
||||
session = SessionLocal()
|
||||
try:
|
||||
return get_effective_notification_policy(session)
|
||||
finally:
|
||||
session.close()
|
||||
|
||||
row = db.get(NotificationPolicy, POLICY_ROW_ID)
|
||||
env_cfg = _policy_from_env()
|
||||
if row is None:
|
||||
return env_cfg
|
||||
|
||||
min_sev = (row.min_severity or "").strip() or env_cfg.min_severity
|
||||
if min_sev not in VALID_SEVERITIES:
|
||||
min_sev = env_cfg.min_severity
|
||||
|
||||
return NotificationPolicyConfig(
|
||||
min_severity=min_sev,
|
||||
use_telegram=bool(row.use_telegram),
|
||||
use_webhook=bool(row.use_webhook),
|
||||
use_email=bool(row.use_email),
|
||||
source="db",
|
||||
)
|
||||
|
||||
|
||||
def _sync_channel_min_severity(db: Session, min_severity: str) -> None:
|
||||
for channel in (CHANNEL_TELEGRAM, CHANNEL_WEBHOOK, CHANNEL_EMAIL):
|
||||
row = db.get(NotificationChannel, channel)
|
||||
if row is not None:
|
||||
row.min_severity = min_severity
|
||||
|
||||
|
||||
def upsert_notification_policy(
|
||||
db: Session,
|
||||
*,
|
||||
min_severity: str,
|
||||
use_telegram: bool,
|
||||
use_webhook: bool,
|
||||
use_email: bool,
|
||||
) -> NotificationPolicyConfig:
|
||||
if min_severity not in VALID_SEVERITIES:
|
||||
raise ValueError(f"invalid min_severity: {min_severity}")
|
||||
|
||||
row = db.get(NotificationPolicy, POLICY_ROW_ID)
|
||||
if row is None:
|
||||
row = NotificationPolicy(
|
||||
id=POLICY_ROW_ID,
|
||||
min_severity=min_severity,
|
||||
use_telegram=use_telegram,
|
||||
use_webhook=use_webhook,
|
||||
use_email=use_email,
|
||||
)
|
||||
db.add(row)
|
||||
else:
|
||||
row.min_severity = min_severity
|
||||
row.use_telegram = use_telegram
|
||||
row.use_webhook = use_webhook
|
||||
row.use_email = use_email
|
||||
|
||||
_sync_channel_min_severity(db, min_severity)
|
||||
db.commit()
|
||||
db.refresh(row)
|
||||
return get_effective_notification_policy(db)
|
||||
Reference in New Issue
Block a user