Add location-aware automatic default bounding box

The OpenSky "Default bounding box" now follows where flying happens.
A new "Automatic" picker mode (the default) resolves the live-map area
from a location cascade — drone telemetry → phone GPS → browser
geolocation → the user's Region country → Europe — instead of a fixed
box. Manual presets and Custom coordinates still work.

- Web App: new shared countries.js dataset (all countries + bbox,
  offline point→country); the bbox picker gains all European countries
  and an Automatic option (client pref prefs.autoBbox); the Region
  setting expands from 6 locale entries to all countries; the live map
  resolves the cascade each poll and sends it as ?bbox=.
- API Server: the states endpoint accepts and validates a ?bbox=
  override (validBBox); the Web App BFF forwards the query; the hub
  relays new phoneLatitude/phoneLongitude telemetry to the Web App.
- Fly App: reports the phone's own GPS (geolocator) alongside
  telemetry, used as the "your location" fallback.
- API panel: the OpenSky bbox picker lists all European countries.

Builds verified across web, panel, both Go modules and the Fly App
APK. Region list, Automatic default and the cascade ?bbox= override
verified in the browser.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
tajniak81
2026-07-14 00:07:12 +02:00
co-authored by Claude Opus 4.8
parent 150758b0bf
commit 94f6876024
20 changed files with 637 additions and 71 deletions
+6 -1
View File
@@ -237,7 +237,12 @@ func (a *App) handleOpenSkyHealth(w http.ResponseWriter, r *http.Request) {
// GET /bff/integrations/opensky/states → API Server /api/integrations/opensky/states.
// Live aircraft positions for the Live map.
func (a *App) handleOpenSkyStates(w http.ResponseWriter, r *http.Request) {
req, _ := http.NewRequest(http.MethodGet, a.apiBaseFor(r)+"/api/integrations/opensky/states", nil)
target := a.apiBaseFor(r) + "/api/integrations/opensky/states"
// Forward the optional ?bbox= override the Live map sends in auto mode.
if r.URL.RawQuery != "" {
target += "?" + r.URL.RawQuery
}
req, _ := http.NewRequest(http.MethodGet, target, nil)
req.Header.Set("Authorization", tokenOf(r))
a.doRelay(w, req)
}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+2 -2
View File
@@ -35,8 +35,8 @@
})()
</script>
<title>PilotVault — Control Panel</title>
<script type="module" crossorigin src="./assets/index-CznFJTz4.js"></script>
<link rel="stylesheet" crossorigin href="./assets/index-D0ghlckk.css">
<script type="module" crossorigin src="./assets/index-BSx6Lf8i.js"></script>
<link rel="stylesheet" crossorigin href="./assets/index-FLz21UuE.css">
</head>
<body>
<div id="app"></div>