src/lib/api.js (view raw)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 |
import { STR } from './strings.js';
const API_BASE = import.meta.env.VITE_API_BASE ?? '';
function getPublicOrigin() {
const configured = import.meta.env.VITE_PUBLIC_ORIGIN?.trim();
if (configured) {
return configured.replace(/\/$/, '');
}
return window.location.origin;
}
export class ApiError extends Error {
constructor(status, code, message, payload = {}) {
super(message);
this.name = 'ApiError';
this.status = status;
this.code = code;
this.payload = payload;
}
}
const ERROR_MESSAGES = {
NOT_FOUND: STR.NOT_FOUND,
EXPIRED: STR.EXPIRED,
DOCUMENT_LOCKED: STR.DOCUMENT_LOCKED,
LOCK_REQUIRED: STR.LOCK_REQUIRED,
CONTENT_TOO_LARGE: STR.CONTENT_TOO_LARGE,
RATE_LIMIT: STR.RATE_LIMIT,
NETWORK: STR.NETWORK,
};
export function friendlyErrorMessage(error) {
if (error instanceof ApiError) {
return error.message || ERROR_MESSAGES[error.code] || STR.GENERIC_ERROR;
}
return ERROR_MESSAGES.NETWORK;
}
async function parseResponse(res) {
let data = null;
const text = await res.text();
if (text) {
try {
data = JSON.parse(text);
} catch {
data = { message: text };
}
}
if (!res.ok) {
const code = data?.error ?? 'UNKNOWN';
const message =
data?.message ?? ERROR_MESSAGES[code] ?? STR.UNEXPECTED_ERROR;
throw new ApiError(res.status, code, message, data ?? {});
}
return data;
}
async function request(path, options = {}) {
let res;
try {
res = await fetch(`${API_BASE}${path}`, {
...options,
headers: {
'Content-Type': 'application/json',
...(options.headers ?? {}),
},
});
} catch {
throw new ApiError(0, 'NETWORK', ERROR_MESSAGES.NETWORK);
}
return parseResponse(res);
}
export function createDocument(body) {
return request('/api/documents', {
method: 'POST',
body: JSON.stringify(body),
});
}
export function fetchViewDocument(token) {
return request(`/api/documents/view/${encodeURIComponent(token)}`);
}
export function fetchEditDocument(token) {
return request(`/api/documents/edit/${encodeURIComponent(token)}`);
}
export function acquireEditLock(token, clientId) {
return request(`/api/documents/edit/${encodeURIComponent(token)}/lock`, {
method: 'POST',
body: JSON.stringify({ clientId }),
});
}
export function refreshEditLock(token, clientId, lockToken) {
return request(
`/api/documents/edit/${encodeURIComponent(token)}/lock/refresh`,
{
method: 'POST',
body: JSON.stringify({ clientId, lockToken }),
},
);
}
export function releaseEditLock(token, clientId, lockToken) {
return request(`/api/documents/edit/${encodeURIComponent(token)}/lock`, {
method: 'DELETE',
body: JSON.stringify({ clientId, lockToken }),
});
}
export function updateDocument(token, body) {
return request(`/api/documents/edit/${encodeURIComponent(token)}`, {
method: 'PUT',
body: JSON.stringify(body),
});
}
export function toAbsoluteUrl(path) {
if (path.startsWith('http')) return path;
return `${getPublicOrigin()}${path}`;
}
|