moonlight-relay-server/src/api.ts

54 lines
1.5 KiB
TypeScript
Raw Normal View History

2026-03-31 15:29:58 -04:00
// api.ts — typed client for the moonrelay REST API.
//
// In Docker / production mode the SPA is served by the same Go process,
// so we use a relative base URL (same origin, no CORS).
// In local Vite dev mode set VITE_API_BASE to point at the backend
// e.g. VITE_API_BASE=http://localhost:8080
const BASE = (import.meta.env.VITE_API_BASE as string | undefined) ?? "";
export interface AppStatus {
vpn_connected: boolean;
vpn_address: string;
moonlight_path: string;
}
export interface Host {
name: string;
ip: string;
port: number;
online: boolean;
seen_at: string;
}
async function request<T>(
method: string,
path: string,
body?: unknown
): Promise<T> {
const res = await fetch(`${BASE}${path}`, {
method,
headers: body ? { "Content-Type": "application/json" } : {},
body: body ? JSON.stringify(body) : undefined,
});
if (!res.ok) {
const err = await res.json().catch(() => ({ error: res.statusText }));
throw new Error(err.error ?? res.statusText);
}
return res.json();
}
export const api = {
getStatus: () => request<AppStatus>("GET", "/api/status"),
getHosts: () => request<Host[]>("GET", "/api/hosts"),
addManualHost: (name: string, ip: string, port = 47984) =>
request<{ ok: boolean }>("POST", "/api/hosts/manual", { name, ip, port }),
connect: (ip: string, app = "Desktop") =>
request<{ ok: boolean }>("POST", "/api/connect", { ip, app }),
pair: (ip: string) =>
request<{ ok: boolean }>("POST", "/api/pair", { ip }),
};