feat(email): HTML branded templates with inline logo, wire remaining notification types
Aegis CI / lint-and-test (push) Has been cancelled
Snyk Security Scan / Python vulnerabilities (backend) (push) Has been cancelled
Snyk Security Scan / npm vulnerabilities (frontend) (push) Has been cancelled
Snyk Security Scan / Docker image vulnerabilities (backend) (push) Has been cancelled

- All webhook emails now render as branded HTML (dark header, inline base64
  Aegis logo, card layout, CTA-button links) instead of plain text.
- Wired the 7 remaining notification-preference keys that had no trigger:
  stale coverage alerts, campaign-activated assignment emails, generic
  test-state-change steps (execution started / blue evaluating / in review),
  all-team-validation broadcasts on every lead vote, webhook delivery
  failures (3rd consecutive failure), new user registration, and background
  job errors (APScheduler global error listener).
- New notify_roles_by_email() helper for role-scoped, preference-gated,
  actor-excludable broadcasts.
- Fixed apscheduler.events stubbing gaps in several test files' sys.modules
  fakes that broke full-suite collection after adding the APScheduler
  error-listener import.
This commit is contained in:
kitos
2026-07-20 11:49:21 +02:00
parent 1d0d880929
commit 91442ede60
21 changed files with 403 additions and 18 deletions
+23
View File
@@ -16,6 +16,7 @@ from datetime import datetime, timedelta, timezone
# Import BackgroundScheduler from apscheduler.schedulers.background
from apscheduler.schedulers.background import BackgroundScheduler
from apscheduler.events import EVENT_JOB_ERROR
# Import SessionLocal from app.database
from app.database import SessionLocal
@@ -60,6 +61,27 @@ logger = logging.getLogger(__name__)
scheduler = BackgroundScheduler()
def _on_job_error(event) -> None:
"""Email admins (opted-in) whenever a scheduled background job raises."""
db = SessionLocal()
try:
from app.services.notification_service import notify_roles_by_email
notify_roles_by_email(
db, roles=["admin"],
preference_key="email_on_system_errors",
subject=f"Aegis Background Job Failed: {event.job_id}",
message=(
f'The scheduled job "{event.job_id}" raised an exception and did '
f"not complete:\n\n{event.exception}"
),
)
db.commit()
except Exception:
logger.exception("Failed to dispatch system-error notification")
finally:
db.close()
# ---------------------------------------------------------------------------
# Job functions
# ---------------------------------------------------------------------------
@@ -440,6 +462,7 @@ def start_scheduler() -> None:
Neither job fires immediately on startup.
"""
scheduler.add_listener(_on_job_error, EVENT_JOB_ERROR)
# Call scheduler.add_job()
scheduler.add_job(
_run_mitre_sync,