diff --git a/core/frontend/src/components/wizard/DefaultParamLoader.vue b/core/frontend/src/components/wizard/DefaultParamLoader.vue index 9d105d7523..5bc312a920 100755 --- a/core/frontend/src/components/wizard/DefaultParamLoader.vue +++ b/core/frontend/src/components/wizard/DefaultParamLoader.vue @@ -95,6 +95,7 @@ import autopilot from '@/store/autopilot_manager' import { Firmware, Vehicle } from '@/types/autopilot' import { printParamWithUnit } from '@/types/autopilot/parameter' import { VForm } from '@/types/vuetify' +import { fetchWithVehicleFallback } from '@/utils/helper_functions' import { availableFirmwares, fetchCurrentBoard } from '../autopilot/AutopilotManagerUpdater' @@ -226,7 +227,7 @@ export default Vue.extend({ return value !== '' }, async fetchParamSets() { - const response = await fetch(REPOSITORY_URL) + const response = await fetchWithVehicleFallback(REPOSITORY_URL) const parameters = await response.json() return parameters diff --git a/core/frontend/src/components/wizard/ScriptLoader.vue b/core/frontend/src/components/wizard/ScriptLoader.vue index de42eb7ed3..df026aee6f 100644 --- a/core/frontend/src/components/wizard/ScriptLoader.vue +++ b/core/frontend/src/components/wizard/ScriptLoader.vue @@ -43,6 +43,7 @@ import Vue from 'vue' import { OneMoreTime } from '@/one-more-time' import autopilot from '@/store/autopilot_manager' import { Firmware, Vehicle } from '@/types/autopilot' +import { fetchWithVehicleFallback } from '@/utils/helper_functions' import { availableFirmwares, fetchCurrentBoard } from '../autopilot/AutopilotManagerUpdater' @@ -130,7 +131,7 @@ export default Vue.extend({ return value !== '' }, async fetchScripts() { - const response = await fetch(REPOSITORY_SCRIPTS_URL) + const response = await fetchWithVehicleFallback(REPOSITORY_SCRIPTS_URL) const scripts = await response.json() return scripts diff --git a/core/frontend/src/components/wizard/Wizard.vue b/core/frontend/src/components/wizard/Wizard.vue index c6326f9a67..c1952a58a4 100644 --- a/core/frontend/src/components/wizard/Wizard.vue +++ b/core/frontend/src/components/wizard/Wizard.vue @@ -323,7 +323,7 @@ import wifi from '@/store/wifi' import { Firmware, Vehicle, vehicleTypeFromString } from '@/types/autopilot' import { Dictionary } from '@/types/common' import back_axios from '@/utils/api' -import { sleep } from '@/utils/helper_functions' +import { fetchWithVehicleFallback, sleep } from '@/utils/helper_functions' import { canUseModelViewer, ensureModelViewer } from '@/utils/model_viewer_support' import ActionStepper, { Configuration, ConfigurationStatus } from './ActionStepper.vue' @@ -761,7 +761,7 @@ export default Vue.extend({ } }, async fetchScript(script: string): Promise { - const response = await fetch(`${REPOSITORY_ROOT}/scripts/ardupilot/${script}`) + const response = await fetchWithVehicleFallback(`${REPOSITORY_ROOT}/scripts/ardupilot/${script}`) return response.text() }, validateParams(): boolean { diff --git a/core/frontend/src/utils/helper_functions.ts b/core/frontend/src/utils/helper_functions.ts index 44ba78fe80..9857e6f505 100644 --- a/core/frontend/src/utils/helper_functions.ts +++ b/core/frontend/src/utils/helper_functions.ts @@ -71,6 +71,40 @@ export function convertGitDescribeToUrl(git_describe: string): string { return `${project_url}/tree/${hash}` } +// Prefix for nginx's caching reverse proxy (see core/tools/nginx/nginx.conf). +// A request to `/cache//` is proxied by the vehicle to `https:///`. +const VEHICLE_PROXY_PREFIX = '/cache/' + +async function fetchWithTimeout(url: string, timeout_ms: number): Promise { + const controller = new AbortController() + const timer = setTimeout(() => controller.abort(), timeout_ms) + try { + return await fetch(url, { signal: controller.signal }) + } finally { + clearTimeout(timer) + } +} + +/** + * Fetch a remote URL, trying the topside browser's own connection first and falling + * back to routing the request through the vehicle's caching proxy when that fails. + * This allows resources hosted outside BlueOS to be reached even when only the vehicle + * (and not the computer running the frontend) has internet access. + */ +export async function fetchWithVehicleFallback(url: string, timeout_ms = 5000): Promise { + try { + const response = await fetchWithTimeout(url, timeout_ms) + if (response.ok) { + return response + } + } catch { + // Direct fetch failed, fall through to the vehicle proxy below. + } + + const proxied_url = VEHICLE_PROXY_PREFIX + url.replace(/^https?:\/\//, '') + return fetchWithTimeout(proxied_url, timeout_ms) +} + export function prettifySize(size_kb: number): string { if (Number.isNaN(size_kb)) { return 'N/A'