Files
Final-Year-Project/WebApp/src/lib/app/api.js

133 lines
4.3 KiB
JavaScript

/* eslint-disable @typescript-eslint/ban-ts-comment */
// @ts-nocheck
import { getAppState } from './store';
const rawBackendUrl = import.meta.env.VITE_BACKEND_URL ?? 'http://localhost:3000';
const backendUrl = rawBackendUrl.replace(/\/+$/, '');
const toBackendUrl = (path) => {
if (/^https?:\/\//i.test(path)) {
return path;
}
return `${backendUrl}${path.startsWith('/') ? path : `/${path}`}`;
};
const parseResponseBody = async (response) => {
const contentType = response.headers.get('content-type') || '';
if (contentType.includes('application/json')) {
return response.json().catch(() => ({}));
}
const text = await response.text().catch(() => '');
return text ? { message: text } : {};
};
const request = async (path, options = {}) => {
const { deviceToken } = getAppState();
const headers = { 'Content-Type': 'application/json' };
if (deviceToken) {
headers.Authorization = `Bearer ${deviceToken}`;
}
const response = await fetch(toBackendUrl(path), {
...options,
credentials: 'include',
headers: {
...headers,
...(options.headers || {})
}
});
const data = await response.json().catch(() => ({}));
if (!response.ok) {
throw new Error(data.message || data.error || response.statusText || 'Request failed');
}
return data;
};
const uploadBinary = async (path, body, options = {}) => {
const { deviceToken } = getAppState();
const headers = {};
if (deviceToken) {
headers.Authorization = `Bearer ${deviceToken}`;
}
if (options.contentType) {
headers['Content-Type'] = options.contentType;
}
const response = await fetch(toBackendUrl(path), {
method: options.method || 'PUT',
body,
credentials: 'include',
headers: {
...headers,
...(options.headers || {})
}
});
const data = await parseResponseBody(response);
if (!response.ok) {
throw new Error(data.message || data.error || response.statusText || 'Request failed');
}
return data;
};
export const getBackendUrl = () => backendUrl;
export const api = {
request,
auth: {
signUp: (data) => request('/api/auth/sign-up/email', { method: 'POST', body: JSON.stringify(data) }),
signIn: (data) => request('/api/auth/sign-in/email', { method: 'POST', body: JSON.stringify(data) }),
getSession: () => request('/api/auth/get-session'),
signOut: () => request('/api/auth/sign-out', { method: 'POST', body: JSON.stringify({}) })
},
devices: {
register: (data) => request('/devices/register', { method: 'POST', body: JSON.stringify(data) }),
list: () => request('/devices'),
update: (deviceId, data) => request(`/devices/${deviceId}`, { method: 'PATCH', body: JSON.stringify(data) }),
listLinks: () => request('/device-links'),
link: (cameraDeviceId, clientDeviceId) =>
request('/device-links', { method: 'POST', body: JSON.stringify({ cameraDeviceId, clientDeviceId }) }),
unlink: (linkId) => request(`/device-links/${linkId}`, { method: 'DELETE' })
},
streams: {
request: (cameraDeviceId) =>
request('/streams/request', {
method: 'POST',
body: JSON.stringify({ cameraDeviceId, reason: 'on_demand' })
}),
accept: (id) => request(`/streams/${id}/accept`, { method: 'POST', body: JSON.stringify({}) }),
end: (id) => request(`/streams/${id}/end`, { method: 'POST', body: JSON.stringify({ reason: 'completed' }) })
},
events: {
startMotion: (payload = {}) =>
request('/events/motion/start', {
method: 'POST',
body: JSON.stringify({ title: 'Simulated Motion', triggeredBy: 'motion', ...payload })
}),
endMotion: (id) => request(`/events/${id}/motion/end`, { method: 'POST', body: JSON.stringify({ status: 'completed' }) }),
finalizeRecording: (id, payload) => request(`/recordings/${id}/finalize`, { method: 'POST', body: JSON.stringify(payload) })
},
ops: {
getReadiness: () => request('/ops/ready'),
listRecordings: () => request('/recordings/me/list'),
getRecordingDownloadUrl: (recordingId) => request(`/recordings/${recordingId}/download-url`),
listNotifications: () => request('/push-notifications/me')
},
pushNotifications: {
markRead: (notificationId) => request(`/push-notifications/${notificationId}/read`, { method: 'POST', body: JSON.stringify({}) })
},
uploads: {
uploadRecordingBlob: (recordingId, blob, contentType = 'application/octet-stream') =>
uploadBinary(`/videos/upload/${recordingId}`, blob, { method: 'PUT', contentType })
}
};