70 lines
2.2 KiB
JavaScript
70 lines
2.2 KiB
JavaScript
/**
|
|
* Thin REST client for the local backend.
|
|
* The page never talks to the robot directly - only to this origin.
|
|
*/
|
|
|
|
class ApiError extends Error {
|
|
constructor(message, code, status) {
|
|
super(message);
|
|
this.name = 'ApiError';
|
|
this.code = code || 'error';
|
|
this.status = status || 0;
|
|
}
|
|
}
|
|
|
|
async function request(path, options = {}) {
|
|
let response;
|
|
try {
|
|
response = await fetch(path, {
|
|
headers: { 'Content-Type': 'application/json' },
|
|
...options,
|
|
});
|
|
} catch (err) {
|
|
// The backend itself is unreachable - a different failure from "robot offline".
|
|
throw new ApiError(
|
|
'Cannot reach the local server. Is backend/main.py still running?',
|
|
'backend_down',
|
|
0,
|
|
);
|
|
}
|
|
|
|
let body = null;
|
|
const text = await response.text();
|
|
if (text) {
|
|
try { body = JSON.parse(text); } catch { body = { error: text }; }
|
|
}
|
|
|
|
if (!response.ok) {
|
|
const message =
|
|
(body && (body.error || body.detail)) ||
|
|
`Request failed (HTTP ${response.status})`;
|
|
throw new ApiError(
|
|
typeof message === 'string' ? message : JSON.stringify(message),
|
|
(body && body.errorCode) || 'http_error',
|
|
response.status,
|
|
);
|
|
}
|
|
return body;
|
|
}
|
|
|
|
export const api = {
|
|
status: () => request('/api/robot/status'),
|
|
config: () => request('/api/config'),
|
|
diagnostics: () => request('/api/robot/diagnostics'),
|
|
reloadConfig: () => request('/api/config/reload', { method: 'POST' }),
|
|
reconnect: () => request('/api/robot/reconnect', { method: 'POST' }),
|
|
speak: (text) => request('/api/robot/speak', {
|
|
method: 'POST',
|
|
body: JSON.stringify({ text }),
|
|
}),
|
|
stop: () => request('/api/robot/stop', { method: 'POST' }),
|
|
history: () => request('/api/speech/history'),
|
|
clearHistory: () => request('/api/speech/history', { method: 'DELETE' }),
|
|
audio: () => request('/api/audio'),
|
|
audioUrl: (id) => `/api/audio/${id}/file`,
|
|
deleteAudio: (id) => request(`/api/audio/${id}`, { method: 'DELETE' }),
|
|
clearAudio: () => request('/api/audio', { method: 'DELETE' }),
|
|
};
|
|
|
|
export { ApiError };
|