|null */ public function findByTypeAndName(string $type, string $name): ?array { try { $statement = $this->pdo->prepare( 'SELECT * FROM integrations WHERE type = :type AND name = :name LIMIT 1' ); $statement->execute([ 'type' => trim($type), 'name' => trim($name), ]); $row = $statement->fetch(PDO::FETCH_ASSOC); } catch (Throwable) { return null; } return is_array($row) ? $row : null; } /** * @return array|null */ public function findFirstByType(string $type): ?array { try { $statement = $this->pdo->prepare( 'SELECT * FROM integrations WHERE type = :type ORDER BY id ASC LIMIT 1' ); $statement->execute(['type' => trim($type)]); $row = $statement->fetch(PDO::FETCH_ASSOC); } catch (Throwable) { return null; } return is_array($row) ? $row : null; } /** * @return array|null */ public function findById(int $id): ?array { if ($id <= 0) { return null; } try { $statement = $this->pdo->prepare('SELECT * FROM integrations WHERE id = :id LIMIT 1'); $statement->execute(['id' => $id]); $row = $statement->fetch(PDO::FETCH_ASSOC); } catch (Throwable) { return null; } return is_array($row) ? $row : null; } public function ensureIntegration( string $type, string $name, string $baseUrl, int $timeoutSeconds = 10, bool $isActive = true ): int { $typeValue = trim($type); $nameValue = trim($name); $baseUrlValue = trim($baseUrl); $existing = $this->findByTypeAndName($typeValue, $nameValue); if ($existing !== null) { return (int) ($existing['id'] ?? 0); } $statement = $this->pdo->prepare( 'INSERT INTO integrations ( type, name, base_url, timeout_seconds, is_active, created_at, updated_at ) VALUES ( :type, :name, :base_url, :timeout_seconds, :is_active, NOW(), NOW() )' ); $statement->execute([ 'type' => $typeValue, 'name' => $nameValue, 'base_url' => $baseUrlValue, 'timeout_seconds' => max(1, $timeoutSeconds), 'is_active' => $isActive ? 1 : 0, ]); return (int) $this->pdo->lastInsertId(); } public function updateApiKeyEncrypted(int $integrationId, ?string $encrypted): void { if ($integrationId <= 0) { return; } $statement = $this->pdo->prepare( 'UPDATE integrations SET api_key_encrypted = :api_key_encrypted, updated_at = NOW() WHERE id = :id' ); $statement->execute([ 'id' => $integrationId, 'api_key_encrypted' => StringHelper::nullableString((string) $encrypted), ]); } public function updateTestResult(int $integrationId, string $status, ?int $httpCode, string $message): void { if ($integrationId <= 0) { return; } $statement = $this->pdo->prepare( 'UPDATE integrations SET last_test_status = :status, last_test_http_code = :http_code, last_test_message = :message, last_test_at = NOW(), updated_at = NOW() WHERE id = :id' ); $statement->execute([ 'id' => $integrationId, 'status' => substr($status, 0, 16), 'http_code' => $httpCode, 'message' => substr($message, 0, 255), ]); } public function getApiKeyEncrypted(int $integrationId): ?string { if ($integrationId <= 0) { return null; } try { $statement = $this->pdo->prepare( 'SELECT api_key_encrypted FROM integrations WHERE id = :id LIMIT 1' ); $statement->execute(['id' => $integrationId]); $value = $statement->fetchColumn(); } catch (Throwable) { return null; } if (!is_string($value)) { return null; } $trimmed = trim($value); return $trimmed === '' ? null : $trimmed; } }