fix: make API calls respect BASE_PATH for subpath deployments
config.apiBaseUrl now derives from <base> tag when no explicit config is set (e.g., /server/api/v1 instead of /api/v1). commands.ts authFetch prepends apiBaseUrl and uses relative paths. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
|
||||
import { useAuthStore } from '../../auth/auth-store'
|
||||
import { config } from '../../config'
|
||||
|
||||
// ── Application Config ────────────────────────────────────────────────────
|
||||
|
||||
@@ -32,20 +33,20 @@ export interface ApplicationConfig {
|
||||
compressSuccess: boolean
|
||||
}
|
||||
|
||||
/** Authenticated fetch using the JWT from auth store */
|
||||
function authFetch(url: string, init?: RequestInit): Promise<Response> {
|
||||
/** Authenticated fetch using the JWT from auth store. Paths are relative to apiBaseUrl. */
|
||||
function authFetch(path: string, init?: RequestInit): Promise<Response> {
|
||||
const token = useAuthStore.getState().accessToken
|
||||
const headers = new Headers(init?.headers)
|
||||
if (token) headers.set('Authorization', `Bearer ${token}`)
|
||||
headers.set('X-Cameleer-Protocol-Version', '1')
|
||||
return fetch(url, { ...init, headers })
|
||||
return fetch(`${config.apiBaseUrl}${path}`, { ...init, headers })
|
||||
}
|
||||
|
||||
export function useAllApplicationConfigs() {
|
||||
return useQuery({
|
||||
queryKey: ['applicationConfig', 'all'],
|
||||
queryFn: async () => {
|
||||
const res = await authFetch('/api/v1/config')
|
||||
const res = await authFetch('/config')
|
||||
if (!res.ok) throw new Error('Failed to fetch configs')
|
||||
return res.json() as Promise<ApplicationConfig[]>
|
||||
},
|
||||
@@ -56,7 +57,7 @@ export function useApplicationConfig(application: string | undefined) {
|
||||
return useQuery({
|
||||
queryKey: ['applicationConfig', application],
|
||||
queryFn: async () => {
|
||||
const res = await authFetch(`/api/v1/config/${application}`)
|
||||
const res = await authFetch(`/config/${application}`)
|
||||
if (!res.ok) throw new Error('Failed to fetch config')
|
||||
return res.json() as Promise<ApplicationConfig>
|
||||
},
|
||||
@@ -73,7 +74,7 @@ export function useUpdateApplicationConfig() {
|
||||
const queryClient = useQueryClient()
|
||||
return useMutation({
|
||||
mutationFn: async (config: ApplicationConfig) => {
|
||||
const res = await authFetch(`/api/v1/config/${config.application}`, {
|
||||
const res = await authFetch(`/config/${config.application}`, {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(config),
|
||||
@@ -94,7 +95,7 @@ export function useProcessorRouteMapping(application?: string) {
|
||||
return useQuery({
|
||||
queryKey: ['config', application, 'processor-routes'],
|
||||
queryFn: async () => {
|
||||
const res = await authFetch(`/api/v1/config/${application}/processor-routes`)
|
||||
const res = await authFetch(`/config/${application}/processor-routes`)
|
||||
if (!res.ok) throw new Error('Failed to fetch processor-route mapping')
|
||||
return res.json() as Promise<Record<string, string>>
|
||||
},
|
||||
@@ -123,7 +124,7 @@ interface SendGroupCommandParams {
|
||||
export function useSendGroupCommand() {
|
||||
return useMutation({
|
||||
mutationFn: async ({ group, type, payload }: SendGroupCommandParams) => {
|
||||
const res = await authFetch(`/api/v1/agents/groups/${encodeURIComponent(group)}/commands`, {
|
||||
const res = await authFetch(`/agents/groups/${encodeURIComponent(group)}/commands`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ type, payload }),
|
||||
@@ -152,7 +153,7 @@ export function useTestExpression() {
|
||||
target: string
|
||||
}) => {
|
||||
const res = await authFetch(
|
||||
`/api/v1/config/${encodeURIComponent(application)}/test-expression`,
|
||||
`/config/${encodeURIComponent(application)}/test-expression`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
@@ -178,7 +179,7 @@ export function useSendRouteCommand() {
|
||||
action: 'start' | 'stop' | 'suspend' | 'resume'
|
||||
routeId: string
|
||||
}) => {
|
||||
const res = await authFetch(`/api/v1/agents/groups/${encodeURIComponent(application)}/commands`, {
|
||||
const res = await authFetch(`/agents/groups/${encodeURIComponent(application)}/commands`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ type: 'route-control', payload: { routeId, action, nonce: crypto.randomUUID() } }),
|
||||
@@ -212,7 +213,7 @@ export function useReplayExchange() {
|
||||
body: string
|
||||
originalExchangeId?: string
|
||||
}): Promise<ReplayResult> => {
|
||||
const res = await authFetch(`/api/v1/agents/${encodeURIComponent(agentId)}/replay`, {
|
||||
const res = await authFetch(`/agents/${encodeURIComponent(agentId)}/replay`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ routeId, body, headers: headers ?? {}, originalExchangeId }),
|
||||
|
||||
@@ -6,8 +6,14 @@ declare global {
|
||||
}
|
||||
}
|
||||
|
||||
/** Base path from <base href="..."> tag, or '/' if none. Always ends with '/'. */
|
||||
const basePath = document.querySelector('base')?.getAttribute('href') ?? '/';
|
||||
|
||||
export const config = {
|
||||
get apiBaseUrl(): string {
|
||||
return window.__CAMELEER_CONFIG__?.apiBaseUrl ?? '/api/v1';
|
||||
return window.__CAMELEER_CONFIG__?.apiBaseUrl ?? `${basePath}api/v1`;
|
||||
},
|
||||
get basePath(): string {
|
||||
return basePath;
|
||||
},
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user