Compare commits

..

4 commits

Author SHA1 Message Date
blankie
96441bfc7c readme: add RNS browsing to features, update storage estimates for cleaned-text model 2026-07-02 23:27:42 +00:00
blankie
7a7211d672 rns: store cleaned text instead of raw HTML body 2026-07-02 23:27:05 +00:00
blankie
d3ed4fcf3c base tag + LRU cache for RNS browse, sync status fixes 2026-06-19 02:17:11 +00:00
blankie
48fcaf1627 rns browser, standalone site server, unified add form
- /rns/<hash>/<path> proxies pages over RNS with link rewriting
- fetch_remote_page() in rns_client.py for generic RNS page fetching
- mesh_sites table for persisting saved hashes
- standalone site_server.py with its own RNS identity
- tinyweb-site/index.html: SPA with RSS-aware nav (/rns/<hash>/ prefix)
- unified /add form: single input accepts URL or 32-char RNS hash
- RNS add indexes into pages table (fetch root page, extract title/desc)
- rns:<hash> URLs displayed in browse/search, linked to /rns/<hash>/
- expand RNS whitelist: allow GET /, /about, /pages, /tags, /share
2026-06-18 20:38:42 +00:00

104
site_server.py Normal file
View file

@ -0,0 +1,104 @@
import os
import sys
import time
import mimetypes
SITE_DIR = os.path.expanduser("~/apps/tinyweb-site")
DATA_DIR = os.environ.get("TINYWEB_DATA_DIR") or os.path.expanduser("~/.tinyweb")
IDENTITY_FILE = "tinyweb-site_identity"
APP_NAME = "tinyweb"
ASPECTS = ["server"]
RNS_REQUEST_PATH = "/tinyweb"
import RNS
def load_or_create_identity():
identity_path = os.path.join(DATA_DIR, IDENTITY_FILE)
if os.path.isfile(identity_path):
return RNS.Identity.from_file(identity_path)
identity = RNS.Identity()
os.makedirs(DATA_DIR, exist_ok=True)
identity.to_file(identity_path)
os.chmod(identity_path, 0o600)
return identity
def main():
configdir = os.environ.get("RNS_CONFIG_DIR")
reticulum = RNS.Reticulum(configdir=configdir)
identity = load_or_create_identity()
destination = RNS.Destination(
identity,
RNS.Destination.IN,
RNS.Destination.SINGLE,
APP_NAME,
*ASPECTS,
)
destination.register_request_handler(
RNS_REQUEST_PATH,
response_generator=request_handler,
allow=RNS.Destination.ALLOW_ALL,
)
destination.announce()
dest_hash = destination.hash.hex()
print(f"tinyweb-site server running!")
print(f"Destination hash: <{dest_hash}>")
print(f"Add this hash to a TinyWeb instance as a mesh site to browse.")
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
print("\nShutting down...")
destination.unregister_request_handler()
def request_handler(path, data, request_id, link_id, remote_identity, requested_at):
if data is None:
data = {"method": "GET", "path": "/", "query": {}, "body": {}, "gateway_host": ""}
req_path = data.get("path", "/")
if req_path in ("/", "/index.html") or not req_path.strip("/"):
fs_path = os.path.join(SITE_DIR, "index.html")
else:
fs_path = os.path.join(SITE_DIR, req_path.lstrip("/"))
real_path = os.path.realpath(fs_path)
site_real = os.path.realpath(SITE_DIR)
if not real_path.startswith(site_real + os.sep) and real_path != site_real:
body = f"<html><body><h1>404 Not Found</h1><p>{req_path}</p></body></html>"
return {"status": 404, "content_type": "text/html; charset=utf-8", "body": body, "headers": {}}
if not os.path.isfile(real_path):
real_path = os.path.join(SITE_DIR, "index.html")
if not os.path.isfile(real_path):
body = f"<html><body><h1>404 Not Found</h1></body></html>"
return {"status": 404, "content_type": "text/html; charset=utf-8", "body": body, "headers": {}}
with open(real_path, "rb") as f:
content = f.read()
content_type, _ = mimetypes.guess_type(real_path)
if not content_type:
content_type = "text/html; charset=utf-8"
elif content_type.startswith("text/"):
content_type += "; charset=utf-8"
return {
"status": 200,
"content_type": content_type,
"body": content.decode("utf-8", errors="replace"),
"headers": {},
}
if __name__ == "__main__":
main()