Files
openconcho/packages/web/src/lib/dispatch.ts
Offending Commit 0935099bc2 feat(web): route web build through same-origin /api proxy
client.current and createScopedClient resolve transport via dispatchFor:
web -> absolute origin + /api base with an X-Honcho-Upstream header; Tauri ->
absolute instance URL + reqwest. Absolute base (not bare "/api") so openapi-fetch
can construct a Request under node/undici and in the browser alike. Fleet fan-out
is unchanged; fleet.test.tsx now asserts the proxy contract.
2026-06-02 11:48:13 -05:00

48 lines
1.8 KiB
TypeScript

import { httpFetch } from "@/lib/http";
import { isTauri } from "@/lib/platform";
/** Same-origin path prefix the web build issues all Honcho calls through. */
export const API_PREFIX = "/api";
/** Request header naming the real Honcho upstream for the proxy to forward to. */
export const UPSTREAM_HEADER = "X-Honcho-Upstream";
/** Response header the proxy sets on its OWN refusals (so they aren't read as upstream auth). */
export const PROXY_REJECT_HEADER = "X-Honcho-Proxy-Reject";
export interface Dispatch {
baseUrl: string;
headers: Record<string, string>;
fetch: typeof globalThis.fetch;
}
function normalizeUpstream(url: string): string {
return url.trim().replace(/\/+$/, "");
}
/**
* Absolute same-origin base for the web proxy. Absolute (origin + `/api`), not the
* bare relative `/api`, so openapi-fetch and node/undici can construct a Request
* without an ambient document base — and so it resolves identically in the browser,
* behind a tunnel, and under jsdom.
*/
function webApiBase(): string {
const origin = typeof location !== "undefined" ? location.origin : "";
return `${origin}${API_PREFIX}`;
}
/**
* Resolve how to issue a request for an instance.
* - Web: same-origin `/api` + `X-Honcho-Upstream` header (proxy forwards server-side, no CORS).
* - Tauri: the absolute instance URL via reqwest (no browser same-origin policy).
*/
export function dispatchFor(instance: { baseUrl: string; token?: string }): Dispatch {
const headers: Record<string, string> = { "Content-Type": "application/json" };
if (instance.token) headers.Authorization = `Bearer ${instance.token}`;
if (isTauri()) {
return { baseUrl: instance.baseUrl, headers, fetch: httpFetch };
}
headers[UPSTREAM_HEADER] = normalizeUpstream(instance.baseUrl);
return { baseUrl: webApiBase(), headers, fetch: httpFetch };
}