This commit is contained in:
Andras Schmelczer 2026-02-02 21:56:35 +00:00
parent 2c613dc0d1
commit a677b9331f
28 changed files with 1647 additions and 1498 deletions

61
frontend/src/lib/api.ts Normal file
View file

@ -0,0 +1,61 @@
import type { FeatureMeta, FeatureFilters } from '../types';
const INITIAL_RETRY_MS = 1000;
const MAX_RETRY_MS = 10000;
export async function fetchWithRetry<T>(
url: string,
onSuccess: (data: T) => void,
signal: AbortSignal
): Promise<void> {
let delay = INITIAL_RETRY_MS;
while (!signal.aborted) {
try {
const res = await fetch(url, { signal });
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const json = await res.json();
onSuccess(json);
return;
} catch (err) {
if (signal.aborted) return;
console.error(`Failed to fetch ${url}, retrying in ${delay}ms:`, err);
await new Promise((resolve) => setTimeout(resolve, delay));
delay = Math.min(delay * 2, MAX_RETRY_MS);
}
}
}
export function getApiBaseUrl(): string {
if (process.env.NODE_ENV === 'production') {
return '';
}
const { pathname, href } = window.location;
const pathMatch = pathname.match(/^(\/proxy\/)(\d+)/);
if (pathMatch) {
return `${pathMatch[1]}8001`;
}
const hrefMatch = href.match(/(\/proxy\/)\d+/);
if (hrefMatch) {
return `${hrefMatch[1]}8001`;
}
return '';
}
export function buildFilterString(filters: FeatureFilters, features: FeatureMeta[]): string {
const entries = Object.entries(filters);
if (entries.length === 0) return '';
return entries
.map(([name, value]) => {
const meta = features.find((f) => f.name === name);
if (meta?.type === 'enum') {
return `${name}:${(value as string[]).join('|')}`;
}
const [min, max] = value as [number, number];
return `${name}:${min}:${max}`;
})
.join(',');
}