'Invalid email or type.']); return; } try { $db = Database::getConnection(); // Rate limit: no more than 3 per day $dailyCheck = $db->prepare("SELECT COUNT(*) FROM verification_attempts WHERE email = ? AND type = ? AND attempted_at >= NOW() - INTERVAL 1 DAY"); $dailyCheck->execute([$email, $type]); $dailyCount = $dailyCheck->fetchColumn(); if ($dailyCount >= 3) { View::render('pages/verify_failed', ['reason' => 'You have reached the daily resend limit. Please try again tomorrow.']); return; } // Rate limit: no more than 1 every 5 minutes $recentCheck = $db->prepare("SELECT COUNT(*) FROM verification_attempts WHERE email = ? AND type = ? AND attempted_at >= NOW() - INTERVAL 5 MINUTE"); $recentCheck->execute([$email, $type]); $recentCount = $recentCheck->fetchColumn(); if ($recentCount > 0) { View::render('pages/verify_failed', ['reason' => 'You must wait a few minutes before requesting another verification email.']); return; } // Log attempt $log = $db->prepare("INSERT INTO verification_attempts (email, type, attempted_at, ip_address, user_agent) VALUES (?, ?, NOW(), ?, ?)"); $log->execute([ $email, $type, $_SERVER['REMOTE_ADDR'] ?? 'unknown', $_SERVER['HTTP_USER_AGENT'] ?? 'unknown', ]); $code = bin2hex(random_bytes(16)); $expiry = (new \DateTime())->modify('+72 hours')->format('Y-m-d H:i:s'); if ($type === 'newsletter') { $stmt = $db->prepare("SELECT id, is_verified FROM subscribers WHERE email = ?"); $stmt->execute([$email]); $row = $stmt->fetch(); if (!$row || (int)$row['is_verified'] === 1) { View::render('pages/verify_failed', ['reason' => 'Email is already verified or not found.']); return; } $update = $db->prepare("UPDATE subscribers SET verification_code = ?, is_verified = 0, verification_expires_at = ? WHERE id = ?"); $update->execute([$code, $expiry, $row['id']]); NewsletterService::sendVerificationEmail($email, $code); } if ($type === 'contact') { $stmt = $db->prepare("SELECT id, is_verified FROM contact_messages WHERE email = ? ORDER BY created_at DESC LIMIT 1"); $stmt->execute([$email]); $row = $stmt->fetch(); if (!$row || (int)$row['is_verified'] === 1) { View::render('pages/verify_failed', ['reason' => 'Email is already verified or not found.']); return; } $update = $db->prepare("UPDATE contact_messages SET verification_code = ?, is_verified = 0, verification_expires_at = ? WHERE id = ?"); $update->execute([$code, $expiry, $row['id']]); ContactService::sendVerificationEmail($email, $code); } View::render('pages/verify_success', [ 'type' => $type, 'message' => 'We just sent you a new verification link.' ]); } catch (\Throwable $e) { Logger::error("Resend verification failed: " . $e->getMessage()); View::render('pages/verify_failed', ['reason' => 'Unexpected error occurred.']); } } }