single-command startup

app.py now auto-starts the gateway HTTP server in a daemon thread,
so users only need `python app.py` to get everything running. The
gateway calls dispatch_request directly when co-located (local mode)
instead of trying to establish an RNS link to itself. Bookmarklet
hardcoded to localhost:8080. gateway.py still works standalone for
connecting to remote instances.
This commit is contained in:
lichenblankie 2026-03-25 23:01:54 -07:00
parent 7ccaf93404
commit c5d8d350a6
3 changed files with 52 additions and 34 deletions

18
app.py
View file

@ -1,9 +1,12 @@
import os import os
import time import time
import threading
import RNS import RNS
from http.server import HTTPServer
from db import init_db from db import init_db
from handlers import dispatch_request from handlers import dispatch_request
from gateway import GatewayState, GatewayHandler, GATEWAY_PORT
APP_NAME = "tinyweb" APP_NAME = "tinyweb"
ASPECTS = ["server"] ASPECTS = ["server"]
@ -24,6 +27,14 @@ def rns_request_handler(path, data, request_id, link_id, remote_identity, reques
return dispatch_request(data) return dispatch_request(data)
def start_gateway(reticulum):
GatewayState.reticulum = reticulum
GatewayState.local_dispatch = dispatch_request
server = HTTPServer(("127.0.0.1", GATEWAY_PORT), GatewayHandler)
thread = threading.Thread(target=server.serve_forever, daemon=True)
thread.start()
def main(): def main():
init_db() init_db()
reticulum = RNS.Reticulum() reticulum = RNS.Reticulum()
@ -44,10 +55,11 @@ def main():
) )
destination.announce() destination.announce()
start_gateway(reticulum)
print(f"TinyWeb Reticulum server running") print(f"TinyWeb running!")
print(f"Destination hash: {RNS.prettyhexrep(destination.hash)}") print(f"Open http://localhost:{GATEWAY_PORT} in your browser")
print(f"Share this hash with clients to connect via gateway.py") print(f"Destination hash: {RNS.prettyhexrep(destination.hash)} (share this so friends can subscribe)")
while True: while True:
time.sleep(1) time.sleep(1)

View file

@ -16,6 +16,7 @@ class GatewayState:
destination = None destination = None
link = None link = None
link_lock = threading.Lock() link_lock = threading.Lock()
local_dispatch = None # set when running inside app.py
def resolve_destination(dest_hash_hex): def resolve_destination(dest_hash_hex):
@ -83,6 +84,9 @@ class GatewayHandler(BaseHTTPRequestHandler):
} }
try: try:
if GatewayState.local_dispatch:
resp = GatewayState.local_dispatch(request_data)
else:
link = ensure_link() link = ensure_link()
receipt = link.request( receipt = link.request(
"/tinyweb", "/tinyweb",
@ -99,6 +103,13 @@ class GatewayHandler(BaseHTTPRequestHandler):
if receipt.get_status() in (RNS.RequestReceipt.READY, RNS.RequestReceipt.DELIVERED): if receipt.get_status() in (RNS.RequestReceipt.READY, RNS.RequestReceipt.DELIVERED):
resp = receipt.get_response() resp = receipt.get_response()
elif receipt.get_status() == RNS.RequestReceipt.FAILED:
self.send_error(504, "Request to TinyWeb server failed")
return
else:
self.send_error(504, "Request to TinyWeb server timed out")
return
self.send_response(resp["status"]) self.send_response(resp["status"])
self.send_header("Content-Type", resp.get("content_type", "text/html; charset=utf-8")) self.send_header("Content-Type", resp.get("content_type", "text/html; charset=utf-8"))
for k, v in resp.get("headers", {}).items(): for k, v in resp.get("headers", {}).items():
@ -107,10 +118,6 @@ class GatewayHandler(BaseHTTPRequestHandler):
resp_body = resp.get("body", "") resp_body = resp.get("body", "")
if resp_body: if resp_body:
self.wfile.write(resp_body.encode() if isinstance(resp_body, str) else resp_body) self.wfile.write(resp_body.encode() if isinstance(resp_body, str) else resp_body)
elif receipt.get_status() == RNS.RequestReceipt.FAILED:
self.send_error(504, "Request to TinyWeb server failed")
else:
self.send_error(504, "Request to TinyWeb server timed out")
except ConnectionError as e: except ConnectionError as e:
GatewayState.link = None GatewayState.link = None

View file

@ -308,12 +308,11 @@ def handle_import_submit(body):
return handle_import_form(f"Imported {imported} page(s). {errors} error(s).") return handle_import_form(f"Imported {imported} page(s). {errors} error(s).")
def handle_style_form(msg="", gateway_host=""): def handle_style_form(msg=""):
css = get_setting("custom_css") css = get_setting("custom_css")
name = get_site_name() name = get_site_name()
sharing = get_setting("sharing_enabled", "0") sharing = get_setting("sharing_enabled", "0")
checked = " checked" if sharing == "1" else "" checked = " checked" if sharing == "1" else ""
host = gateway_host or "localhost:8080"
return _respond( return _respond(
f"<h1>customize</h1>" f"<h1>customize</h1>"
f"<h2>name your search engine</h2>" f"<h2>name your search engine</h2>"
@ -340,7 +339,7 @@ def handle_style_form(msg="", gateway_host=""):
f"</form>" f"</form>"
f"<h2>bookmarklet</h2>" f"<h2>bookmarklet</h2>"
f"<p>Drag this link to your bookmarks bar. Click it on any page to index it instantly.</p>" f"<p>Drag this link to your bookmarks bar. Click it on any page to index it instantly.</p>"
f'<p><a href="javascript:void(fetch(\'http://{esc(host)}/bookmark?url=\'+encodeURIComponent(location.href)).then(r=>r.text()).then(t=>alert(t)).catch(()=>alert(\'tinyweb not running\')))">+ save to {esc(name)}</a></p>' f'<p><a href="javascript:void(fetch(\'http://localhost:8080/bookmark?url=\'+encodeURIComponent(location.href)).then(r=>r.text()).then(t=>alert(t)).catch(()=>alert(\'tinyweb not running\')))">+ save to {esc(name)}</a></p>'
f"<p>{msg}</p>" f"<p>{msg}</p>"
f'<a href="/">back</a>' f'<a href="/">back</a>'
) )
@ -651,7 +650,7 @@ def dispatch_request(data):
elif path == "/bookmark": elif path == "/bookmark":
return handle_bookmark(query) return handle_bookmark(query)
elif path == "/style": elif path == "/style":
return handle_style_form(gateway_host=gateway_host) return handle_style_form()
elif path == "/export": elif path == "/export":
return handle_export() return handle_export()
elif path == "/import": elif path == "/import":