Security + E-Mail-HTML + Quartalsbericht + Registrierungspflicht
Registrierung & Login: - E-Mail-Verifikation jetzt Pflicht vor erstem Login - Register gibt keinen Token mehr zurück → "Postfach prüfen"-Screen - Login blockt mit EMAIL_NOT_VERIFIED (403) wenn unverifiziert - Resend-Verification ohne Auth (email-basiert) - Frontend: _renderVerifyPending() nach Register und Login-Fehler - Account-Lockout: 5 Fehlversuche → 15 Min gesperrt (ratelimit.py) - Login Rate-Limit zusätzlich per E-Mail-Adresse (5/5 Min) - Fehler-Tracking wird bei erfolgreichem Login zurückgesetzt E-Mail-Templates (alle Mails jetzt HTML): - email_html() Shared-Template in mailer.py (Gradient-Header, Warm-Beige) - Verifikations-Mail, Passwort-Reset → HTML mit CTA-Button - Admin-Outreach: plain text auto-wrapped in HTML - Züchter-Mails (Antrag/Genehmigung/Ablehnung) → Template - Tierschutz-Alert (litters.py) → Template - send_support_mail → HTML - outreach._build_message() + _send_smtp() unterstützen jetzt html= Parameter Forum-Schutz: - Post-Cooldown: 30 Sek zwischen beliebigen Posts (DB-Check) - Stunden-Limit: 5 Threads / 20 Antworten pro User/Stunde - Duplikat-Erkennung: gleicher Text in 5 Min blockiert (in-memory) - content_filter.py: Spam-Keywords, URL-Sperre für Accounts < 7 Tage, Sonderzeichen-Ratio-Check Security-Headers: - HSTS: max-age=31536000; includeSubDomains - Content-Security-Policy: frame-ancestors none, base-uri self, … - X-Frame-Options entfernt (CSP frame-ancestors ist moderner) Honeypot-Fallen (13 Scanner-Pfade → 24h IP-Sperre): - /api/admin/users, /api/v1/users, /api/.env, /api/config, /api/setup, /api/install, /api/phpinfo, /api/debug, /api/actuator, /api/swagger, /api/graphql u.a. Quartalsbericht-System: - backend/scripts/generate_reports.py: 6 Sections (Sicherheit, Funktionsumfang, Dateien, Nutzer, Partner, Server) - make reports: generiert alle Berichte aus dem Container, committed - Scheduler: quarterly_report Job (1. Feb/Mai/Aug/Nov 07:00) → vollständige HTML-Mail an ADMIN_EMAIL - quarterly_report erscheint im täglichen Status-Report Admin-Panel: - "Forum & Meldungen" → "Forum"
This commit is contained in:
parent
c1bb728153
commit
de1677154f
15 changed files with 1363 additions and 141 deletions
|
|
@ -106,44 +106,67 @@ async def send_email(to: str, subject: str, html: str, plain: str = ""):
|
|||
logger.warning(f"Kein Mail-Backend konfiguriert — Mail NICHT gesendet: «{subject}» → {to}")
|
||||
|
||||
|
||||
def email_html(
|
||||
body_html: str,
|
||||
cta_url: str = None,
|
||||
cta_label: str = None,
|
||||
footer_text: str = None,
|
||||
) -> str:
|
||||
"""Shared branded HTML email template (matches Status-Report design)."""
|
||||
cta_block = ""
|
||||
if cta_url and cta_label:
|
||||
cta_block = f"""
|
||||
<p style="margin:24px 0 0">
|
||||
<a href="{cta_url}"
|
||||
style="background:#C4843A;color:#fff;padding:14px 28px;border-radius:8px;
|
||||
text-decoration:none;font-weight:700;font-size:15px;display:inline-block">
|
||||
{cta_label}
|
||||
</a>
|
||||
</p>"""
|
||||
|
||||
footer = footer_text or "Ban Yaro · banyaro.app"
|
||||
|
||||
return f"""\
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head><meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
</head>
|
||||
<body style="font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;background:#f5f0ea;margin:0;padding:0">
|
||||
<div style="max-width:600px;margin:24px auto;background:#fff;border-radius:14px;overflow:hidden;box-shadow:0 2px 16px rgba(0,0,0,.08)">
|
||||
|
||||
<div style="background:linear-gradient(135deg,#C4843A,#e8a857);padding:24px 28px;color:#fff">
|
||||
<div style="font-size:22px;font-weight:800;margin-bottom:2px">🐾 Ban Yaro</div>
|
||||
</div>
|
||||
|
||||
<div style="padding:28px;color:#333;font-size:15px;line-height:1.6">
|
||||
{body_html}{cta_block}
|
||||
</div>
|
||||
|
||||
<div style="padding:14px 28px;background:#fdf6ef;font-size:11px;color:#aaa;text-align:center">
|
||||
{footer}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>"""
|
||||
|
||||
|
||||
async def send_verify_email(to: str, name: str, token: str):
|
||||
url = f"{APP_URL}/api/auth/verify/{token}"
|
||||
subject = "Ban Yaro — E-Mail-Adresse bestätigen"
|
||||
|
||||
html = f"""\
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head><meta charset="utf-8"></head>
|
||||
<body style="font-family:sans-serif;background:#f9f9f9;margin:0;padding:0">
|
||||
<div style="max-width:520px;margin:32px auto;background:#fff;border-radius:12px;
|
||||
padding:40px 32px;box-shadow:0 2px 8px rgba(0,0,0,.08)">
|
||||
<h1 style="color:#C4843A;margin:0 0 8px;font-size:24px">Ban Yaro 🐾</h1>
|
||||
<p style="color:#444;margin:0 0 24px">Hallo {name},</p>
|
||||
<p style="color:#444;margin:0 0 24px">
|
||||
body = f"""
|
||||
<p style="margin:0 0 16px">Hallo <b>{name}</b>,</p>
|
||||
<p style="margin:0 0 16px">
|
||||
bitte bestätige deine E-Mail-Adresse, damit dein Konto vollständig eingerichtet ist.
|
||||
</p>
|
||||
<p style="margin:0 0 32px">
|
||||
<a href="{url}"
|
||||
style="background:#C4843A;color:#fff;padding:14px 28px;border-radius:8px;
|
||||
text-decoration:none;font-weight:700;font-size:15px;display:inline-block">
|
||||
E-Mail bestätigen
|
||||
</a>
|
||||
</p>
|
||||
<p style="color:#888;font-size:13px;margin:0 0 8px">
|
||||
Der Link ist 48 Stunden gültig.
|
||||
</p>
|
||||
<p style="color:#bbb;font-size:12px;margin:0">
|
||||
<p style="margin:0;font-size:13px;color:#888">Der Link ist 48 Stunden gültig.</p>
|
||||
<p style="margin:12px 0 0;font-size:12px;color:#bbb">
|
||||
Falls du dich nicht bei Ban Yaro registriert hast, ignoriere diese E-Mail einfach.
|
||||
</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>"""
|
||||
</p>"""
|
||||
|
||||
plain = (
|
||||
f"Ban Yaro — E-Mail-Adresse bestätigen\n\n"
|
||||
f"Hallo {name},\n\n"
|
||||
f"bitte bestätige deine E-Mail-Adresse:\n{url}\n\n"
|
||||
f"Der Link ist 48 Stunden gültig.\n"
|
||||
)
|
||||
html = email_html(body, cta_url=url, cta_label="E-Mail bestätigen")
|
||||
plain = f"Ban Yaro — E-Mail bestätigen\n\nHallo {name},\n\nbitte bestätige:\n{url}\n\nLink ist 48h gültig.\n"
|
||||
|
||||
await send_email(to, subject, html, plain)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue