import type { AdminLoginLogsResponse, AdminLoginResponse, AdminManagedUserResponse, AdminMqttStatus, AdminRuntimeSettingsPayload, AdminRuntimeSettingsResponse, AdminUsersResponse, BlockingRuleResponse, DiscardDetails, ForbiddenWordBlockingRule, ForbiddenWordBlockingRulePayload, HealthStatus, HelpContentResponse, HelpPreviewResponse, IPBlockingRule, IPBlockingRulePayload, ListResponse, MapBoundsQuery, MapReport, MapTileSource, MapTileSourcePayload, MapTileSourceResponse, MapViewportResponse, MQTTForwarder, MQTTForwarderPayload, MQTTForwardMutationResponse, MQTTForwardStatusResponse, MQTTForwardTopic, MQTTForwardTopicPayload, NodeBlockingRule, NodeBlockingRulePayload, NodeInfo, PositionRecord, PublicMapTileSourceResponse, PublicMapTileSourcesResponse, TelemetryRecord, TextMessage, } from './types' async function requestJSON(path: string, init?: RequestInit): Promise { const response = await fetch(path, { credentials: 'same-origin', ...init }) if (!response.ok) { let message = `${response.status} ${response.statusText}` try { const data = (await response.json()) as { error?: string } if (data.error) { message = data.error } } catch { // Keep the HTTP status message when the response is not JSON. } throw new Error(message) } return response.json() as Promise } function listPath(path: string, limit: number, offset: number, nodeId = ''): string { const params = new URLSearchParams({ limit: String(limit), offset: String(offset) }) if (nodeId) { params.set('node_id', nodeId) } return `${path}?${params.toString()}` } function getJSON(path: string): Promise { return requestJSON(path) } function postJSON(path: string, body?: unknown): Promise { return requestJSON(path, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: body == null ? undefined : JSON.stringify(body), }) } function putJSON(path: string, body?: unknown): Promise { return requestJSON(path, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: body == null ? undefined : JSON.stringify(body), }) } function deleteJSON(path: string): Promise { return requestJSON(path, { method: 'DELETE' }) } export function getHealth(): Promise { return getJSON('/api/health') } export function getHelpContent(): Promise { return getJSON('/api/help') } export function getNodeInfo(limit = 500, offset = 0): Promise> { return getJSON>(listPath('/api/nodeinfo', limit, offset)) } export function getNodeInfoById(nodeId: string): Promise { return getJSON(`/api/nodeinfo/${encodeURIComponent(nodeId)}`) } export function getMapReports(limit = 500, offset = 0, bounds?: MapBoundsQuery): Promise> { const params = new URLSearchParams({ limit: String(limit), offset: String(offset) }) if (bounds) { params.set('min_lat', String(bounds.min_lat)) params.set('max_lat', String(bounds.max_lat)) params.set('min_lng', String(bounds.min_lng)) params.set('max_lng', String(bounds.max_lng)) } return getJSON>(`/api/map-reports?${params.toString()}`) } export function getMapReportById(nodeId: string): Promise { return getJSON(`/api/map-reports/${encodeURIComponent(nodeId)}`) } export function getMapReportViewport(bounds: MapBoundsQuery, zoom: number, limit = 1000): Promise { const params = new URLSearchParams({ min_lat: String(bounds.min_lat), max_lat: String(bounds.max_lat), min_lng: String(bounds.min_lng), max_lng: String(bounds.max_lng), zoom: String(zoom), limit: String(limit), }) return getJSON(`/api/map-reports/viewport?${params.toString()}`) } export function getDefaultMapSource(): Promise { return getJSON('/api/map-source/default') } export function getEnabledMapSources(): Promise { return getJSON('/api/map-source/enabled') } export function getTextMessages(limit = 100, offset = 0, nodeId = ''): Promise> { return getJSON>(listPath('/api/text-messages', limit, offset, nodeId)) } export function deleteTextMessage(id: number): Promise<{ status: string }> { return deleteJSON<{ status: string }>(`/api/admin/text-messages/${id}`) } export function deleteNode(nodeId: string): Promise<{ status: string }> { return deleteJSON<{ status: string }>(`/api/admin/nodes/${encodeURIComponent(nodeId)}`) } export function getPositions(limit = 500, offset = 0, nodeId = ''): Promise> { return getJSON>(listPath('/api/positions', limit, offset, nodeId)) } export function getDiscardDetails(limit = 100, offset = 0): Promise> { return getJSON>(listPath('/api/discard-details', limit, offset)) } export function getTelemetry(limit = 500, offset = 0, nodeId = ''): Promise> { return getJSON>(listPath('/api/telemetry', limit, offset, nodeId)) } export function adminLogin(username: string, password: string): Promise { return postJSON('/api/admin/login', { username, password }) } export function adminLogout(): Promise<{ status: string }> { return postJSON<{ status: string }>('/api/admin/logout') } export function getAdminMe(): Promise { return getJSON('/api/admin/me') } export function getAdminMqttStatus(): Promise { return getJSON('/api/admin/mqtt/status') } export function getAdminRuntimeSettings(): Promise { return getJSON('/api/admin/runtime-settings') } export function updateAdminRuntimeSettings(payload: AdminRuntimeSettingsPayload): Promise { return putJSON('/api/admin/runtime-settings', payload) } export function getAdminHelpContent(): Promise { return getJSON('/api/admin/help') } export function saveAdminHelpContent(markdown: string): Promise { return postJSON('/api/admin/help', { markdown }) } export function previewAdminHelpContent(markdown: string): Promise { return postJSON('/api/admin/help/preview', { markdown }) } export function getAdminUsers(): Promise { return getJSON('/api/admin/users') } export function createAdminUser(username: string, password: string): Promise { return postJSON('/api/admin/users', { username, password }) } export function updateAdminUserPassword(id: number, password: string): Promise { return putJSON(`/api/admin/users/${id}/password`, { password }) } export function getAdminLoginLogs(limit = 100, offset = 0): Promise { return getJSON(`/api/admin/log/login?limit=${limit}&offset=${offset}`) } export function getAdminMapSources(limit = 100, offset = 0): Promise> { return getJSON>(listPath('/api/admin/map-source', limit, offset)) } export function createAdminMapSource(payload: MapTileSourcePayload): Promise { return postJSON('/api/admin/map-source', payload) } export function updateAdminMapSource(id: number, payload: MapTileSourcePayload): Promise { return putJSON(`/api/admin/map-source/${id}`, payload) } export function deleteAdminMapSource(id: number): Promise<{ status: string }> { return deleteJSON<{ status: string }>(`/api/admin/map-source/${id}`) } export function setDefaultAdminMapSource(id: number): Promise { return postJSON(`/api/admin/map-source/${id}/default`) } export function getNodeBlockingRules(limit = 100, offset = 0): Promise> { return getJSON>(listPath('/api/admin/blocking/nodes', limit, offset)) } export function createNodeBlockingRule(payload: NodeBlockingRulePayload): Promise> { return postJSON>('/api/admin/blocking/nodes', payload) } export function updateNodeBlockingRule(id: number, payload: NodeBlockingRulePayload): Promise> { return putJSON>(`/api/admin/blocking/nodes/${id}`, payload) } export function deleteNodeBlockingRule(id: number): Promise<{ status: string }> { return deleteJSON<{ status: string }>(`/api/admin/blocking/nodes/${id}`) } export function getIPBlockingRules(limit = 100, offset = 0): Promise> { return getJSON>(listPath('/api/admin/blocking/ips', limit, offset)) } export function createIPBlockingRule(payload: IPBlockingRulePayload): Promise> { return postJSON>('/api/admin/blocking/ips', payload) } export function updateIPBlockingRule(id: number, payload: IPBlockingRulePayload): Promise> { return putJSON>(`/api/admin/blocking/ips/${id}`, payload) } export function deleteIPBlockingRule(id: number): Promise<{ status: string }> { return deleteJSON<{ status: string }>(`/api/admin/blocking/ips/${id}`) } export function getForbiddenWordBlockingRules(limit = 100, offset = 0): Promise> { return getJSON>(listPath('/api/admin/blocking/words', limit, offset)) } export function createForbiddenWordBlockingRule(payload: ForbiddenWordBlockingRulePayload): Promise> { return postJSON>('/api/admin/blocking/words', payload) } export function updateForbiddenWordBlockingRule(id: number, payload: ForbiddenWordBlockingRulePayload): Promise> { return putJSON>(`/api/admin/blocking/words/${id}`, payload) } export function deleteForbiddenWordBlockingRule(id: number): Promise<{ status: string }> { return deleteJSON<{ status: string }>(`/api/admin/blocking/words/${id}`) } export function getMQTTForwarders(limit = 100, offset = 0): Promise> { return getJSON>(listPath('/api/admin/mqtt-forward/forwarders', limit, offset)) } export function createMQTTForwarder(payload: MQTTForwarderPayload): Promise> { return postJSON>('/api/admin/mqtt-forward/forwarders', payload) } export function updateMQTTForwarder(id: number, payload: MQTTForwarderPayload): Promise> { return putJSON>(`/api/admin/mqtt-forward/forwarders/${id}`, payload) } export function deleteMQTTForwarder(id: number): Promise<{ status: string }> { return deleteJSON<{ status: string }>(`/api/admin/mqtt-forward/forwarders/${id}`) } export function restartMQTTForwarder(id: number): Promise<{ status: string }> { return postJSON<{ status: string }>(`/api/admin/mqtt-forward/forwarders/${id}/restart`) } export function getMQTTForwardTopics(forwarderId: number, limit = 100, offset = 0): Promise> { return getJSON>(listPath(`/api/admin/mqtt-forward/forwarders/${forwarderId}/topics`, limit, offset)) } export function createMQTTForwardTopic(forwarderId: number, payload: MQTTForwardTopicPayload): Promise> { return postJSON>(`/api/admin/mqtt-forward/forwarders/${forwarderId}/topics`, payload) } export function updateMQTTForwardTopic(id: number, payload: MQTTForwardTopicPayload): Promise> { return putJSON>(`/api/admin/mqtt-forward/topics/${id}`, payload) } export function deleteMQTTForwardTopic(id: number): Promise<{ status: string }> { return deleteJSON<{ status: string }>(`/api/admin/mqtt-forward/topics/${id}`) } export function getMQTTForwardStatus(): Promise { return getJSON('/api/admin/mqtt-forward/status') }