Initial implementation of IPFS CLI tool

This commit is contained in:
2026-03-15 13:11:57 +03:00
commit 4d27ceb117
6 changed files with 178 additions and 0 deletions
+3
View File
@@ -0,0 +1,3 @@
IPFS_API_URL=http://127.0.0.1:5001
# Optional:
# IPFS_API_TOKEN=token_here
+23
View File
@@ -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/
+57
View File
@@ -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
```
View File
+93
View File
@@ -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())
+2
View File
@@ -0,0 +1,2 @@
python-dotenv>=1.0.0
requests>=2.32.0