false,'error'=>'invalid_email']); exit; } // Rate limiting table creation and validation try { $conn->query("CREATE TABLE IF NOT EXISTS verification_attempts ( email VARCHAR(255) PRIMARY KEY, attempts INT NOT NULL DEFAULT 0, last_attempt_at DATETIME NOT NULL, locked_until DATETIME NULL, code_hash VARCHAR(128) NULL, expires_at DATETIME NULL )"); } catch (Exception $e) {} $nowStr = date('Y-m-d H:i:s'); $nowTime = time(); $stmt = $conn->prepare("SELECT attempts, last_attempt_at, locked_until FROM verification_attempts WHERE email = ?"); $stmt->bind_param('s', $email); $stmt->execute(); $res = $stmt->get_result(); $row = $res ? $res->fetch_assoc() : null; $stmt->close(); if($row){ $attempts = intval($row['attempts']); $lastAttempt = strtotime($row['last_attempt_at']); $lockedUntil = $row['locked_until'] ? strtotime($row['locked_until']) : null; if($lockedUntil && $nowTime < $lockedUntil){ $diff = $lockedUntil - $nowTime; $hours = floor($diff / 3600); $mins = ceil(($diff % 3600) / 60); http_response_code(429); echo json_encode([ 'success' => false, 'error' => 'locked_24h', 'message' => "Batas pengiriman kode tercapai. Anda harus menunggu {$hours} jam {$mins} menit.", 'locked_until' => $lockedUntil * 1000 ]); exit; } if($nowTime - $lastAttempt < 30){ $remaining = 30 - ($nowTime - $lastAttempt); http_response_code(429); echo json_encode([ 'success' => false, 'error' => 'cooldown_active', 'message' => "Silakan tunggu {$remaining} detik sebelum mengirim kembali.", 'remaining' => $remaining ]); exit; } } // Generate 6-digit code $verificationCode = str_pad(random_int(0, 999999), 6, '0', STR_PAD_LEFT); $codeHash = hash('sha256', $verificationCode); $expiresAt = date('Y-m-d H:i:s', $nowTime + 15 * 60); // 15 minutes expiry $mailSuccess = false; try{ $subject = 'Kode Verifikasi Email - WebGIS'; $message = "Kode verifikasi Anda adalah:\n\n" . $verificationCode . "\n\nKode ini berlaku selama 15 menit. Jangan bagikan kode ini kepada siapapun.\n\nJika Anda tidak melakukan pendaftaran ini, abaikan email ini."; $mailSuccess = send_mail_notify($email, $subject, $message, false); }catch(Exception $e){ error_log('Mailing exception: ' . $e->getMessage()); } // Update/Insert attempts on database if($row){ $newAttempts = $row['attempts']; if($mailSuccess){ $newAttempts = ($row['locked_until'] && $nowTime >= strtotime($row['locked_until'])) ? 1 : ($row['attempts'] + 1); } $newLockedUntil = ($newAttempts >= 3) ? date('Y-m-d H:i:s', $nowTime + 24 * 3600) : $row['locked_until']; $up = $conn->prepare("UPDATE verification_attempts SET attempts = ?, last_attempt_at = ?, locked_until = ?, code_hash = ?, expires_at = ? WHERE email = ?"); $up->bind_param('isssss', $newAttempts, $nowStr, $newLockedUntil, $codeHash, $expiresAt, $email); $up->execute(); $up->close(); } else { $attemptsVal = $mailSuccess ? 1 : 0; $nullVal = null; $ins = $conn->prepare("INSERT INTO verification_attempts (email, attempts, last_attempt_at, locked_until, code_hash, expires_at) VALUES (?, ?, ?, ?, ?, ?)"); $ins->bind_param('sissss', $email, $attemptsVal, $nowStr, $nullVal, $codeHash, $expiresAt); $ins->execute(); $ins->close(); } $response = ['success'=>true,'email'=>$email,'message'=>'code_sent']; if(getenv('DEBUG_MODE') === '1' || $_SERVER['REMOTE_ADDR'] === '127.0.0.1' || !$mailSuccess){ $response['debug_code'] = $verificationCode; } echo json_encode($response); ?>