Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions public/locales/en/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -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 here</0>.",
"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 here</0>.",
Expand Down
8 changes: 6 additions & 2 deletions src/app/api/_utils/api_constants.ts
Original file line number Diff line number Diff line change
@@ -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';

Expand Down
36 changes: 25 additions & 11 deletions src/app/api/_utils/proxy.ts
Original file line number Diff line number Diff line change
@@ -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";
Expand All @@ -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 }
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}
10 changes: 8 additions & 2 deletions src/components/Application/application_error.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,17 @@ export function ApplicationError({flightFound, flightData}: {flightFound: boolea
return <Box sx={{ py: 2 }}>
<Alert severity="error"><Trans k="error.no_flight_found" /></Alert>
</Box>;


case Reason.INCONSISTENT_DATA:
trackEvent(EVENT_API_ERROR, { category: 'flight_search', error: errorReasonApi });
return <Box sx={{ py: 2 }}>
<Alert severity="error"><Trans k="error.inconsistent_data" /></Alert>
</Box>;

default:
trackEvent(EVENT_API_ERROR, { category: 'flight_search', error: errorReasonApi });
return <Box sx={{ py: 2 }}>
<Alert severity="error"><Trans k="error.no_flight_found" /></Alert>
<Alert severity="error"><Trans k="error.comm_error" /></Alert>
</Box>;
}

Expand Down
4 changes: 2 additions & 2 deletions src/hooks/api/use_flightstats_api.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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}`);
Expand All @@ -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,
};
Expand Down