findRow($id, $ip); if (!$row) { return null; } $blockedUntil = $this->addHours($hours); $attempts = max((int) $row->attempts, 10); return DB::transaction(function () use ($row, $blockedUntil, $attempts) { $row->blocked_until = $blockedUntil; $row->attempts = $attempts; $row->save(); return $row->refresh(); }); } public function unbanOne(?int $id, ?string $ip): ?IpAttempt { $row = $this->findRow($id, $ip); if (!$row) { return null; } return DB::transaction(function () use ($row) { $row->blocked_until = null; $row->attempts = 0; $row->save(); return $row->refresh(); }); } public function unbanAll(): int { return (int) IpAttempt::query() ->where('blocked_until', '>', $this->utcNow()) ->update([ 'blocked_until' => null, 'attempts' => 0, ]); } private function findRow(?int $id, ?string $ip): ?IpAttempt { if ($id && $id > 0) { return IpAttempt::query()->find($id); } if ($ip !== null && $ip !== '') { return IpAttempt::query()->where('ip_address', $ip)->first(); } return null; } private function utcNow(): string { if (function_exists('utc_now')) { return (string) utc_now(); } return now('UTC')->toDateTimeString(); } private function addHours(int $hours): string { $hours = max(1, $hours); if (function_exists('utc_now')) { return (string) date('Y-m-d H:i:s', strtotime("+{$hours} hours", strtotime((string) utc_now()))); } return now('UTC')->addHours($hours)->toDateTimeString(); } }