orderBy('updated_at', 'desc'); $status = strtolower(trim((string) ($filters['status'] ?? 'all'))); if ($status === 'active') { $query->where('blocked_until', '>', $this->utcNow()); } if (!empty($filters['search'])) { $term = '%' . trim((string) $filters['search']) . '%'; $query->where('ip_address', 'like', $term); } $sortBy = (string) ($filters['sort_by'] ?? 'updated_at'); $sortDir = strtolower((string) ($filters['sort_dir'] ?? 'desc')) === 'asc' ? 'asc' : 'desc'; $allowedSorts = ['updated_at', 'blocked_until', 'attempts', 'ip_address', 'last_attempt_at']; if (in_array($sortBy, $allowedSorts, true)) { $query->orderBy($sortBy, $sortDir); } return $query->paginate($perPage, ['*'], 'page', $page); } public function find(int $id): ?IpAttempt { return IpAttempt::query()->find($id); } private function utcNow(): string { if (function_exists('utc_now')) { return (string) utc_now(); } return now('UTC')->toDateTimeString(); } }