mirror of
https://github.com/zvx-echo6/meshai.git
synced 2026-06-11 17:34:44 +02:00
refactor(notifications): self-contained rules, remove abstract channels
- Each notification rule contains its own delivery config inline - No more separate channels with abstract IDs to cross-reference - Delivery type selector (Mesh Broadcast/DM/Email/Webhook) with inline config fields per type - Follows MeshMonitor trigger-action UX pattern - Channel picker from radio for mesh broadcast - Node picker for mesh DM - Collapsed rule cards show readable one-line summary - Trigger type: condition (alerts) or schedule (daily reports) - Schedule triggers support daily, weekly, custom cron - Message types: mesh health, RF propagation, alerts digest, custom - Migrates old channels+rules config to new flat format on load Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
3fa7b9fe5e
commit
b4f7e24c26
9 changed files with 1248 additions and 1095 deletions
|
|
@ -1,45 +1,10 @@
|
|||
"""Notification API routes."""
|
||||
|
||||
from fastapi import APIRouter, Request, HTTPException
|
||||
from pydantic import BaseModel
|
||||
from typing import Optional
|
||||
|
||||
router = APIRouter(prefix="/notifications", tags=["notifications"])
|
||||
|
||||
|
||||
class ChannelCreate(BaseModel):
|
||||
"""Channel creation request."""
|
||||
id: str
|
||||
type: str
|
||||
enabled: bool = True
|
||||
channel_index: int = 0
|
||||
node_ids: list[str] = []
|
||||
smtp_host: str = ""
|
||||
smtp_port: int = 587
|
||||
smtp_user: str = ""
|
||||
smtp_password: str = ""
|
||||
smtp_tls: bool = True
|
||||
from_address: str = ""
|
||||
recipients: list[str] = []
|
||||
url: str = ""
|
||||
headers: dict = {}
|
||||
|
||||
|
||||
class RuleCreate(BaseModel):
|
||||
"""Rule creation request."""
|
||||
name: str
|
||||
categories: list[str] = []
|
||||
min_severity: str = "warning"
|
||||
channel_ids: list[str] = []
|
||||
override_quiet: bool = False
|
||||
|
||||
|
||||
class QuietHoursUpdate(BaseModel):
|
||||
"""Quiet hours update request."""
|
||||
start: str
|
||||
end: str
|
||||
|
||||
|
||||
@router.get("/categories")
|
||||
async def get_categories():
|
||||
"""Get all alert categories with descriptions."""
|
||||
|
|
@ -50,34 +15,6 @@ async def get_categories():
|
|||
return []
|
||||
|
||||
|
||||
@router.get("/channels")
|
||||
async def get_channels(request: Request):
|
||||
"""Get configured notification channels."""
|
||||
notification_router = getattr(request.app.state, "notification_router", None)
|
||||
if not notification_router:
|
||||
return []
|
||||
return notification_router.get_channels()
|
||||
|
||||
|
||||
@router.post("/channels")
|
||||
async def create_channel(request: Request, channel: ChannelCreate):
|
||||
"""Create a new notification channel."""
|
||||
# This would require runtime config modification
|
||||
# For now, return not implemented
|
||||
raise HTTPException(status_code=501, detail="Channel creation requires config file edit")
|
||||
|
||||
|
||||
@router.post("/channels/{channel_id}/test")
|
||||
async def test_channel(request: Request, channel_id: str):
|
||||
"""Send a test alert to a channel."""
|
||||
notification_router = getattr(request.app.state, "notification_router", None)
|
||||
if not notification_router:
|
||||
raise HTTPException(status_code=404, detail="Notification router not configured")
|
||||
|
||||
success, message = await notification_router.test_channel(channel_id)
|
||||
return {"success": success, "message": message}
|
||||
|
||||
|
||||
@router.get("/rules")
|
||||
async def get_rules(request: Request):
|
||||
"""Get configured notification rules."""
|
||||
|
|
@ -87,27 +24,12 @@ async def get_rules(request: Request):
|
|||
return notification_router.get_rules()
|
||||
|
||||
|
||||
@router.post("/rules")
|
||||
async def create_rule(request: Request, rule: RuleCreate):
|
||||
"""Create a new notification rule."""
|
||||
# This would require runtime config modification
|
||||
raise HTTPException(status_code=501, detail="Rule creation requires config file edit")
|
||||
@router.post("/rules/{rule_index}/test")
|
||||
async def test_rule(request: Request, rule_index: int):
|
||||
"""Send a test alert through a specific rule."""
|
||||
notification_router = getattr(request.app.state, "notification_router", None)
|
||||
if not notification_router:
|
||||
raise HTTPException(status_code=404, detail="Notification router not configured")
|
||||
|
||||
|
||||
@router.get("/quiet-hours")
|
||||
async def get_quiet_hours(request: Request):
|
||||
"""Get quiet hours configuration."""
|
||||
config = getattr(request.app.state, "config", None)
|
||||
if not config or not hasattr(config, "notifications"):
|
||||
return {"start": "22:00", "end": "06:00"}
|
||||
return {
|
||||
"start": config.notifications.quiet_hours_start,
|
||||
"end": config.notifications.quiet_hours_end,
|
||||
}
|
||||
|
||||
|
||||
@router.put("/quiet-hours")
|
||||
async def update_quiet_hours(request: Request, quiet_hours: QuietHoursUpdate):
|
||||
"""Update quiet hours configuration."""
|
||||
# This would require runtime config modification
|
||||
raise HTTPException(status_code=501, detail="Quiet hours update requires config file edit")
|
||||
success, message = await notification_router.test_rule(rule_index)
|
||||
return {"success": success, "message": message}
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
425
meshai/dashboard/static/assets/index-BOJS6jme.js
Normal file
425
meshai/dashboard/static/assets/index-BOJS6jme.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -8,8 +8,8 @@
|
|||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600;700&display=swap" rel="stylesheet">
|
||||
<script type="module" crossorigin src="/assets/index-BNjrbmGz.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-so1NV9Au.css">
|
||||
<script type="module" crossorigin src="/assets/index-BOJS6jme.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-DG_2rmdm.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue