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
@@ -274,6 +274,7 @@ class NotificationDispatcher:
to_email=receiver.email,
subject=subject,
body_text=message,
body_html=message,
to_name=receiver.name,
)
results.append(result)
@@ -30,6 +30,33 @@ class EmailClient:
def __init__(self, smtp_config: SmtpConfig) -> None:
self._config = smtp_config
async def verify_connection(self) -> dict[str, Any]:
"""Test SMTP connection and authentication without sending an email."""
try:
import aiosmtplib
except ImportError:
return {"success": False, "error": "aiosmtplib not installed"}
cfg = self._config
if not cfg.host:
return {"success": False, "error": "SMTP host not configured"}
try:
smtp = aiosmtplib.SMTP(
hostname=cfg.host,
port=cfg.port,
use_tls=cfg.use_tls,
start_tls=not cfg.use_tls and cfg.port != 25,
)
await smtp.connect()
if cfg.username and cfg.password:
await smtp.login(cfg.username, cfg.password)
await smtp.quit()
return {"success": True}
except Exception as e:
_LOGGER.warning("SMTP verification failed for %s:%d: %s", cfg.host, cfg.port, e)
return {"success": False, "error": str(e)}
async def send(
self,
to_email: str,