Initial implementation of IPFS CLI tool
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
IPFS_API_URL=http://127.0.0.1:5001
|
||||
# Optional:
|
||||
# IPFS_API_TOKEN=token_here
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
# Python
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*.pyo
|
||||
*.pyd
|
||||
.Python
|
||||
|
||||
# Virtual environments
|
||||
.venv/
|
||||
venv/
|
||||
env/
|
||||
|
||||
# Environment variables
|
||||
.env
|
||||
|
||||
# OS-specific
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# IDE / Editor
|
||||
.vscode/
|
||||
.idea/
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
### IPFS CLI (Python)
|
||||
|
||||
Простая утилита для загрузки файлов в IPFS и скачивания по CID через HTTP API Kubo (`http://127.0.0.1:5001` по умолчанию).
|
||||
|
||||
### Установка
|
||||
|
||||
```bash
|
||||
python -m venv .venv
|
||||
|
||||
# Windows PowerShell
|
||||
.\.venv\Scripts\Activate.ps1
|
||||
|
||||
# Linux / macOS / WSL
|
||||
source .venv/bin/activate
|
||||
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
|
||||
### Конфигурация
|
||||
|
||||
Создайте файл `.env` в корне проекта (можно скопировать из `.env.example`):
|
||||
|
||||
|
||||
```bash
|
||||
cp .env.example .env
|
||||
```
|
||||
|
||||
По умолчанию используется:
|
||||
|
||||
```text
|
||||
IPFS_API_URL = http://127.0.0.1:5001
|
||||
IPFS_API_TOKEN = (пусто)
|
||||
```
|
||||
|
||||
Перед работой IPFS‑узел должен быть запущен:
|
||||
|
||||
```bash
|
||||
ipfs init # один раз
|
||||
ipfs daemon # при каждом запуске
|
||||
```
|
||||
|
||||
|
||||
### Использование
|
||||
|
||||
Загрузка файла:
|
||||
|
||||
```bash
|
||||
python ipfs_cli.py upload path/to/file.txt
|
||||
# вывод: Qm... (CID)
|
||||
```
|
||||
|
||||
Скачивание файла по CID (сохранится в `./download/<CID>.bin`):
|
||||
|
||||
```bash
|
||||
python ipfs_cli.py download QmXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
||||
```
|
||||
+93
@@ -0,0 +1,93 @@
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import requests
|
||||
|
||||
API_URL = os.getenv("IPFS_API_URL", "http://127.0.0.1:5001").rstrip("/")
|
||||
TOKEN = os.getenv("IPFS_API_TOKEN")
|
||||
|
||||
|
||||
def _auth_headers() -> dict[str, str]:
|
||||
return {"Authorization": f"Bearer {TOKEN}"} if TOKEN else {}
|
||||
|
||||
|
||||
def upload_file(path: Path) -> str:
|
||||
if not path.is_file():
|
||||
raise FileNotFoundError(f"Файл не найден: {path}")
|
||||
|
||||
url = f"{API_URL}/api/v0/add"
|
||||
with path.open("rb") as f:
|
||||
files = {"file": (path.name, f)}
|
||||
r = requests.post(url, headers=_auth_headers(), files=files, timeout=120)
|
||||
|
||||
if r.status_code != 200:
|
||||
raise RuntimeError(f"Ошибка загрузки (status={r.status_code}): {r.text}")
|
||||
|
||||
data = r.json()
|
||||
cid = data.get("Hash")
|
||||
if not cid:
|
||||
raise RuntimeError(f"Не удалось получить CID из ответа: {data}")
|
||||
return cid
|
||||
|
||||
|
||||
def download_file(cid: str) -> Path:
|
||||
if not cid:
|
||||
raise ValueError("CID пустой")
|
||||
|
||||
download_dir = Path("download")
|
||||
download_dir.mkdir(parents=True, exist_ok=True)
|
||||
out_path = download_dir / f"{cid}.bin"
|
||||
|
||||
url = f"{API_URL}/api/v0/cat"
|
||||
params = {"arg": cid}
|
||||
|
||||
with requests.post(
|
||||
url,
|
||||
headers=_auth_headers(),
|
||||
params=params,
|
||||
stream=True,
|
||||
timeout=120,
|
||||
) as r:
|
||||
if r.status_code != 200:
|
||||
raise RuntimeError(
|
||||
f"Ошибка скачивания (status={r.status_code}): {r.text}"
|
||||
)
|
||||
with out_path.open("wb") as f:
|
||||
for chunk in r.iter_content(chunk_size=8192):
|
||||
if chunk:
|
||||
f.write(chunk)
|
||||
|
||||
return out_path
|
||||
|
||||
|
||||
def main(argv: list[str] | None = None) -> int:
|
||||
parser = argparse.ArgumentParser(add_help=False)
|
||||
sub = parser.add_subparsers(dest="command", required=True)
|
||||
|
||||
up = sub.add_parser("upload", add_help=False)
|
||||
up.add_argument("file")
|
||||
|
||||
dl = sub.add_parser("download", add_help=False)
|
||||
dl.add_argument("cid")
|
||||
|
||||
args = parser.parse_args(argv)
|
||||
|
||||
try:
|
||||
if args.command == "upload":
|
||||
cid = upload_file(Path(args.file))
|
||||
print(cid)
|
||||
elif args.command == "download":
|
||||
out = download_file(args.cid)
|
||||
print(out)
|
||||
else:
|
||||
raise ValueError("Неизвестная команда")
|
||||
return 0
|
||||
except Exception as exc:
|
||||
print(f"Ошибка: {exc}", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@@ -0,0 +1,2 @@
|
||||
python-dotenv>=1.0.0
|
||||
requests>=2.32.0
|
||||
Reference in New Issue
Block a user