120 lines
2.9 KiB
JavaScript
120 lines
2.9 KiB
JavaScript
|
|
import { API_PATHS } from '../app/config.js';
|
||
|
|
|
||
|
|
function normalizeBaseUrl(baseUrl) {
|
||
|
|
return baseUrl.trim().replace(/\/+$/, '');
|
||
|
|
}
|
||
|
|
|
||
|
|
function buildUrl({ baseUrl, database, kitchenId, path, query = {}, includeKitchen = true }) {
|
||
|
|
const cleanBaseUrl = normalizeBaseUrl(baseUrl);
|
||
|
|
const encodedDatabase = encodeURIComponent(database);
|
||
|
|
const encodedPath = path.replace(/^\/+/, '');
|
||
|
|
const kitchenSegment =
|
||
|
|
includeKitchen && kitchenId
|
||
|
|
? `/kitchen/${encodeURIComponent(String(kitchenId))}`
|
||
|
|
: '';
|
||
|
|
|
||
|
|
const url = new URL(
|
||
|
|
`${cleanBaseUrl}/${encodedDatabase}${kitchenSegment}/${encodedPath}`,
|
||
|
|
);
|
||
|
|
|
||
|
|
Object.entries(query).forEach(([key, value]) => {
|
||
|
|
if (value !== undefined && value !== null && value !== '') {
|
||
|
|
url.searchParams.set(key, String(value));
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
return url.toString();
|
||
|
|
}
|
||
|
|
|
||
|
|
async function parseResponse(response) {
|
||
|
|
const contentType = response.headers.get('content-type') || '';
|
||
|
|
|
||
|
|
if (contentType.includes('application/json')) {
|
||
|
|
return response.json();
|
||
|
|
}
|
||
|
|
|
||
|
|
if (contentType.includes('image/')) {
|
||
|
|
return response.blob();
|
||
|
|
}
|
||
|
|
|
||
|
|
if (response.status === 204) {
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
|
||
|
|
return response.text();
|
||
|
|
}
|
||
|
|
|
||
|
|
function normalizeError(response, payload) {
|
||
|
|
const message =
|
||
|
|
payload?.message ||
|
||
|
|
payload?.error ||
|
||
|
|
`Request failed with status ${response.status}.`;
|
||
|
|
|
||
|
|
return new Error(message, {
|
||
|
|
cause: {
|
||
|
|
status: response.status,
|
||
|
|
details: payload?.errors || payload?.details || null,
|
||
|
|
},
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
export async function apiRequest(store, path, options = {}) {
|
||
|
|
const { config, session, activeKitchen } = store;
|
||
|
|
|
||
|
|
if (!config.baseUrl || !config.database) {
|
||
|
|
throw new Error('Server URL and database name are required.');
|
||
|
|
}
|
||
|
|
|
||
|
|
const headers = new Headers(options.headers || {});
|
||
|
|
headers.set('Accept', options.accept || 'application/json');
|
||
|
|
|
||
|
|
if (options.body && !options.isFormData) {
|
||
|
|
headers.set('Content-Type', 'application/json');
|
||
|
|
}
|
||
|
|
|
||
|
|
if (session?.applicationKey) {
|
||
|
|
headers.set('Authorization', `Bearer ${session.applicationKey}`);
|
||
|
|
}
|
||
|
|
|
||
|
|
const response = await fetch(
|
||
|
|
buildUrl({
|
||
|
|
baseUrl: config.baseUrl,
|
||
|
|
database: config.database,
|
||
|
|
kitchenId: activeKitchen?.id,
|
||
|
|
path,
|
||
|
|
query: options.query,
|
||
|
|
includeKitchen: options.includeKitchen !== false,
|
||
|
|
}),
|
||
|
|
{
|
||
|
|
method: options.method || 'GET',
|
||
|
|
headers,
|
||
|
|
body:
|
||
|
|
options.body && !options.isFormData
|
||
|
|
? JSON.stringify(options.body)
|
||
|
|
: options.body || undefined,
|
||
|
|
},
|
||
|
|
);
|
||
|
|
|
||
|
|
const payload = await parseResponse(response);
|
||
|
|
if (!response.ok) {
|
||
|
|
throw normalizeError(response, payload);
|
||
|
|
}
|
||
|
|
|
||
|
|
return payload;
|
||
|
|
}
|
||
|
|
|
||
|
|
export function getPath(key) {
|
||
|
|
return API_PATHS[key];
|
||
|
|
}
|
||
|
|
|
||
|
|
export function buildKitchenApiUrl(store, path, query = {}) {
|
||
|
|
return buildUrl({
|
||
|
|
baseUrl: store.config.baseUrl,
|
||
|
|
database: store.config.database,
|
||
|
|
kitchenId: store.activeKitchen?.id,
|
||
|
|
path,
|
||
|
|
query,
|
||
|
|
includeKitchen: true,
|
||
|
|
});
|
||
|
|
}
|