gc-wing/apps/web/src/entities/aisTarget/api/searchAisTargets.ts

51 lines
2.0 KiB
TypeScript

import type { AisTargetSearchResponse } from "../model/types";
export type SearchAisTargetsParams = {
minutes: number;
bbox?: string;
centerLon?: number;
centerLat?: number;
radiusMeters?: number;
};
export async function searchAisTargets(params: SearchAisTargetsParams, signal?: AbortSignal) {
// Same convention as the "dark" project:
// - dev: default to Vite proxy base `/snp-api`
// - prod/other: can be overridden via `VITE_API_URL` (e.g. `http://host:8041/snp-api`)
const base = (import.meta.env.VITE_API_URL || "/snp-api").replace(/\/$/, "");
const u = new URL(`${base}/api/ais-target/search`, window.location.origin);
u.searchParams.set("minutes", String(params.minutes));
if (params.bbox) u.searchParams.set("bbox", params.bbox);
if (typeof params.centerLon === "number" && Number.isFinite(params.centerLon)) {
u.searchParams.set("centerLon", String(params.centerLon));
}
if (typeof params.centerLat === "number" && Number.isFinite(params.centerLat)) {
u.searchParams.set("centerLat", String(params.centerLat));
}
if (typeof params.radiusMeters === "number" && Number.isFinite(params.radiusMeters)) {
u.searchParams.set("radiusMeters", String(params.radiusMeters));
}
const res = await fetch(u, { signal, headers: { accept: "application/json" } });
const txt = await res.text();
let json: unknown = null;
try {
json = JSON.parse(txt);
} catch {
// ignore
}
if (!res.ok) {
const msg =
json && typeof json === "object" && typeof (json as { message?: unknown }).message === "string"
? (json as { message: string }).message
: txt.slice(0, 200) || res.statusText;
throw new Error(`AIS target API failed: ${res.status} ${msg}`);
}
if (!json || typeof json !== "object") throw new Error("AIS target API returned invalid payload");
const parsed = json as AisTargetSearchResponse;
if (!parsed.success) throw new Error(parsed.message || "AIS target API returned success=false");
return parsed;
}