polish: emoji favicon (kills the /favicon.ico 404) + adsb.lol 420/429 → clean empty result

- inline SVG 🌍 favicon so the browser never requests /favicon.ico
- proxy/adsb: on adsb.lol rate-limit (420 'Enhance Your Calm' or 429) with no
  cached fallback, return 200 {"ac":[]} instead of the error — layer shows
  nothing until next poll, no scary browser console error; widened upstream
  spacing 1.0→1.3s to hit the limit less often

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
type-two 2026-07-20 20:18:13 +10:00
parent 596cc4db19
commit d0d3da116c
2 changed files with 8 additions and 1 deletions

View File

@ -4,6 +4,7 @@
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>GODSIGH ▸ World View</title>
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='88'>%F0%9F%8C%8D</text></svg>" />
<script>window.CESIUM_BASE_URL = 'https://cdn.jsdelivr.net/npm/cesium@1.143.0/Build/Cesium/';</script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/cesium@1.143.0/Build/Cesium/Widgets/widgets.css" />
<link rel="stylesheet" href="css/style.css" />

View File

@ -78,7 +78,7 @@ ADSB_PATH_RE = re.compile(
r'|lat/-?\d{1,3}(\.\d+)?/lon/-?\d{1,3}(\.\d+)?/dist/\d{1,4}(\.\d+)?'
r'|closest/-?\d{1,3}(\.\d+)?/-?\d{1,3}(\.\d+)?/\d{1,4}(\.\d+)?)$')
ADSB_CACHE_SEC = 120
ADSB_MIN_INTERVAL = 1.0 # min seconds between upstream adsb.lol calls (anti-burst; it 429s fast)
ADSB_MIN_INTERVAL = 1.3 # min seconds between upstream adsb.lol calls (anti-burst; it 429s fast)
_adsb_cache = {} # path -> (body, ts)
_adsb_lock = threading.Lock()
_adsb_next = [0.0] # earliest wall-time the next upstream call may start
@ -353,6 +353,12 @@ class Handler(SimpleHTTPRequestHandler):
except urllib.error.HTTPError as e:
if hit is not None:
return self._send(200, "application/json", hit[0], {"X-Godsigh-Cache": "stale"})
# adsb.lol rate-limits with 429 or 420 ("Enhance Your Calm"). With no
# cache to fall back on, return a clean empty result (200) rather than
# surfacing the error — the layer just shows nothing until the next
# poll, and the browser console stays quiet.
if e.code in (420, 429):
return self._send(200, "application/json", b'{"ac":[]}', {"X-Godsigh-Cache": "ratelimited"})
return self._send_json(e.code, {"error": f"adsb.lol upstream {e.code}"})
except Exception as e:
if hit is not None: