diff --git a/ipfs_client.py b/ipfs_client.py index fd46890..6d3117f 100644 --- a/ipfs_client.py +++ b/ipfs_client.py @@ -3,14 +3,15 @@ IPFS client for Art DAG L2 server. Provides functions to fetch and pin content from IPFS. -L2 uses IPFS to retrieve content from the federated network. +Uses direct HTTP API calls for compatibility with all Kubo versions. """ import logging import os +import re from typing import Optional -import ipfshttpclient +import requests logger = logging.getLogger(__name__) @@ -21,9 +22,26 @@ IPFS_API = os.getenv("IPFS_API", "/ip4/127.0.0.1/tcp/5001") IPFS_TIMEOUT = int(os.getenv("IPFS_TIMEOUT", "60")) -def get_client(): - """Get an IPFS client connection.""" - return ipfshttpclient.connect(IPFS_API, timeout=IPFS_TIMEOUT) +def _multiaddr_to_url(multiaddr: str) -> str: + """Convert IPFS multiaddr to HTTP URL.""" + # Handle /dns/hostname/tcp/port format + dns_match = re.match(r"/dns[46]?/([^/]+)/tcp/(\d+)", multiaddr) + if dns_match: + return f"http://{dns_match.group(1)}:{dns_match.group(2)}" + + # Handle /ip4/address/tcp/port format + ip4_match = re.match(r"/ip4/([^/]+)/tcp/(\d+)", multiaddr) + if ip4_match: + return f"http://{ip4_match.group(1)}:{ip4_match.group(2)}" + + # Fallback: assume it's already a URL or use default + if multiaddr.startswith("http"): + return multiaddr + return "http://127.0.0.1:5001" + + +# Base URL for IPFS API +IPFS_BASE_URL = _multiaddr_to_url(IPFS_API) def get_bytes(cid: str) -> Optional[bytes]: @@ -37,10 +55,15 @@ def get_bytes(cid: str) -> Optional[bytes]: Content as bytes or None on failure """ try: - with get_client() as client: - data = client.cat(cid) - logger.info(f"Retrieved from IPFS: {cid} ({len(data)} bytes)") - return data + url = f"{IPFS_BASE_URL}/api/v0/cat" + params = {"arg": cid} + + response = requests.post(url, params=params, timeout=IPFS_TIMEOUT) + response.raise_for_status() + data = response.content + + logger.info(f"Retrieved from IPFS: {cid} ({len(data)} bytes)") + return data except Exception as e: logger.error(f"Failed to get from IPFS: {e}") return None @@ -57,10 +80,14 @@ def pin(cid: str) -> bool: True on success, False on failure """ try: - with get_client() as client: - client.pin.add(cid) - logger.info(f"Pinned on IPFS: {cid}") - return True + url = f"{IPFS_BASE_URL}/api/v0/pin/add" + params = {"arg": cid} + + response = requests.post(url, params=params, timeout=IPFS_TIMEOUT) + response.raise_for_status() + + logger.info(f"Pinned on IPFS: {cid}") + return True except Exception as e: logger.error(f"Failed to pin on IPFS: {e}") return False @@ -77,10 +104,14 @@ def unpin(cid: str) -> bool: True on success, False on failure """ try: - with get_client() as client: - client.pin.rm(cid) - logger.info(f"Unpinned from IPFS: {cid}") - return True + url = f"{IPFS_BASE_URL}/api/v0/pin/rm" + params = {"arg": cid} + + response = requests.post(url, params=params, timeout=IPFS_TIMEOUT) + response.raise_for_status() + + logger.info(f"Unpinned from IPFS: {cid}") + return True except Exception as e: logger.error(f"Failed to unpin from IPFS: {e}") return False @@ -94,9 +125,9 @@ def is_available() -> bool: True if IPFS is available, False otherwise """ try: - with get_client() as client: - client.id() - return True + url = f"{IPFS_BASE_URL}/api/v0/id" + response = requests.post(url, timeout=5) + return response.status_code == 200 except Exception: return False @@ -109,9 +140,10 @@ def get_node_id() -> Optional[str]: Peer ID string or None on failure """ try: - with get_client() as client: - info = client.id() - return info.get("ID") + url = f"{IPFS_BASE_URL}/api/v0/id" + response = requests.post(url, timeout=IPFS_TIMEOUT) + response.raise_for_status() + return response.json().get("ID") except Exception as e: logger.error(f"Failed to get node ID: {e}") return None diff --git a/requirements.txt b/requirements.txt index 16e3a47..faa8463 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,4 +7,3 @@ python-jose[cryptography]>=3.3.0 markdown>=3.5.0 python-multipart>=0.0.6 asyncpg>=0.29.0 -ipfshttpclient>=0.7.0