feat: default tracker configs, email validation, expandable target links

- Tracker now has default_tracking_config_id and default_template_config_id
  that apply to all linked targets unless overridden per-target
- Dispatch falls back to tracker defaults when per-link configs are null
- Email bot creation validates SMTP connection before saving
- Email notifications sent as HTML (links render properly)
- Linked target items are expandable: collapsed shows config CrossLinks,
  expanded shows config selectors; action buttons always visible
- Fix email bot test button icon (mdiEmailSend → mdiSend)
- Fix target type icons in LinkedTargetsSection for all types
- Provider filter moved above search in sidebar
This commit is contained in:
2026-03-24 22:32:37 +03:00
parent d4cb388c74
commit 6e35926772
16 changed files with 246 additions and 102 deletions
@@ -55,6 +55,18 @@ async def create_email_bot(
user: User = Depends(get_current_user),
session: AsyncSession = Depends(get_session),
):
# Verify SMTP connection before saving
from notify_bridge_core.notifications.email.client import EmailClient, SmtpConfig
client = EmailClient(SmtpConfig(
host=body.smtp_host, port=body.smtp_port,
username=body.smtp_username, password=body.smtp_password,
from_address=body.email, from_name=body.name,
use_tls=body.smtp_use_tls,
))
result = await client.verify_connection()
if not result.get("success"):
raise HTTPException(status_code=400, detail=f"SMTP connection failed: {result.get('error', 'Unknown error')}")
bot = EmailBot(user_id=user.id, **body.model_dump())
session.add(bot)
await session.commit()
@@ -32,6 +32,8 @@ class NotificationTrackerCreate(BaseModel):
collection_ids: list[str] = []
scan_interval: int = 60
batch_duration: int = 0
default_tracking_config_id: int | None = None
default_template_config_id: int | None = None
enabled: bool = True
@@ -41,6 +43,8 @@ class NotificationTrackerUpdate(BaseModel):
collection_ids: list[str] | None = None
scan_interval: int | None = None
batch_duration: int | None = None
default_tracking_config_id: int | None = None
default_template_config_id: int | None = None
enabled: bool | None = None
@@ -190,6 +194,8 @@ async def _tracker_response(session: AsyncSession, t: NotificationTracker) -> di
"collection_ids": t.collection_ids,
"scan_interval": t.scan_interval,
"batch_duration": t.batch_duration,
"default_tracking_config_id": t.default_tracking_config_id,
"default_template_config_id": t.default_template_config_id,
"enabled": t.enabled,
"tracker_targets": tracker_targets,
"created_at": t.created_at.isoformat(),