Dateien
AegisSight-Monitor/src/email_utils/templates.py
Claude Code 4e51834163 feat(emails): zweisprachige E-Mail-Templates + Notification-Texte org-relativ
- email_utils/templates.magic_link_login_email + incident_notification_email
  nehmen jetzt lang Parameter (de | en).
- routers/auth.request_magic_link zieht Sprache aus der Org des Users und
  uebergibt sie ans Template.
- agents/orchestrator._send_email_notifications_for_incident lokalisiert
  ebenfalls und gibt lang an incident_notification_email durch.
- DB-Notification-Texte (refresh_summary, new_articles) sind in der
  Pipeline org-sprach-relativ (englische Variante: "3 new articles", etc.).
  Status-Change-Notifications: Codes (confirmed/contradicted) bleiben, FE
  uebersetzt sie in Phase 6.

Phase 5 von 8 (eng_demo / Org-Sprache).
2026-05-13 21:08:32 +00:00

153 Zeilen
5.9 KiB
Python

"""HTML-E-Mail-Vorlagen für Magic Links, Einladungen und Benachrichtigungen.
Sprache pro Empfaenger-Org gesteuert (Default 'de').
"""
def magic_link_login_email(username: str, link: str, lang: str = "de") -> tuple[str, str]:
"""Erzeugt Login-E-Mail mit Magic Link.
Args:
username: Empfaenger-Anzeigename
link: Magic-Link-URL
lang: ISO-Sprachcode ('de' | 'en')
Returns:
(subject, html_body)
"""
if lang == "en":
subject = "AegisSight Monitor - Sign in"
body = (
"Hi {username},",
"Click the button below to sign in:",
"Sign in",
"Or copy this link into your browser:",
"This link is valid for 10 minutes. If you did not request this sign-in, simply ignore this email.",
)
else:
subject = "AegisSight Monitor - Anmeldung"
body = (
"Hallo {username},",
"Klicken Sie auf den Button, um sich anzumelden:",
"Jetzt anmelden",
"Oder kopieren Sie diesen Link in Ihren Browser:",
"Dieser Link ist 10 Minuten gültig. Falls Sie diese Anmeldung nicht angefordert haben, ignorieren Sie diese E-Mail.",
)
greeting, intro, button_label, copy_hint, validity = body
html = f"""<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"></head>
<body style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; background: #0f172a; color: #e2e8f0; padding: 40px 20px;">
<div style="max-width: 480px; margin: 0 auto; background: #1e293b; border-radius: 12px; padding: 32px; border: 1px solid #334155;">
<h1 style="color: #f0b429; font-size: 20px; margin: 0 0 24px 0;">AegisSight Monitor</h1>
<p style="margin: 0 0 16px 0;">{greeting.format(username=username)}</p>
<p style="margin: 0 0 24px 0;">{intro}</p>
<div style="text-align: center; margin: 0 0 24px 0;">
<a href="{link}" style="display: inline-block; background: #f0b429; color: #0f172a; padding: 14px 40px; border-radius: 6px; text-decoration: none; font-weight: 600; font-size: 16px;">{button_label}</a>
</div>
<p style="color: #94a3b8; font-size: 13px; margin: 0 0 12px 0;">{copy_hint}</p>
<p style="color: #64748b; font-size: 11px; word-break: break-all; margin: 0 0 24px 0;">{link}</p>
<p style="color: #94a3b8; font-size: 13px; margin: 0;">{validity}</p>
</div>
</body>
</html>"""
return subject, html
def incident_notification_email(
username: str,
incident_title: str,
notifications: list[dict],
dashboard_url: str,
incident_type: str = "adhoc",
lang: str = "de",
) -> tuple[str, str]:
"""Erzeugt Benachrichtigungs-E-Mail für Lagen-Updates.
Args:
username: Empfaenger-Name
incident_title: Titel der Lage/Recherche
notifications: Liste von {"text": ..., "icon": ...} Dicts
dashboard_url: Link zum Dashboard
incident_type: "adhoc" oder "research"
lang: ISO-Sprachcode ('de' | 'en')
Returns:
(subject, html_body)
"""
is_research = incident_type == "research"
if lang == "en":
type_label = "Research" if is_research else "Situation"
type_label_lower = "research" if is_research else "situation"
notification_word = "notification"
greeting = f"Hi {username},"
intro = f"There is news on the {type_label_lower}"
button_label = "Open in dashboard"
footer = "You can disable these notifications in your dashboard settings."
else:
type_label = "Recherche" if is_research else "Lagebild"
type_label_lower = "Recherche" if is_research else "Lage"
notification_word = "Benachrichtigung"
greeting = f"Hallo {username},"
intro = f"es gibt Neuigkeiten zur {type_label_lower}"
button_label = "Im Dashboard ansehen"
footer = "Diese Benachrichtigung kann in den Einstellungen im Dashboard deaktiviert werden."
subject = f"AegisSight - {incident_title}"
icon_map = {
"success": "&#10003;",
"warning": "&#9888;",
"error": "&#10007;",
"info": "&#9432;",
}
color_map = {
"success": "#22c55e",
"warning": "#f0b429",
"error": "#ef4444",
"info": "#94a3b8",
}
items_html = ""
for n in notifications:
icon = icon_map.get(n.get("icon", "info"), "&#9432;")
color = color_map.get(n.get("icon", "info"), "#94a3b8")
text = n.get("text", "")
items_html += f"""
<div style="display: flex; align-items: flex-start; gap: 10px; padding: 10px 0; border-bottom: 1px solid #334155;">
<span style="color: {color}; font-size: 18px; line-height: 1;">{icon}</span>
<span style="color: #e2e8f0; font-size: 14px; line-height: 1.4;">{text}</span>
</div>"""
html = f"""<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"></head>
<body style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; background: #0f172a; color: #e2e8f0; padding: 40px 20px;">
<div style="max-width: 480px; margin: 0 auto; background: #1e293b; border-radius: 12px; padding: 32px; border: 1px solid #334155;">
<h1 style="color: #f0b429; font-size: 20px; margin: 0 0 8px 0;">AegisSight Monitor</h1>
<p style="color: #94a3b8; font-size: 12px; margin: 0 0 24px 0;">{type_label} - {notification_word}</p>
<p style="margin: 0 0 8px 0;">{greeting}</p>
<p style="margin: 0 0 20px 0;">{intro} <strong style="color: #f0b429;">{incident_title}</strong>:</p>
<div style="background: #0f172a; border-radius: 8px; padding: 4px 16px; margin: 0 0 24px 0;">
{items_html}
</div>
<div style="text-align: center; margin: 0 0 24px 0;">
<a href="{dashboard_url}" style="display: inline-block; background: #f0b429; color: #0f172a; padding: 12px 32px; border-radius: 6px; text-decoration: none; font-weight: 600;">{button_label}</a>
</div>
<p style="color: #64748b; font-size: 12px; margin: 0;">{footer}</p>
</div>
</body>
</html>"""
return subject, html