'Serial Key not provided'], 401); return false; } $serialKey = $matches[1]; if (empty($machineId)) { View::json(['error' => 'Machine ID not provided'], 400); return false; } try { $conn = \App\Config\Database::getInstance()->getConnection(); // Find server by Serial Key and join with client to check both statuses $stmt = $conn->prepare(" SELECT s.id, s.machine_id, s.status, s.ip_v4, s.client_id, c.status as client_status FROM servers s JOIN clients c ON s.client_id = c.id WHERE s.serial_key = :serial "); $stmt->execute(['serial' => $serialKey]); $server = $stmt->fetch(); if (!$server) { $this->log(null, 'auth_failed', "Invalid Serial Key: $serialKey"); View::json(['error' => 'Invalid Serial Key'], 401); return false; } // Get Client IP (Handle Proxy) $clientIp = $_SERVER['HTTP_X_FORWARDED_FOR'] ?? $_SERVER['REMOTE_ADDR'] ?? ''; // If multiple IPs in X-Forwarded-For, take the first one if (strpos($clientIp, ',') !== false) { $clientIp = trim(explode(',', $clientIp)[0]); } // Validate IP if ($server['ip_v4'] !== $clientIp) { $this->log($server['id'], 'auth_blocked', "Invalid IP Address. Expected: {$server['ip_v4']}, Got: $clientIp"); View::json(['error' => 'Invalid IP Address'], 403); return false; } if ($server['status'] !== 'active') { $this->log($server['id'], 'auth_blocked', 'Server is inactive'); View::json(['error' => 'Server is inactive'], 403); return false; } // Check if client is active if ($server['client_status'] !== 'active') { $this->log($server['id'], 'auth_blocked', 'Client is inactive'); View::json(['error' => 'Client is inactive'], 403); return false; } // Bind Machine ID if first use if (empty($server['machine_id'])) { $update = $conn->prepare("UPDATE servers SET machine_id = :mid WHERE id = :id"); $update->execute(['mid' => $machineId, 'id' => $server['id']]); $this->log($server['id'], 'bind_machine', "Machine ID bound: $machineId"); } // Validate Machine ID elseif ($server['machine_id'] !== $machineId) { $this->log($server['id'], 'auth_blocked', "Invalid Machine ID. Expected: {$server['machine_id']}, Got: $machineId"); View::json(['error' => 'Serial Key already bound to another machine'], 403); return false; } // Log Success (Optional: Log every sync might be too much, maybe only on change? // For now, let's log sync to show activity as requested "verificar conexões") $this->log($server['id'], 'sync', 'Agent connected'); // Update Last Seen $updateLastSeen = $conn->prepare("UPDATE servers SET last_seen = NOW() WHERE id = :id"); $updateLastSeen->execute(['id' => $server['id']]); return true; } catch (\Exception $e) { $this->log(null, 'error', 'System error: ' . $e->getMessage()); View::json(['error' => 'Authentication error'], 500); return false; } } private function log($serverId, $action, $message) { try { $conn = \App\Config\Database::getInstance()->getConnection(); $stmt = $conn->prepare("INSERT INTO api_logs (server_id, action, message, ip_address) VALUES (:sid, :act, :msg, :ip)"); // Get Client IP (Handle Proxy) $clientIp = $_SERVER['HTTP_X_FORWARDED_FOR'] ?? $_SERVER['REMOTE_ADDR'] ?? ''; // If multiple IPs in X-Forwarded-For, take the first one if (strpos($clientIp, ',') !== false) { $clientIp = trim(explode(',', $clientIp)[0]); } $stmt->execute([ 'sid' => $serverId, 'act' => $action, 'msg' => $message, 'ip' => $clientIp ]); } catch (\Exception $e) { // Silent fail for logs } } }