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
@@ -104,14 +104,7 @@ async def _send_telegram_broadcast(target: NotificationTarget, message: str, rec
)
results.append(result)
successes = sum(1 for r in results if r.get("success"))
if successes == len(results) and results:
return {"success": True, "receivers": len(results)}
elif successes > 0:
return {"success": True, "receivers": len(results), "partial_failures": len(results) - successes}
elif results:
return results[0]
return {"success": False, "error": "No valid receivers"}
return _aggregate(results)
async def _send_webhook_broadcast(target: NotificationTarget, message: str, receivers: list[dict]) -> dict:
@@ -130,14 +123,7 @@ async def _send_webhook_broadcast(target: NotificationTarget, message: str, rece
client = WebhookClient(session, url, headers)
results.append(await client.send({"message": message, "event_type": "notification"}))
successes = sum(1 for r in results if r.get("success"))
if successes == len(results) and results:
return {"success": True, "receivers": len(results)}
elif successes > 0:
return {"success": True, "receivers": len(results), "partial_failures": len(results) - successes}
elif results:
return results[0]
return {"success": False, "error": "No valid receivers"}
return _aggregate(results)
async def _send_email_broadcast(target: NotificationTarget, message: str, receivers: list[dict]) -> dict:
@@ -179,18 +165,12 @@ async def _send_email_broadcast(target: NotificationTarget, message: str, receiv
to_email=email,
subject="Notification from Notify Bridge",
body_text=message,
body_html=message,
to_name=recv.get("name", ""),
)
results.append(result)
successes = sum(1 for r in results if r.get("success"))
if successes == len(results) and results:
return {"success": True, "receivers": len(results)}
elif successes > 0:
return {"success": True, "receivers": len(results), "partial_failures": len(results) - successes}
elif results:
return results[0]
return {"success": False, "error": "No valid email receivers"}
return _aggregate(results)
async def _send_webhook_like_broadcast(target: NotificationTarget, message: str, receivers: list[dict]) -> dict: