feat: add apprise notification delivery

This commit is contained in:
Christian Krakau-Louis
2026-05-22 22:42:29 +02:00
parent 701a3603b5
commit dd4b90c9de
9 changed files with 320 additions and 162 deletions
+38 -46
View File
@@ -21,6 +21,7 @@ from sqlalchemy.orm import Session
from app.core.database import get_db
from app.core.security import require_admin_auth
from app.models.setting import Setting
from app.services.notifications import send_notification
router = APIRouter()
logger = logging.getLogger(__name__)
@@ -113,66 +114,25 @@ SETTING_DEFAULTS: List[Dict[str, Any]] = [
},
# ── Notifications ─────────────────────────────────────────────────────────
{
"key": "notifications.email_enabled",
"key": "notifications.apprise_enabled",
"value": "false",
"description": "Send email notifications when new DMARC failures are detected",
"description": "Send notifications through configured Apprise target URLs",
"value_type": "boolean",
"category": "notifications",
},
{
"key": "notifications.email_from",
"key": "notifications.apprise_urls",
"value": "",
"description": "From address used for notification emails",
"description": "Newline-separated Apprise notification target URLs",
"value_type": "string",
"category": "notifications",
},
{
"key": "notifications.email_to",
"value": "",
"description": "Comma-separated list of recipient addresses for notifications",
"value_type": "string",
"category": "notifications",
},
{
"key": "notifications.smtp_host",
"value": "",
"description": "SMTP server hostname for sending notification emails",
"value_type": "string",
"category": "notifications",
},
{
"key": "notifications.smtp_port",
"value": "587",
"description": "SMTP server port",
"value_type": "integer",
"category": "notifications",
},
{
"key": "notifications.smtp_username",
"value": "",
"description": "SMTP authentication username",
"value_type": "string",
"category": "notifications",
},
{
"key": "notifications.smtp_password",
"value": "",
"description": "SMTP authentication password",
"value_type": "string",
"category": "notifications",
},
{
"key": "notifications.smtp_use_tls",
"value": "true",
"description": "Use TLS when connecting to the SMTP server",
"value_type": "boolean",
"category": "notifications",
},
]
# Keys whose values should be redacted in GET responses (treated as secrets)
_SECRET_KEYS = {
"cloudflare.api_token",
"notifications.apprise_urls",
"notifications.smtp_password",
}
@@ -245,6 +205,17 @@ class SettingResponse(BaseModel):
updated_at: Optional[str]
class NotificationTestResponse(BaseModel):
"""Sanitized response from a test notification send."""
success: bool
message: str
configured_targets: int = 0
invalid_targets: int = 0
skipped: bool = False
error: Optional[str] = None
# ---------------------------------------------------------------------------
# Endpoints
# ---------------------------------------------------------------------------
@@ -269,6 +240,27 @@ async def list_settings(
return [_row_to_dict(row) for row in rows]
@router.post("/notifications/test", response_model=NotificationTestResponse)
async def test_notification_settings(
db: Session = Depends(get_db),
_auth: dict = Depends(require_admin_auth),
) -> NotificationTestResponse:
"""Send a test notification using the configured Apprise targets."""
_seed_defaults(db)
result = send_notification(
db,
title="DMARQ test notification",
body="This confirms that DMARQ can reach the configured notification target.",
force=True,
)
if not result.success:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=result.to_dict(),
)
return result.to_dict()
@router.get("/{key:path}", response_model=SettingResponse)
async def get_setting(
key: str,