diff --git a/public/locales/en/common.json b/public/locales/en/common.json
index ba9aa59..3e51e9e 100644
--- a/public/locales/en/common.json
+++ b/public/locales/en/common.json
@@ -85,6 +85,7 @@
"departureDate": "Please select a departure date"
},
"no_flight_found": "No matching flight found",
+ "comm_error": "Unable to connect to flight data service. Please try again later.",
"inconsistent_data": "The flight data provided by the flight data service is inconsistent and cannot be used for protection purposes.",
"airport_not_whitelisted": "Protection for flights from {{dep}} to {{arr}} is not available. Please choose a different flight. Refer to the <0>complete list of approved airports here0>.",
"airport_blacklisted": "Protection for flights from or to {{airport}} is not available. Please choose a different flight. Refer to the <0>complete list of approved airports here0>.",
diff --git a/src/app/api/_utils/api_constants.ts b/src/app/api/_utils/api_constants.ts
index 0d3941a..eced42d 100644
--- a/src/app/api/_utils/api_constants.ts
+++ b/src/app/api/_utils/api_constants.ts
@@ -1,7 +1,11 @@
export const FLIGHTSTATS_BASE_URL = process.env.FLIGHTSTATS_BASE_URL || 'https://api.flightstats.com/flex';
-export const FLIGHTSTATS_APP_ID = process.env.FLIGHTSTATS_APP_ID || '123456789';
-export const FLIGHTSTATS_APP_KEY = process.env.FLIGHTSTATS_APP_KEY || '123456789';
+export const FLIGHTSTATS_APP_ID = process.env.FLIGHTSTATS_APP_ID || '';
+export const FLIGHTSTATS_APP_KEY = process.env.FLIGHTSTATS_APP_KEY || '';
+
+if (!FLIGHTSTATS_APP_ID || !FLIGHTSTATS_APP_KEY) {
+ console.error('WARNING: FLIGHTSTATS_APP_ID and/or FLIGHTSTATS_APP_KEY environment variables are not set. Flightstats API requests will fail.');
+}
export const APP_BASE_URL = process.env.NEXT_PUBLIC_APP_BASE_URL || 'https://flightdelay.app';
diff --git a/src/app/api/_utils/proxy.ts b/src/app/api/_utils/proxy.ts
index 66df292..b61bd3e 100644
--- a/src/app/api/_utils/proxy.ts
+++ b/src/app/api/_utils/proxy.ts
@@ -1,4 +1,4 @@
-import axios, { AxiosHeaders } from "axios";
+import axios, { AxiosHeaders, isAxiosError } from "axios";
import { LOGGER } from "../../../utils/logger_backend";
const LOG_API_PROXY = process.env.LOG_API_PROXY ?? "false";
@@ -8,21 +8,35 @@ export async function sendRequestAndReturnResponse(reqId: string, url: string, m
LOGGER.debug(`proxy request ==> ${method ?? 'GET'} ${url}`, { reqId });
}
const before = Date.now();
- const proxyResponse = await axios({
- url: url,
- method: method ?? 'GET',
- data: body,
- headers: headers,
- });
+ let proxyResponse;
+ try {
+ proxyResponse = await axios({
+ url: url,
+ method: method ?? 'GET',
+ data: body,
+ headers: headers,
+ validateStatus: () => true, // accept all status codes, handle errors explicitly
+ });
+ } catch (error) {
+ const after = Date.now();
+ const errMsg = isAxiosError(error) ? `${error.code}: ${error.message}` : String(error);
+ LOGGER.error(`proxy error <== ${errMsg}`, { reqId, proxyRequestDuration: after - before });
+ return Response.json(
+ { error: 'proxy_request_failed', message: errMsg },
+ {
+ status: 502,
+ headers: { 'Content-Type': 'application/json', 'X-Proxy-Request-Id': reqId }
+ });
+ }
const after = Date.now();
if (LOG_API_PROXY.toLowerCase() === "true") {
LOGGER.debug(`proxy response <== ${proxyResponse.status}`, { reqId, proxyRequestDuration: after - before });
}
const respJson = await proxyResponse.data;
return Response.json(
- respJson,
- {
- status: proxyResponse.status,
- headers: { 'Content-Type': 'application/json', 'X-Proxy-Request-Id': reqId }
+ respJson,
+ {
+ status: proxyResponse.status,
+ headers: { 'Content-Type': 'application/json', 'X-Proxy-Request-Id': reqId }
});
}
diff --git a/src/app/api/flightstats/schedule/[carrier]/[flightNumber]/[departureDate]/route.ts b/src/app/api/flightstats/schedule/[carrier]/[flightNumber]/[departureDate]/route.ts
index 42889bd..9bce94a 100644
--- a/src/app/api/flightstats/schedule/[carrier]/[flightNumber]/[departureDate]/route.ts
+++ b/src/app/api/flightstats/schedule/[carrier]/[flightNumber]/[departureDate]/route.ts
@@ -18,8 +18,16 @@ export async function GET(
const flightNumber = params.flightNumber;
const departureDate = params.departureDate;
LOGGER.info(`[${reqId}] fetching flight status for ${carrier} ${flightNumber} ${departureDate}`);
- const year = departureDate.split('-')[0];
- const month = departureDate.split('-')[1];
- const day = departureDate.split('-')[2];
+
+ const dateMatch = departureDate.match(/^(\d{4})-(\d{2})-(\d{2})$/);
+ if (!dateMatch) {
+ LOGGER.warn(`[${reqId}] invalid departure date format: ${departureDate}`);
+ return Response.json(
+ { error: 'invalid_date', message: 'Departure date must be in YYYY-MM-DD format' },
+ { status: 400, headers: { 'Content-Type': 'application/json', 'X-Proxy-Request-Id': reqId } }
+ );
+ }
+ const [, year, month, day] = dateMatch;
+
return sendRequestAndReturnResponse(reqId, flightstatsScheduleUrl(carrier, flightNumber, year, month, day));
}
diff --git a/src/components/Application/application_error.tsx b/src/components/Application/application_error.tsx
index 380795f..245f5fa 100644
--- a/src/components/Application/application_error.tsx
+++ b/src/components/Application/application_error.tsx
@@ -76,11 +76,17 @@ export function ApplicationError({flightFound, flightData}: {flightFound: boolea
return
;
-
+
+ case Reason.INCONSISTENT_DATA:
+ trackEvent(EVENT_API_ERROR, { category: 'flight_search', error: errorReasonApi });
+ return
+
+ ;
+
default:
trackEvent(EVENT_API_ERROR, { category: 'flight_search', error: errorReasonApi });
return
-
+
;
}
diff --git a/src/hooks/api/use_flightstats_api.tsx b/src/hooks/api/use_flightstats_api.tsx
index 3b11e36..6e1b54e 100644
--- a/src/hooks/api/use_flightstats_api.tsx
+++ b/src/hooks/api/use_flightstats_api.tsx
@@ -8,7 +8,7 @@ export function useFlightstatsApi() {
async function fetchFlightData(carrier: string, flightNumber: string, departureDate: dayjs.Dayjs): Promise<{ flights: ScheduledFlight[], airports: Airport[], carrier: string, flightNumber: string}> {
console.log("fetching flight data for", carrier, flightNumber, departureDate);
const uri = `/api/flightstats/schedule/${encodeURIComponent(carrier)}/${encodeURIComponent(flightNumber)}/${encodeURIComponent(departureDate.format('YYYY-MM-DD'))}`;
- const res = await fetch(uri, { cache: 'force-cache' });
+ const res = await fetch(uri, { cache: 'no-store' });
if (! res.ok) {
throw new Error(`Error fetching flightstats data: ${res.statusText}`);
@@ -26,7 +26,7 @@ export function useFlightstatsApi() {
return {
flights: jsonResponse.scheduledFlights as ScheduledFlight[],
- airports: jsonResponse.appendix.airports as Airport[],
+ airports: jsonResponse.appendix?.airports as Airport[] ?? [],
carrier,
flightNumber,
};