From 1ed95ff52ad60a071bc47ab6731eb2068a832026 Mon Sep 17 00:00:00 2001 From: zgaetano Date: Tue, 31 Mar 2026 15:29:58 -0400 Subject: [PATCH] Add src/api.ts --- src/api.ts | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 src/api.ts diff --git a/src/api.ts b/src/api.ts new file mode 100644 index 0000000..7f659e4 --- /dev/null +++ b/src/api.ts @@ -0,0 +1,53 @@ +// 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( + method: string, + path: string, + body?: unknown +): Promise { + 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("GET", "/api/status"), + getHosts: () => request("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 }), +};