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
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:
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user