Add bounding-box region picker to the API Server plugin panel
Give the superadmin Plugins panel the same OpenSky "Default bounding box" experience as the Web App: a grouped preset dropdown (World / Continents / Countries) plus a Custom option for manual lamin,lomin,lamax,lomax entry. Introduce a "bbox" config-field type (rendering hint only; unknown types still degrade to a text input) and mark the OpenSky bbox field with it. Values that match a preset render as the region name. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
c5e34e32be
commit
150758b0bf
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1
-1
@@ -6,7 +6,7 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<meta name="theme-color" content="#0F1E3D" />
|
<meta name="theme-color" content="#0F1E3D" />
|
||||||
<title>PilotVault · API Server</title>
|
<title>PilotVault · API Server</title>
|
||||||
<script type="module" crossorigin src="/assets/index-BPK80y5V.js"></script>
|
<script type="module" crossorigin src="/assets/index--4fMszzi.js"></script>
|
||||||
<link rel="stylesheet" crossorigin href="/assets/index-CVh8EDzq.css">
|
<link rel="stylesheet" crossorigin href="/assets/index-CVh8EDzq.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ func (p *Plugin) Descriptor() plugins.Descriptor {
|
|||||||
Help: "Global account tier. Leave it unset to let each organization or user pick their own plan; set a value only to force one plan for everyone. Determines the daily credit allowance shown next to remaining credits."},
|
Help: "Global account tier. Leave it unset to let each organization or user pick their own plan; set a value only to force one plan for everyone. Determines the daily credit allowance shown next to remaining credits."},
|
||||||
{Key: "clientId", Label: "OAuth2 client ID", Type: "text", Help: "Optional — leave blank for anonymous access (lower rate limits)."},
|
{Key: "clientId", Label: "OAuth2 client ID", Type: "text", Help: "Optional — leave blank for anonymous access (lower rate limits)."},
|
||||||
{Key: "clientSecret", Label: "OAuth2 client secret", Type: "password", Secret: true, Help: "Paired with the client ID for authenticated access."},
|
{Key: "clientSecret", Label: "OAuth2 client secret", Type: "password", Secret: true, Help: "Paired with the client ID for authenticated access."},
|
||||||
{Key: "bbox", Label: "Default bounding box", Type: "text", Default: defaultBBox, Help: "lamin,lomin,lamax,lomax — used by the health probe and states.bbox."},
|
{Key: "bbox", Label: "Default bounding box", Type: "bbox", Default: defaultBBox, Help: "Pick a region or choose Custom to enter lamin,lomin,lamax,lomax — used by the health probe and states.bbox."},
|
||||||
{Key: "allowAnonymous", Label: "Anonymous access", Type: "select", Default: "true",
|
{Key: "allowAnonymous", Label: "Anonymous access", Type: "select", Default: "true",
|
||||||
Options: []plugins.SelectOption{
|
Options: []plugins.SelectOption{
|
||||||
{Value: "true", Label: "Enabled — allow use without credentials"},
|
{Value: "true", Label: "Enabled — allow use without credentials"},
|
||||||
|
|||||||
@@ -176,6 +176,54 @@ const pluginsMsg = ref("");
|
|||||||
const editingPlugin = ref(""); // name whose config form is expanded
|
const editingPlugin = ref(""); // name whose config form is expanded
|
||||||
const editConfig = ref({}); // working copy of the expanded plugin's config
|
const editConfig = ref({}); // working copy of the expanded plugin's config
|
||||||
const busyPlugin = ref(""); // name of a plugin with an in-flight action
|
const busyPlugin = ref(""); // name of a plugin with an in-flight action
|
||||||
|
|
||||||
|
// Predefined bounding boxes for the "bbox" config field (lamin,lomin,lamax,lomax).
|
||||||
|
// Mirrors the Web App picker; a "Custom…" entry falls back to manual coordinates.
|
||||||
|
// Europe is the default (kept in sync with the OpenSky plugin's defaultBBox).
|
||||||
|
const BBOX_GROUPS = [
|
||||||
|
{ label: "World", options: [{ value: "-90,-180,90,180", label: "World" }] },
|
||||||
|
{ label: "Continents", options: [
|
||||||
|
{ value: "34,-25,72,45", label: "Europe" },
|
||||||
|
{ value: "-35,-18,38,52", label: "Africa" },
|
||||||
|
{ value: "5,25,82,180", label: "Asia" },
|
||||||
|
{ value: "7,-168,72,-52", label: "North America" },
|
||||||
|
{ value: "-56,-82,13,-34", label: "South America" },
|
||||||
|
{ value: "-48,110,-10,180", label: "Oceania" },
|
||||||
|
] },
|
||||||
|
{ label: "Countries", options: [
|
||||||
|
{ value: "49,14.1,54.9,24.2", label: "Poland" },
|
||||||
|
{ value: "50.5,3.2,53.7,7.3", label: "Netherlands" },
|
||||||
|
{ value: "47.2,5.8,55.1,15.1", label: "Germany" },
|
||||||
|
{ value: "41.3,-5.2,51.1,9.6", label: "France" },
|
||||||
|
{ value: "49.9,-8.7,59,1.8", label: "United Kingdom" },
|
||||||
|
{ value: "35.9,-9.6,43.8,3.4", label: "Spain" },
|
||||||
|
{ value: "36.6,6.6,47.1,18.6", label: "Italy" },
|
||||||
|
{ value: "24,-125,49.5,-66.5", label: "United States" },
|
||||||
|
] },
|
||||||
|
];
|
||||||
|
const BBOX_FLAT = BBOX_GROUPS.flatMap((g) => g.options);
|
||||||
|
// Normalise to "n1,n2,n3,n4" so matching ignores spacing / trailing zeros.
|
||||||
|
function normBbox(s) {
|
||||||
|
const parts = String(s || "").split(",").map((x) => x.trim());
|
||||||
|
if (parts.length !== 4) return "";
|
||||||
|
const nums = parts.map(Number);
|
||||||
|
if (nums.some((n) => Number.isNaN(n))) return "";
|
||||||
|
return nums.join(",");
|
||||||
|
}
|
||||||
|
// Field keys currently in manual ("Custom…") mode, so the text input is shown.
|
||||||
|
const bboxCustom = ref({});
|
||||||
|
// The <select> value for a bbox field: a matching preset, or "__custom__".
|
||||||
|
function bboxSelectValue(key) {
|
||||||
|
if (bboxCustom.value[key]) return "__custom__";
|
||||||
|
const norm = normBbox(editConfig.value[key]);
|
||||||
|
const match = norm && BBOX_FLAT.find((o) => normBbox(o.value) === norm);
|
||||||
|
return match ? match.value : "__custom__";
|
||||||
|
}
|
||||||
|
function setBboxSelect(key, v) {
|
||||||
|
if (v === "__custom__") { bboxCustom.value = { ...bboxCustom.value, [key]: true }; return; }
|
||||||
|
bboxCustom.value = { ...bboxCustom.value, [key]: false };
|
||||||
|
editConfig.value[key] = v;
|
||||||
|
}
|
||||||
const newExt = ref({ name: "", baseURL: "", provider: "" });
|
const newExt = ref({ name: "", baseURL: "", provider: "" });
|
||||||
const newExtErr = ref("");
|
const newExtErr = ref("");
|
||||||
const newExtBusy = ref(false);
|
const newExtBusy = ref(false);
|
||||||
@@ -278,6 +326,7 @@ function startPluginEdit(p) {
|
|||||||
if (cfg[f.key] === undefined && f.default) cfg[f.key] = f.default;
|
if (cfg[f.key] === undefined && f.default) cfg[f.key] = f.default;
|
||||||
}
|
}
|
||||||
editConfig.value = cfg;
|
editConfig.value = cfg;
|
||||||
|
bboxCustom.value = {}; // start on the preset dropdown; non-preset values imply Custom
|
||||||
}
|
}
|
||||||
|
|
||||||
async function savePluginConfig(p) {
|
async function savePluginConfig(p) {
|
||||||
@@ -1273,6 +1322,22 @@ const deviceApi = [
|
|||||||
<select v-if="f.type === 'select'" v-model="editConfig[f.key]" class="pv-input">
|
<select v-if="f.type === 'select'" v-model="editConfig[f.key]" class="pv-input">
|
||||||
<option v-for="o in f.options" :key="o.value" :value="o.value">{{ o.label }}</option>
|
<option v-for="o in f.options" :key="o.value" :value="o.value">{{ o.label }}</option>
|
||||||
</select>
|
</select>
|
||||||
|
<template v-else-if="f.type === 'bbox'">
|
||||||
|
<select :value="bboxSelectValue(f.key)" class="pv-input" @change="setBboxSelect(f.key, $event.target.value)">
|
||||||
|
<optgroup v-for="g in BBOX_GROUPS" :key="g.label" :label="g.label">
|
||||||
|
<option v-for="o in g.options" :key="o.value" :value="o.value">{{ o.label }}</option>
|
||||||
|
</optgroup>
|
||||||
|
<option value="__custom__">Custom…</option>
|
||||||
|
</select>
|
||||||
|
<input
|
||||||
|
v-if="bboxSelectValue(f.key) === '__custom__'"
|
||||||
|
v-model="editConfig[f.key]"
|
||||||
|
type="text"
|
||||||
|
class="pv-input font-mono"
|
||||||
|
autocomplete="off"
|
||||||
|
placeholder="50.5,3.2,53.7,7.3"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
<input
|
<input
|
||||||
v-else
|
v-else
|
||||||
v-model="editConfig[f.key]"
|
v-model="editConfig[f.key]"
|
||||||
|
|||||||
Reference in New Issue
Block a user