ipAttempt = model(IpAttempt::class); } public function index() { $page = max(1, (int) ($this->request->getGet('page') ?? 1)); $perPage = min(100, max(1, (int) ($this->request->getGet('per_page') ?? 20))); $status = strtolower(trim((string) ($this->request->getGet('status') ?? 'all'))); $now = Carbon::now('UTC')->toDateTimeString(); $query = $this->ipAttempt->newQuery()->orderBy('updated_at', 'DESC'); if ($status === 'active') { $query->where('blocked_until', '>', $now); } elseif ($status === 'expired') { $query->where('blocked_until', '<=', $now) ->whereNotNull('blocked_until'); } $result = $this->paginate($query, $page, $perPage); return $this->success($result, 'IP bans retrieved successfully'); } public function show($id = null) { $ban = $this->ipAttempt->find($id); if (!$ban) { return $this->error('IP ban not found', Response::HTTP_NOT_FOUND); } return $this->success($ban, 'IP ban retrieved successfully'); } public function unban() { $payload = $this->payloadData(); if (empty($payload)) { return $this->error('Invalid request data', Response::HTTP_BAD_REQUEST); } $id = (int) ($payload['id'] ?? 0); $ip = trim((string) ($payload['ip'] ?? '')); $all = (int) ($payload['all'] ?? 0) === 1; try { if ($all) { $now = Carbon::now('UTC')->toDateTimeString(); $builder = $this->ipAttempt->newQuery(); $count = $builder->where('blocked_until', '>', $now) ->update([ 'blocked_until' => null, 'attempts' => 0, ]); return $this->success(['count' => $count], $count . ' IP(s) unbanned.'); } $row = null; if ($id > 0) { $row = $this->ipAttempt->find($id); } elseif ($ip !== '') { $row = $this->ipAttempt->newQuery() ->where('ip_address', $ip) ->first(); } if (!$row) { return $this->error('IP record not found', Response::HTTP_NOT_FOUND); } $ipAddress = $row['ip_address'] ?? $row->ip_address ?? ''; $this->ipAttempt->update($row->id ?? $row['id'], [ 'blocked_until' => null, 'attempts' => 0, ]); return $this->success(['ip' => $ipAddress], 'IP ' . $ipAddress . ' unbanned.'); } catch (\Throwable $e) { log_message('error', 'IP unban error: ' . $e->getMessage()); return $this->respondError('Failed to unban IP', Response::HTTP_INTERNAL_SERVER_ERROR); } } public function ban() { $payload = $this->payloadData(); if (empty($payload)) { return $this->error('Invalid request data', Response::HTTP_BAD_REQUEST); } $rules = [ 'ip' => 'required|ip', 'blocked_until' => 'required|date', ]; $errors = $this->validateRequest($payload, $rules); if (!empty($errors)) { return $this->respondValidationError($errors); } try { $ip = trim($payload['ip']); $blockedUntil = Carbon::parse($payload['blocked_until']) ->timezone('UTC') ->format('Y-m-d H:i:s'); $reason = $payload['reason'] ?? null; $attempts = (int) ($payload['attempts'] ?? 5); $existing = $this->ipAttempt->newQuery() ->where('ip_address', $ip) ->first(); if ($existing) { $this->ipAttempt->update($existing->id ?? $existing['id'], [ 'blocked_until' => $blockedUntil, 'attempts' => $attempts, 'reason' => $reason, ]); $ban = $this->ipAttempt->find($existing->id ?? $existing['id']); } else { $ban = $this->ipAttempt->create([ 'ip_address' => $ip, 'blocked_until' => $blockedUntil, 'attempts' => $attempts, 'reason' => $reason, ]); } $responseData = $ban instanceof Model ? $ban->toArray() : $ban; return $this->success($responseData, 'IP banned successfully', Response::HTTP_CREATED); } catch (\Throwable $e) { log_message('error', 'IP ban error: ' . $e->getMessage()); return $this->respondError('Failed to ban IP', Response::HTTP_INTERNAL_SERVER_ERROR); } } /** * POST /api/v1/ip-bans/ban-now * Ban an IP immediately for a specified number of hours */ public function banNow() { $payload = $this->payloadData(); // Accept both POST and GET params $id = (int) ($payload['id'] ?? $this->request->getGet('id') ?? 0); $ip = trim((string) ($payload['ip'] ?? $this->request->getGet('ip') ?? '')); $hours = (int) ($payload['hours'] ?? $this->request->getGet('hours') ?? 24); if ($hours <= 0) { $hours = 24; } try { $row = null; if ($id > 0) { $row = $this->ipAttempt->find($id); } elseif ($ip !== '') { $row = $this->ipAttempt->newQuery() ->where('ip_address', $ip) ->first(); } if (!$row) { return $this->error('IP record not found', Response::HTTP_NOT_FOUND); } $blockedUntil = Carbon::now('UTC')->addHours($hours)->format('Y-m-d H:i:s'); $currentAttempts = (int) ($row['attempts'] ?? $row->attempts ?? 0); $this->ipAttempt->update($row['id'] ?? $row->id, [ 'blocked_until' => $blockedUntil, // Optionally set attempts to threshold for clarity 'attempts' => max($currentAttempts, 10), ]); $ipAddress = $row['ip_address'] ?? $row->ip_address; return $this->success([ 'ip' => $ipAddress, 'blocked_until' => $blockedUntil, 'hours' => $hours, ], "IP {$ipAddress} banned for {$hours}h."); } catch (\Throwable $e) { log_message('error', 'IP ban now error: ' . $e->getMessage()); return $this->respondError('Unable to ban: ' . $e->getMessage(), Response::HTTP_INTERNAL_SERVER_ERROR); } } }