// In production (embedded in Go binary), the frontend is served from the same origin as the API. // In dev mode, Next.js runs on :3000 and Go on :3721, so we need explicit addresses. // When deployed to cloud (e.g. AWS), the user can set a custom backend URL in Settings. const isDev = process.env.NODE_ENV === 'development'; const BACKEND_URL_STORAGE_KEY = 'edge-ai-backend-url'; const RELAY_TOKEN_STORAGE_KEY = 'edge-ai-relay-token'; function getStoredBackendUrl(): string { if (typeof window === 'undefined') return ''; return localStorage.getItem(BACKEND_URL_STORAGE_KEY) || ''; } export function setBackendUrl(url: string): void { if (typeof window === 'undefined') return; if (url) { localStorage.setItem(BACKEND_URL_STORAGE_KEY, url.replace(/\/+$/, '')); } else { localStorage.removeItem(BACKEND_URL_STORAGE_KEY); } } export function getBackendUrl(): string { if (isDev) return 'http://localhost:3721'; return getStoredBackendUrl(); } export function getApiBaseUrl(): string { // In dev mode, use relative path so Next.js rewrite proxy handles it if (isDev) return '/api'; const backend = getStoredBackendUrl(); return backend ? `${backend}/api` : '/api'; } export function getWsBaseUrl(): string { if (typeof window === 'undefined') return ''; if (isDev) return 'ws://localhost:3721'; const backend = getStoredBackendUrl(); if (backend) { return backend.replace(/^http/, 'ws'); } const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; return `${protocol}//${window.location.host}`; } // --- Relay Token --- export function getRelayToken(): string { if (typeof window === 'undefined') return ''; return localStorage.getItem(RELAY_TOKEN_STORAGE_KEY) || ''; } export function setRelayToken(token: string): void { if (typeof window === 'undefined') return; if (token) { localStorage.setItem(RELAY_TOKEN_STORAGE_KEY, token); } else { localStorage.removeItem(RELAY_TOKEN_STORAGE_KEY); } } // Check URL query params for a relay token (e.g. ?token=abc123). // If found, cache it in localStorage and remove the param from the URL. // This is the primary mechanism: local server opens the browser with the // token embedded in the URL, so no cross-origin fetch is needed. export function syncRelayTokenFromURL(): string { if (typeof window === 'undefined') return ''; const params = new URLSearchParams(window.location.search); const token = params.get('token'); if (token) { setRelayToken(token); // Clean up URL — remove token param without reload params.delete('token'); const newSearch = params.toString(); const newUrl = window.location.pathname + (newSearch ? `?${newSearch}` : '') + window.location.hash; window.history.replaceState({}, '', newUrl); return token; } return getRelayToken(); } // Kept for backward compatibility — now simply returns cached token. export async function fetchAndCacheRelayToken(): Promise { return syncRelayTokenFromURL(); } // Append relay token as query parameter to a URL. Used for resources loaded // via or other HTML elements that cannot send custom headers. export function appendRelayToken(url: string): string { const token = getRelayToken(); if (!token) return url; const sep = url.includes('?') ? '&' : '?'; return `${url}${sep}token=${encodeURIComponent(token)}`; } export const ROUTES = { HOME: '/', MODELS: '/models', MODEL_DETAIL: (id: string) => `/models/${id}`, DEVICES: '/devices', DEVICE_DETAIL: (id: string) => `/devices/${id}`, WORKSPACE: (deviceId: string) => `/workspace/${deviceId}`, } as const; export const TASK_TYPES = { object_detection: 'Object Detection', classification: 'Classification', } as const; export const HARDWARE_OPTIONS = ['KL520', 'KL720', 'KL730'] as const;