From 8f9732ec0c9de376b7921164dba21260dde40278 Mon Sep 17 00:00:00 2001 From: Liang Jiaqing Date: Sat, 8 Aug 2026 14:31:41 +0800 Subject: [PATCH] hub: /api/peers incremental via psig (unchanged list -> {same:1}, ~68% less relay bytes) --- frontends/hub.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/frontends/hub.py b/frontends/hub.py index ceea4465..f362502e 100644 --- a/frontends/hub.py +++ b/frontends/hub.py @@ -6,7 +6,7 @@ HTTP (errors = {'error','code'} + status: offline/gone 404, busy 409, timeout 50 [{j,title,n}]}],sig} or {same:1,sig} | {name}/seg/{i}/{j}?off=N -> {content,off,n} (step bodies, tailable) POST {name}/put {"text":..} -> {ok:1} | {name}/abort -> {ok:1} """ -import os, re, sys, json, time, asyncio, threading, random, hmac +import os, re, sys, json, time, asyncio, threading, random, hmac, hashlib PORT = int(os.environ.get('GA_HUB_PORT', 19736)) WEB_PORT = int(os.environ.get('GA_HUB_WEB_PORT', PORT + 1)) # the only face fit to be tunnelled out (frp this one) TOKEN = os.environ.get('GA_HUB_TOKEN') or ''.join(random.choices('abcdefghijkmnpqrstuvwxyz23456789', k=6)) @@ -162,7 +162,7 @@ if __name__ == '__main__': if name and peers.get(name) is ws: # never evict the successor that replaced us peers.pop(name, None); meta.pop(name, None); print(f'[-] {name} ({len(peers)} online)') @app.get('/api/peers') - async def api_peers(): # one cheap round-trip per peer: counts only, and only if its sig moved + async def api_peers(psig: str = None): # psig given -> {same:1} while the row set holds (no psig = full list) now = time.time() names = [n for n in peers if 'get' in (meta.get(n, {}).get('caps') or []) and now >= pdead.get(n, 0)] rs = await asyncio.gather(*[ask(n, {'op': 'get', 'detail': 0, @@ -180,7 +180,9 @@ if __name__ == '__main__': 'caps': caps, 'run': r.get('run')}) if r.get('sig'): pcache[n] = rows[-1] for n in [x for x in pcache if x not in peers]: pcache.pop(n, None) - return rows + if psig is None: return rows + sg = hashlib.md5(json.dumps(rows, sort_keys=True).encode()).hexdigest()[:12] + return {'same': 1} if psig == sg else {'rows': rows, 'psig': sg} @app.get('/api/{name}/messages') async def api_messages(name: str, detail: int = 1, sig: str = None, since: int = 0): return out(await ask(name, {'op': 'get', 'detail': detail, 'sig': sig, 'since': since})) @app.get('/api/{name}/seg/{i}/{j}')