extractHost($this->tcpPort->site->url); if (!$host) { Log::error('CheckTcpPort: Invalid URL', [ 'tcp_port_id' => $this->tcpPort->id, 'site_id' => $this->tcpPort->site_id, 'url' => $this->tcpPort->site->url, ]); $this->tcpPort->update([ 'is_open' => false, 'response_time_ms' => null, 'last_checked_at' => now(), ]); return; } $result = $this->checkPort($host, $this->tcpPort->port); $wasOpen = $this->tcpPort->is_open; $this->tcpPort->update([ 'is_open' => $result['is_open'], 'response_time_ms' => $result['response_time'], 'last_checked_at' => now(), ]); Log::info('CheckTcpPort: Check completed', [ 'tcp_port_id' => $this->tcpPort->id, 'site_id' => $this->tcpPort->site_id, 'host' => $host, 'port' => $this->tcpPort->port, 'is_open' => $result['is_open'], 'response_time_ms' => $result['response_time'], ]); // Send notification if port status changed from open to closed if ($wasOpen === true && $result['is_open'] === false) { $this->tcpPort->site->user->notify(new TcpPortAlert( $this->tcpPort, 'closed' )); Log::warning('CheckTcpPort: Port closed alert sent', [ 'tcp_port_id' => $this->tcpPort->id, 'port' => $this->tcpPort->port, ]); } // Optionally send notification if port opened if ($wasOpen === false && $result['is_open'] === true) { $this->tcpPort->site->user->notify(new TcpPortAlert( $this->tcpPort, 'opened' )); Log::info('CheckTcpPort: Port opened alert sent', [ 'tcp_port_id' => $this->tcpPort->id, 'port' => $this->tcpPort->port, ]); } } catch (\Exception $e) { Log::error('CheckTcpPort: Failed', [ 'tcp_port_id' => $this->tcpPort->id, 'error' => $e->getMessage(), 'trace' => $e->getTraceAsString(), ]); $this->tcpPort->update([ 'is_open' => false, 'response_time_ms' => null, 'last_checked_at' => now(), ]); throw $e; } } /** * Extract host from URL. */ protected function extractHost(string $url): ?string { $parsed = parse_url($url); return $parsed['host'] ?? null; } /** * Check if a TCP port is open. */ protected function checkPort(string $host, int $port): array { $timeout = 5; $startTime = microtime(true); $errno = 0; $errstr = ''; // Suppress warnings and capture errors $fp = @fsockopen($host, $port, $errno, $errstr, $timeout); $endTime = microtime(true); $responseTime = (int) round(($endTime - $startTime) * 1000); $isOpen = $fp !== false; if ($fp) { fclose($fp); } if (!$isOpen) { Log::debug('CheckTcpPort: Port check failed', [ 'host' => $host, 'port' => $port, 'errno' => $errno, 'errstr' => $errstr, 'response_time_ms' => $responseTime, ]); } return [ 'is_open' => $isOpen, 'response_time' => $isOpen ? $responseTime : null, 'error_code' => $errno, 'error_message' => $errstr, ]; } /** * Handle a job failure. */ public function failed(\Throwable $exception): void { Log::error('CheckTcpPort: Job failed after all retries', [ 'tcp_port_id' => $this->tcpPort->id, 'exception' => $exception->getMessage(), ]); } }