fix: report adsb unavailable state as degraded
All checks were successful
Codex Template Compliance / template-compliance (pull_request) Successful in 5s
Build / test-and-image (pull_request) Successful in 1m13s

This commit is contained in:
MrSphay
2026-05-17 13:55:42 +02:00
parent c2d572e6f5
commit b2f604b120
6 changed files with 146 additions and 11 deletions

View File

@@ -1,7 +1,8 @@
// ADS-B Exchange — Unfiltered Flight Tracking (including Military)
// Unlike FlightRadar24/FlightAware, ADS-B Exchange does NOT filter military aircraft.
// Public feed access varies; RapidAPI tier available for programmatic use.
// This module attempts the public endpoints and falls back to a documented stub.
// This module reports explicit disabled/degraded state instead of making
// unavailable aircraft data look live.
import { safeFetch } from '../utils/fetch.mjs';
@@ -140,6 +141,7 @@ async function fetchViaRapidApi(apiKey) {
// Get all military aircraft
const data = await safeFetch(`${ENDPOINTS.rapidApi}/mil`, {
timeout: 20000,
source: 'adsb-rapidapi',
headers: {
'X-RapidAPI-Key': apiKey,
'X-RapidAPI-Host': 'adsbexchange-com1.p.rapidapi.com',
@@ -151,21 +153,26 @@ async function fetchViaRapidApi(apiKey) {
// Attempt to fetch from public feed
async function fetchPublicFeed() {
const data = await safeFetch(ENDPOINTS.publicFeed, { timeout: 15000 });
const data = await safeFetch(ENDPOINTS.publicFeed, { timeout: 15000, source: 'adsb-public' });
return data;
}
// Get military aircraft from available sources
export async function getMilitaryAircraft(apiKey) {
async function getMilitaryAircraftResult(apiKey) {
const failures = [];
// Try RapidAPI first if key available
if (apiKey) {
const data = await fetchViaRapidApi(apiKey);
if (data && !data.error) {
const aircraft = data.ac || data.aircraft || [];
if (Array.isArray(aircraft)) {
return aircraft.map(classifyAircraft).filter(a => a.isMilitary);
return {
provider: 'rapidapi',
aircraft: aircraft.map(classifyAircraft).filter(a => a.isMilitary),
};
}
}
failures.push({ provider: 'rapidapi', error: data?.error || 'RapidAPI returned an unsupported payload' });
}
// Try public feed
@@ -173,11 +180,21 @@ export async function getMilitaryAircraft(apiKey) {
if (pubData && !pubData.error) {
const aircraft = pubData.ac || pubData.aircraft || pubData.states || [];
if (Array.isArray(aircraft)) {
return aircraft.map(classifyAircraft).filter(a => a.isMilitary);
return {
provider: 'public-feed',
aircraft: aircraft.map(classifyAircraft).filter(a => a.isMilitary),
};
}
}
failures.push({ provider: 'public-feed', error: pubData?.error || 'Public feed returned an unsupported payload' });
return null; // all sources failed
return { provider: null, aircraft: null, failures };
}
// Get military aircraft from available sources
export async function getMilitaryAircraft(apiKey) {
const result = await getMilitaryAircraftResult(apiKey);
return result.aircraft;
}
// Get all aircraft in a geographic bounding box via RapidAPI
@@ -208,7 +225,8 @@ export async function getAircraftInArea(lat, lon, radiusNm = 250, apiKey) {
// Briefing — attempt to get military flight data, document what's available
export async function briefing() {
const apiKey = process.env.ADSB_API_KEY || process.env.RAPIDAPI_KEY || null;
const militaryAircraft = await getMilitaryAircraft(apiKey);
const result = await getMilitaryAircraftResult(apiKey);
const militaryAircraft = result.aircraft;
// If we got data, analyze it
if (militaryAircraft && militaryAircraft.length > 0) {
@@ -255,6 +273,7 @@ export async function briefing() {
source: 'ADS-B Exchange',
timestamp: new Date().toISOString(),
status: 'live',
provider: result.provider,
totalMilitary: militaryAircraft.length,
byCountry,
categories: {
@@ -269,10 +288,18 @@ export async function briefing() {
}
// No data available — return stub with integration documentation
const status = apiKey ? 'degraded' : 'disabled';
const error = apiKey
? 'ADS-B providers returned no usable aircraft data'
: 'ADSB_API_KEY or RAPIDAPI_KEY is not configured';
return {
source: 'ADS-B Exchange',
timestamp: new Date().toISOString(),
status: apiKey ? 'error' : 'no_key',
status,
provider: result.provider,
error,
failures: result.failures,
militaryAircraft: [],
message: apiKey
? 'ADS-B Exchange API returned no data. The endpoint may be temporarily unavailable.'