diff --git a/backend/routers/auth.py b/backend/routers/auth.py index ec67cd6..6f83e92 100644 --- a/backend/routers/auth.py +++ b/backend/routers/auth.py @@ -323,3 +323,74 @@ async def verify_email(token: str): "email": prof['email'] } } + + +@router.post("/resend-verification") +@limiter.limit("3/hour") +async def resend_verification(req: dict, request: Request): + """Resend verification email for unverified account.""" + email = req.get('email', '').strip().lower() + + if not email: + raise HTTPException(400, "E-Mail-Adresse erforderlich") + + with get_db() as conn: + cur = get_cursor(conn) + + # Find profile by email + cur.execute(""" + SELECT id, name, email, email_verified, verification_token, verification_expires + FROM profiles + WHERE email=%s + """, (email,)) + + prof = cur.fetchone() + + if not prof: + # Don't leak info about existing emails + return {"ok": True, "message": "Falls ein Account mit dieser E-Mail existiert, wurde eine Bestätigungs-E-Mail versendet."} + + if prof['email_verified']: + raise HTTPException(400, "E-Mail-Adresse bereits bestätigt") + + # Generate new verification token + verification_token = secrets.token_urlsafe(32) + verification_expires = datetime.now(timezone.utc) + timedelta(hours=24) + + cur.execute(""" + UPDATE profiles + SET verification_token=%s, verification_expires=%s + WHERE id=%s + """, (verification_token, verification_expires, prof['id'])) + + # Send verification email + app_url = os.getenv("APP_URL", "https://mitai.jinkendo.de") + verify_url = f"{app_url}/verify?token={verification_token}" + + email_body = f"""Hallo {prof['name']}, + +du hast eine neue Bestätigungs-E-Mail angefordert. + +Bitte bestätige deine E-Mail-Adresse, indem du auf folgenden Link klickst: + +{verify_url} + +Dieser Link ist 24 Stunden gültig. + +Falls du diese E-Mail nicht angefordert hast, kannst du sie einfach ignorieren. + +Viele Grüße +Dein Mitai Jinkendo Team +""" + + try: + send_email( + to=email, + subject="Neue Bestätigungs-E-Mail - Mitai Jinkendo", + body=email_body + ) + except Exception as e: + print(f"Failed to send verification email: {e}") + raise HTTPException(500, "E-Mail konnte nicht versendet werden") + + return {"ok": True, "message": "Bestätigungs-E-Mail wurde erneut versendet."} diff --git a/frontend/src/components/EmailVerificationBanner.jsx b/frontend/src/components/EmailVerificationBanner.jsx index 302ffec..cf65c98 100644 --- a/frontend/src/components/EmailVerificationBanner.jsx +++ b/frontend/src/components/EmailVerificationBanner.jsx @@ -1,7 +1,33 @@ +import { useState } from 'react' +import { api } from '../utils/api' + export default function EmailVerificationBanner({ profile }) { + const [resending, setResending] = useState(false) + const [success, setSuccess] = useState(false) + const [error, setError] = useState(null) + // Only show if email is not verified if (!profile || profile.email_verified !== false) return null + const handleResend = async () => { + if (!profile.email) return + + setResending(true) + setError(null) + setSuccess(false) + + try { + await api.resendVerification(profile.email) + setSuccess(true) + setTimeout(() => setSuccess(false), 5000) + } catch (err) { + setError(err.message || 'Fehler beim Versenden') + setTimeout(() => setError(null), 5000) + } finally { + setResending(false) + } + } + return (
+ Dieser Link ist leider nicht mehr gültig. Bitte fordere eine neue Bestätigungs-E-Mail an. +
+ + {resendSuccess ? ( ++ Bitte prüfe dein Postfach. +
+