#!/usr/bin/env python3 """Simple ZeroTier helper for joining networks and discovering peers. Usage: python scripts/zerotier_setup.py --join python scripts/zerotier_setup.py --list python scripts/zerotier_setup.py --discover This is a convenience tool to exercise the API/zerotier.py functionality while prototyping and bringing up remote peers for store testing. """ from __future__ import annotations import argparse import json import sys from typing import Any from pathlib import Path from SYS.logger import log, debug try: from API import zerotier except Exception: zerotier = None def main(argv=None): parser = argparse.ArgumentParser(description="ZeroTier helper for Medios-Macina") parser.add_argument("--list", action="store_true", help="List local ZeroTier networks") parser.add_argument("--join", type=str, help="Join a ZeroTier network by ID") parser.add_argument("--leave", type=str, help="Leave a ZeroTier network by ID") parser.add_argument("--discover", type=str, help="Discover services on a ZeroTier network ID") parser.add_argument("--upload", type=str, help="Upload a file to a discovered 'remote' service on this ZeroTier network ID") parser.add_argument("--file", type=str, help="Local file to upload (used with --upload)") parser.add_argument("--tag", action="append", help="Tag to attach (repeatable)", default=[]) parser.add_argument("--url", action="append", help="URL to associate (repeatable)", default=[]) parser.add_argument("--api-key", type=str, help="API key to use for uploads (optional)") parser.add_argument("--json", action="store_true", help="Output JSON when appropriate") args = parser.parse_args(argv) if zerotier is None: log("ZeroTier API module not available; ensure API/zerotier.py is importable and zerotier or zerotier-cli is installed") return 1 if args.list: nets = zerotier.list_networks() if args.json: print(json.dumps([n.__dict__ for n in nets], indent=2)) else: for n in nets: print(f"{n.id}\t{name:=}{n.name}\t{n.status}\t{n.assigned_addresses}") return 0 if args.join: try: ok = zerotier.join_network(args.join) print("Joined" if ok else "Failed to join") return 0 if ok else 2 except Exception as exc: log(f"Join failed: {exc}") print(f"Join failed: {exc}") return 2 if args.leave: try: ok = zerotier.leave_network(args.leave) print("Left" if ok else "Failed to leave") return 0 if ok else 2 except Exception as exc: log(f"Leave failed: {exc}") print(f"Leave failed: {exc}") return 2 if args.discover: probes = zerotier.discover_services_on_network(args.discover) if args.json: print(json.dumps([p.__dict__ for p in probes], indent=2, default=str)) else: for p in probes: print(f"{p.address}:{p.port}{p.path} -> status={p.status_code} hint={p.service_hint}") return 0 if args.upload: # Upload a file to the first discovered remote service on the network if not args.file: print("ERROR: --file is required for --upload") return 2 probe = zerotier.find_peer_service(args.upload, service_hint="remote") if not probe: print("No remote service found on network") return 2 base = f"http://{probe.address}:{probe.port}" try: import httpx url = base.rstrip("/") + "/files/upload" headers = {} if args.api_key: headers["X-API-Key"] = args.api_key with open(args.file, "rb") as fh: files = {"file": (Path(args.file).name, fh)} data = [] for t in (args.tag or []): data.append(("tag", t)) for u in (args.url or []): data.append(("url", u)) resp = httpx.post(url, files=files, data=data, headers=headers, timeout=30) print(resp.status_code, resp.text) return 0 if resp.status_code in (200, 201) else 2 except Exception: import requests url = base.rstrip("/") + "/files/upload" headers = {} if args.api_key: headers["X-API-Key"] = args.api_key with open(args.file, "rb") as fh: files = {"file": (Path(args.file).name, fh)} data = [] for t in (args.tag or []): data.append(("tag", t)) for u in (args.url or []): data.append(("url", u)) resp = requests.post(url, files=files, data=data, headers=headers, timeout=30) print(resp.status_code, resp.text) return 0 if resp.status_code in (200, 201) else 2 parser.print_help() return 0 if __name__ == "__main__": sys.exit(main())