118 lines
3.2 KiB
JavaScript
118 lines
3.2 KiB
JavaScript
const API_KEY_STORAGE_KEY = "palico-api-key";
|
|
const DISPLAY_NAME_STORAGE_KEY = "palico-display-name";
|
|
|
|
export function getStoredApiKey() {
|
|
return localStorage.getItem(API_KEY_STORAGE_KEY) || "";
|
|
}
|
|
|
|
export function setStoredApiKey(value) {
|
|
localStorage.setItem(API_KEY_STORAGE_KEY, value || "");
|
|
}
|
|
|
|
export function getStoredDisplayName() {
|
|
return localStorage.getItem(DISPLAY_NAME_STORAGE_KEY) || "";
|
|
}
|
|
|
|
export function setStoredDisplayName(value) {
|
|
localStorage.setItem(DISPLAY_NAME_STORAGE_KEY, value || "");
|
|
}
|
|
|
|
export function createStatusManager(statusElement) {
|
|
let hideTimeout;
|
|
|
|
function setStatus(message, type = "info", autoHide = true) {
|
|
if (!statusElement) return;
|
|
statusElement.textContent = message;
|
|
statusElement.className = `status status--${type}`;
|
|
statusElement.hidden = false;
|
|
|
|
if (autoHide) {
|
|
clearTimeout(hideTimeout);
|
|
hideTimeout = setTimeout(() => {
|
|
statusElement.hidden = true;
|
|
}, 5000);
|
|
}
|
|
}
|
|
|
|
function clearStatus() {
|
|
if (!statusElement) return;
|
|
statusElement.hidden = true;
|
|
statusElement.textContent = "";
|
|
clearTimeout(hideTimeout);
|
|
}
|
|
|
|
return { setStatus, clearStatus };
|
|
}
|
|
|
|
export function escapeHtml(text = "") {
|
|
return text
|
|
.toString()
|
|
.replace(/&/g, "&")
|
|
.replace(/</g, "<")
|
|
.replace(/>/g, ">")
|
|
.replace(/"/g, """)
|
|
.replace(/'/g, "'");
|
|
}
|
|
|
|
export function formatBytes(bytes = 0) {
|
|
if (!Number(bytes)) return "0 B";
|
|
const units = ["B", "KB", "MB", "GB", "TB"];
|
|
const index = Math.floor(Math.log(bytes) / Math.log(1024));
|
|
return `${(bytes / Math.pow(1024, index)).toFixed(1)} ${units[index]}`;
|
|
}
|
|
|
|
export function formatDate(value) {
|
|
if (!value) return "?";
|
|
try {
|
|
return new Date(value).toLocaleString();
|
|
} catch (_) {
|
|
return value;
|
|
}
|
|
}
|
|
|
|
export function createApiClient({ getApiKey, basePath = "" } = {}) {
|
|
const normalizedBase = (basePath || "").replace(/\/$/, "");
|
|
|
|
return async function apiFetch(path, options = {}) {
|
|
const url = path.startsWith("http")
|
|
? path
|
|
: `${normalizedBase}${path.startsWith("/") ? "" : "/"}${path}`;
|
|
|
|
const fetchOptions = {
|
|
method: options.method || "GET",
|
|
headers: new Headers(options.headers || {}),
|
|
};
|
|
|
|
const apiKey = typeof getApiKey === "function" ? getApiKey() : null;
|
|
if (apiKey) {
|
|
fetchOptions.headers.set("Authorization", `Bearer ${apiKey}`);
|
|
}
|
|
|
|
if (options.body instanceof FormData) {
|
|
fetchOptions.body = options.body;
|
|
} else if (options.body) {
|
|
if (!fetchOptions.headers.has("Content-Type")) {
|
|
fetchOptions.headers.set("Content-Type", "application/json");
|
|
}
|
|
fetchOptions.body =
|
|
typeof options.body === "string" ? options.body : JSON.stringify(options.body);
|
|
}
|
|
|
|
const response = await fetch(url, fetchOptions);
|
|
const contentType = response.headers.get("content-type") || "";
|
|
let payload = null;
|
|
|
|
if (contentType.includes("application/json")) {
|
|
payload = await response.json();
|
|
} else {
|
|
payload = await response.text();
|
|
}
|
|
|
|
if (!response.ok) {
|
|
const errorMessage = payload?.error || payload?.message || response.statusText;
|
|
throw new Error(errorMessage);
|
|
}
|
|
|
|
return payload;
|
|
};
|
|
}
|