- Subscriptions now use Reticulum destination hashes instead of HTTP URLs - All subscription syncing happens over encrypted RNS links (rns_client.py) - Add remote_pages table for synced content from subscriptions - Search results now include pages from synced subscriptions, grouped by source - Remove HTTP dependency from subscription handlers
78 lines
2.4 KiB
Python
78 lines
2.4 KiB
Python
import time
|
|
import RNS
|
|
|
|
APP_NAME = "tinyweb"
|
|
ASPECTS = ["server"]
|
|
REQUEST_TIMEOUT = 30
|
|
|
|
|
|
def fetch_remote_sites(dest_hash_hex):
|
|
"""
|
|
Connect to a remote TinyWeb instance over Reticulum and fetch its
|
|
shared sites. Returns the response dict from /api/sites, or raises
|
|
an exception on failure.
|
|
"""
|
|
dest_hash = bytes.fromhex(dest_hash_hex)
|
|
|
|
# Resolve path if needed
|
|
if not RNS.Transport.has_path(dest_hash):
|
|
RNS.Transport.request_path(dest_hash)
|
|
elapsed = 0
|
|
while not RNS.Transport.has_path(dest_hash) and elapsed < 15:
|
|
time.sleep(0.5)
|
|
elapsed += 0.5
|
|
if not RNS.Transport.has_path(dest_hash):
|
|
raise ConnectionError(f"Could not find path to {dest_hash_hex}")
|
|
|
|
server_identity = RNS.Identity.recall(dest_hash)
|
|
if server_identity is None:
|
|
raise ConnectionError(f"Could not recall identity for {dest_hash_hex}")
|
|
|
|
destination = RNS.Destination(
|
|
server_identity,
|
|
RNS.Destination.OUT,
|
|
RNS.Destination.SINGLE,
|
|
APP_NAME,
|
|
*ASPECTS,
|
|
)
|
|
|
|
# Establish link
|
|
link = RNS.Link(destination)
|
|
elapsed = 0
|
|
while link.status == RNS.Link.PENDING and elapsed < 15:
|
|
time.sleep(0.25)
|
|
elapsed += 0.25
|
|
|
|
if link.status != RNS.Link.ACTIVE:
|
|
raise ConnectionError(f"Could not establish link to {dest_hash_hex}")
|
|
|
|
try:
|
|
# Request /api/sites
|
|
request_data = {
|
|
"method": "GET",
|
|
"path": "/api/sites",
|
|
"query": {},
|
|
"body": {},
|
|
"gateway_host": "",
|
|
}
|
|
|
|
receipt = link.request("/tinyweb", data=request_data, timeout=REQUEST_TIMEOUT)
|
|
|
|
elapsed = 0
|
|
done = (RNS.RequestReceipt.READY, RNS.RequestReceipt.DELIVERED, RNS.RequestReceipt.FAILED)
|
|
while receipt.get_status() not in done and elapsed < REQUEST_TIMEOUT:
|
|
time.sleep(0.5)
|
|
elapsed += 0.5
|
|
|
|
if receipt.get_status() in (RNS.RequestReceipt.READY, RNS.RequestReceipt.DELIVERED):
|
|
resp = receipt.get_response()
|
|
if resp["status"] == 403:
|
|
raise PermissionError("That instance has sharing disabled.")
|
|
if resp["status"] != 200:
|
|
raise ConnectionError(f"Remote returned status {resp['status']}")
|
|
import json
|
|
return json.loads(resp["body"])
|
|
else:
|
|
raise ConnectionError(f"Request failed or timed out")
|
|
finally:
|
|
link.teardown()
|