diff --git a/backend/routers/auth.py b/backend/routers/auth.py index f8ccce4..ec67cd6 100644 --- a/backend/routers/auth.py +++ b/backend/routers/auth.py @@ -7,7 +7,7 @@ import os import secrets import smtplib from typing import Optional -from datetime import datetime, timedelta +from datetime import datetime, timedelta, timezone from email.mime.text import MIMEText from fastapi import APIRouter, HTTPException, Header, Depends @@ -233,19 +233,20 @@ async def register(req: RegisterRequest, request: Request): # Generate verification token verification_token = secrets.token_urlsafe(32) - verification_expires = datetime.now() + timedelta(hours=24) + verification_expires = datetime.now(timezone.utc) + timedelta(hours=24) # Create profile (inactive until verified) profile_id = str(secrets.token_hex(16)) pin_hash = hash_pin(password) + trial_ends = datetime.now(timezone.utc) + timedelta(days=14) # 14-day trial cur.execute(""" INSERT INTO profiles ( id, name, email, pin_hash, auth_type, role, tier, email_verified, verification_token, verification_expires, - created - ) VALUES (%s, %s, %s, %s, 'email', 'user', 'free', FALSE, %s, %s, CURRENT_TIMESTAMP) - """, (profile_id, name, email, pin_hash, verification_token, verification_expires)) + trial_ends_at, created + ) VALUES (%s, %s, %s, %s, 'email', 'user', 'free', FALSE, %s, %s, %s, CURRENT_TIMESTAMP) + """, (profile_id, name, email, pin_hash, verification_token, verification_expires, trial_ends)) # Send verification email app_url = os.getenv("APP_URL", "https://mitai.jinkendo.de") @@ -294,7 +295,7 @@ async def verify_email(token: str): raise HTTPException(400, "E-Mail-Adresse bereits bestätigt") # Check if token expired - if prof['verification_expires'] and datetime.now() > prof['verification_expires']: + if prof['verification_expires'] and datetime.now(timezone.utc) > prof['verification_expires']: raise HTTPException(400, "Verifikations-Link abgelaufen. Bitte registriere dich erneut.") # Mark as verified and clear token @@ -306,7 +307,7 @@ async def verify_email(token: str): # Create session (auto-login after verification) session_token = make_token() - expires = datetime.now() + timedelta(days=30) + expires = datetime.now(timezone.utc) + timedelta(days=30) cur.execute(""" INSERT INTO sessions (token, profile_id, expires_at, created) VALUES (%s, %s, %s, CURRENT_TIMESTAMP) diff --git a/frontend/src/components/EmailVerificationBanner.jsx b/frontend/src/components/EmailVerificationBanner.jsx new file mode 100644 index 0000000..302ffec --- /dev/null +++ b/frontend/src/components/EmailVerificationBanner.jsx @@ -0,0 +1,42 @@ +export default function EmailVerificationBanner({ profile }) { + // Only show if email is not verified + if (!profile || profile.email_verified !== false) return null + + return ( +
+
+ 📧 +
+
+
+ E-Mail-Adresse noch nicht bestätigt +
+
+ Bitte prüfe dein Postfach und klicke auf den Bestätigungslink. + Ohne Bestätigung ist dein Account eingeschränkt. +
+
+
+ ) +} diff --git a/frontend/src/pages/Dashboard.jsx b/frontend/src/pages/Dashboard.jsx index a5fc3cb..536367d 100644 --- a/frontend/src/pages/Dashboard.jsx +++ b/frontend/src/pages/Dashboard.jsx @@ -9,6 +9,7 @@ import { api } from '../utils/api' import { useProfile } from '../context/ProfileContext' import { getBfCategory } from '../utils/calc' import TrialBanner from '../components/TrialBanner' +import EmailVerificationBanner from '../components/EmailVerificationBanner' import { getInterpretation, getStatusColor, getStatusBg } from '../utils/interpret' import Markdown from '../utils/Markdown' import dayjs from 'dayjs' @@ -316,6 +317,9 @@ export default function Dashboard() { + {/* Email Verification Banner */} + + {/* Trial Banner */}