Some checks failed
CLI Tests / test-cli (push) Has started running
Cross-Node Transaction Testing / transaction-test (push) Successful in 2s
Deploy to Testnet / deploy-testnet (push) Successful in 1m9s
Documentation Validation / validate-docs (push) Failing after 9s
Documentation Validation / validate-policies-strict (push) Successful in 4s
Multi-Node Stress Testing / stress-test (push) Successful in 3s
Node Failover Simulation / failover-test (push) Successful in 2s
Security Scanning / security-scan (push) Successful in 29s
- Update all command module docstrings to consistent format ending with "for AITBC CLI" - Remove unused imports (json, httpx, pydantic) from multiple command modules - Reorder imports to group utils imports together - Change sys.path in click_cli.py from /opt/aitbc to /opt/aitbc/cli - Fix relative imports in cross_chain.py and ipfs.py to use .config
172 lines
5.0 KiB
Python
172 lines
5.0 KiB
Python
"""IPFS storage and retrieval commands for AITBC CLI"""
|
|
|
|
import click
|
|
from .config import get_config
|
|
from utils import output, error, success, warning
|
|
from pathlib import Path
|
|
from typing import Optional
|
|
|
|
@click.group()
|
|
def ipfs():
|
|
"""IPFS distributed storage commands"""
|
|
pass
|
|
|
|
@ipfs.command()
|
|
@click.option("--file", type=click.Path(exists=True), required=True, help="File to upload")
|
|
@click.option("--pin", is_flag=True, default=False, help="Pin the content")
|
|
@click.option("--name", help="Optional name for the content")
|
|
def upload(file: str, pin: bool, name: Optional[str]):
|
|
"""Upload file to IPFS"""
|
|
try:
|
|
file_path = Path(file)
|
|
|
|
# For demo purposes, generate a pseudo CID
|
|
# In production, this would call actual IPFS service
|
|
import hashlib
|
|
with open(file_path, 'rb') as f:
|
|
data = f.read()
|
|
|
|
# Generate pseudo CID based on file hash
|
|
file_hash = hashlib.sha256(data).hexdigest()
|
|
cid = f"Qm{file_hash[:44]}"
|
|
|
|
# Store in local demo storage
|
|
storage_dir = Path.home() / ".aitbc"
|
|
storage_dir.mkdir(parents=True, exist_ok=True)
|
|
|
|
ipfs_data = {
|
|
"cid": cid,
|
|
"name": name or file_path.name,
|
|
"size": len(data),
|
|
"pinned": pin,
|
|
"file_path": str(file_path),
|
|
"timestamp": str(Path(file_path).stat().st_mtime)
|
|
}
|
|
|
|
ipfs_file = storage_dir / "ipfs_storage.json"
|
|
ipfs_storage = {}
|
|
if ipfs_file.exists():
|
|
with open(ipfs_file, 'r') as f:
|
|
ipfs_storage = json.load(f)
|
|
|
|
ipfs_storage[cid] = ipfs_data
|
|
|
|
with open(ipfs_file, 'w') as f:
|
|
json.dump(ipfs_storage, f, indent=2)
|
|
|
|
success(f"File uploaded to IPFS")
|
|
output({
|
|
"cid": cid,
|
|
"name": ipfs_data["name"],
|
|
"size": ipfs_data["size"],
|
|
"pinned": pin
|
|
})
|
|
|
|
except Exception as e:
|
|
error(f"Failed to upload file: {e}")
|
|
|
|
@ipfs.command()
|
|
@click.argument("cid")
|
|
@click.option("--output", type=click.Path(), help="Output file path")
|
|
def download(cid: str, output: Optional[str]):
|
|
"""Download file from IPFS by CID"""
|
|
try:
|
|
storage_dir = Path.home() / ".aitbc"
|
|
ipfs_file = storage_dir / "ipfs_storage.json"
|
|
|
|
if not ipfs_file.exists():
|
|
error("IPFS storage not found")
|
|
return
|
|
|
|
with open(ipfs_file, 'r') as f:
|
|
ipfs_storage = json.load(f)
|
|
|
|
if cid not in ipfs_storage:
|
|
error(f"CID {cid} not found in local storage")
|
|
return
|
|
|
|
ipfs_data = ipfs_storage[cid]
|
|
file_path = Path(ipfs_data["file_path"])
|
|
|
|
if not file_path.exists():
|
|
error(f"Original file {file_path} no longer exists")
|
|
return
|
|
|
|
# Copy to output path if specified
|
|
if output:
|
|
import shutil
|
|
shutil.copy(file_path, output)
|
|
success(f"File downloaded to {output}")
|
|
else:
|
|
success(f"File retrieved from {file_path}")
|
|
|
|
output({
|
|
"cid": cid,
|
|
"name": ipfs_data["name"],
|
|
"size": ipfs_data["size"],
|
|
"file_path": str(file_path)
|
|
})
|
|
|
|
except Exception as e:
|
|
error(f"Failed to download file: {e}")
|
|
|
|
@ipfs.command()
|
|
@click.argument("cid")
|
|
def pin(cid: str):
|
|
"""Pin content to IPFS"""
|
|
try:
|
|
storage_dir = Path.home() / ".aitbc"
|
|
ipfs_file = storage_dir / "ipfs_storage.json"
|
|
|
|
if not ipfs_file.exists():
|
|
error("IPFS storage not found")
|
|
return
|
|
|
|
with open(ipfs_file, 'r') as f:
|
|
ipfs_storage = json.load(f)
|
|
|
|
if cid not in ipfs_storage:
|
|
error(f"CID {cid} not found in local storage")
|
|
return
|
|
|
|
ipfs_storage[cid]["pinned"] = True
|
|
|
|
with open(ipfs_file, 'w') as f:
|
|
json.dump(ipfs_storage, f, indent=2)
|
|
|
|
success(f"CID {cid} pinned")
|
|
output({"cid": cid, "pinned": True})
|
|
|
|
except Exception as e:
|
|
error(f"Failed to pin CID: {e}")
|
|
|
|
@ipfs.command()
|
|
def list():
|
|
"""List all stored IPFS content"""
|
|
try:
|
|
storage_dir = Path.home() / ".aitbc"
|
|
ipfs_file = storage_dir / "ipfs_storage.json"
|
|
|
|
if not ipfs_file.exists():
|
|
warning("No IPFS storage found")
|
|
return
|
|
|
|
with open(ipfs_file, 'r') as f:
|
|
ipfs_storage = json.load(f)
|
|
|
|
output({
|
|
"total": len(ipfs_storage),
|
|
"items": [
|
|
{
|
|
"cid": cid,
|
|
"name": data["name"],
|
|
"size": data["size"],
|
|
"pinned": data["pinned"]
|
|
}
|
|
for cid, data in ipfs_storage.items()
|
|
]
|
|
})
|
|
|
|
except Exception as e:
|
|
error(f"Failed to list IPFS content: {e}")
|