query("DELETE FROM login_attempts WHERE ip_address = :ip", ['ip' => $ip]); $_SESSION['wt_admin'] = true; session_regenerate_id(true); return true; } self::recordFailure($ip); return false; } public static function isLockedOut(string $ip): bool { $row = db()->fetch("SELECT * FROM login_attempts WHERE ip_address = :ip", ['ip' => $ip]); if (!$row || (int) $row['attempts'] < self::MAX_ATTEMPTS) { return false; } $lockoutUntil = strtotime($row['last_attempt']) + self::LOCKOUT_MINUTES * 60; return time() < $lockoutUntil; } private static function recordFailure(string $ip): void { $row = db()->fetch("SELECT * FROM login_attempts WHERE ip_address = :ip", ['ip' => $ip]); if ($row) { $lockoutUntil = strtotime($row['last_attempt']) + self::LOCKOUT_MINUTES * 60; // Reset the counter once the previous lockout window has fully expired. $attempts = (time() < $lockoutUntil) ? (int) $row['attempts'] + 1 : 1; db()->update('login_attempts', ['attempts' => $attempts, 'last_attempt' => date('Y-m-d H:i:s')], 'ip_address = :ip', ['ip' => $ip]); } else { db()->insert('login_attempts', ['ip_address' => $ip, 'attempts' => 1, 'last_attempt' => date('Y-m-d H:i:s')]); } } public static function isLoggedIn(): bool { return !empty($_SESSION['wt_admin']); } public static function require(): void { if (!self::isLoggedIn()) { header('Location: /admin/login.php'); exit; } } public static function logout(): void { unset($_SESSION['wt_admin']); session_regenerate_id(true); } }