Some checks failed
ci / ci (22, ubuntu-latest) (push) Has been cancelled
Nuxt 4 + Supabase + Flightics API. Incluye búsqueda de vuelos, inspiraciones, watchlist, tracking de precios y mapa interactivo. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
101 lines
3.3 KiB
TypeScript
101 lines
3.3 KiB
TypeScript
export default defineCachedEventHandler(async (event) => {
|
|
const { flightno } = getQuery(event)
|
|
if (!flightno || typeof flightno !== 'string') {
|
|
throw createError({ statusCode: 400, message: 'flightno is required' })
|
|
}
|
|
|
|
const code = flightno.replace(/\s/g, '').toUpperCase()
|
|
const fr24Url = `https://www.flightradar24.com/data/flights/${code.toLowerCase()}`
|
|
|
|
// FR24 live feed
|
|
const data = await $fetch<any>(`https://data-live.flightradar24.com/zones/fcgi/feed.js`, {
|
|
query: { flightno: code, faa: 1, satellite: 1, mlat: 1, flarm: 1, adsb: 1, gnd: 1, air: 1, vehicles: 0, estimated: 1, gliders: 0 },
|
|
headers: {
|
|
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36',
|
|
'Accept': 'application/json'
|
|
}
|
|
}).catch(() => null)
|
|
|
|
if (!data) return { found: false, fr24Url }
|
|
|
|
// Parse flight entries (skip metadata keys)
|
|
const flights = Object.entries(data)
|
|
.filter(([key]) => !['full_count', 'version', 'stats'].includes(key))
|
|
.map(([id, val]: [string, any]) => {
|
|
if (!Array.isArray(val) || val.length < 18) return null
|
|
return {
|
|
fr24Id: id,
|
|
icao24: val[0],
|
|
lat: val[1],
|
|
lon: val[2],
|
|
heading: val[3],
|
|
altitude: val[4],
|
|
speed: val[5],
|
|
squawk: val[6],
|
|
aircraft: val[8],
|
|
registration: val[9],
|
|
timestamp: val[10],
|
|
origin: val[11],
|
|
destination: val[12],
|
|
flightNumber: val[13],
|
|
onGround: val[14] === 1,
|
|
verticalSpeed: val[15],
|
|
callsign: val[16],
|
|
airline: val[18]
|
|
}
|
|
})
|
|
.filter((f): f is NonNullable<typeof f> => f != null && (f.lat !== 0 || f.lon !== 0 || f.aircraft != null))
|
|
|
|
if (flights.length === 0) return { found: false, fr24Url }
|
|
|
|
const flight = flights[0]
|
|
|
|
// Get detail for richer data
|
|
let detail: any = null
|
|
if (flight.fr24Id) {
|
|
detail = await $fetch<any>(`https://data-live.flightradar24.com/clickhandler/`, {
|
|
query: { version: '1.5', flight: flight.fr24Id },
|
|
headers: {
|
|
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36'
|
|
}
|
|
}).catch(() => null)
|
|
}
|
|
|
|
// Determine status
|
|
let status = 'Programado'
|
|
if (detail?.status?.text) {
|
|
status = detail.status.text
|
|
} else if (flight.altitude > 0 && !flight.onGround) {
|
|
status = 'En vuelo'
|
|
} else if (flight.onGround && flight.speed > 5) {
|
|
status = 'En tierra (taxiing)'
|
|
} else if (flight.onGround) {
|
|
status = 'En tierra'
|
|
}
|
|
|
|
return {
|
|
found: true,
|
|
fr24Url,
|
|
flight: {
|
|
flightNumber: flight.flightNumber || code,
|
|
callsign: flight.callsign,
|
|
aircraft: detail?.aircraft?.model?.text || flight.aircraft || null,
|
|
aircraftCode: flight.aircraft,
|
|
registration: flight.registration,
|
|
airline: detail?.airline?.name || flight.airline || null,
|
|
origin: flight.origin,
|
|
destination: flight.destination,
|
|
lat: flight.lat,
|
|
lon: flight.lon,
|
|
altitude: flight.altitude,
|
|
speed: flight.speed,
|
|
heading: flight.heading,
|
|
onGround: flight.onGround,
|
|
verticalSpeed: flight.verticalSpeed,
|
|
status,
|
|
departureDelay: detail?.time?.historical?.delay?.departure ?? null,
|
|
arrivalDelay: detail?.time?.historical?.delay?.arrival ?? null
|
|
}
|
|
}
|
|
}, { maxAge: 60 * 5 }) // Cache 5 min
|