Replaced `Link` components with `NavLink` for active state support and added new sidebar navigation styling. Enhanced API URL handling to prevent mixed content when using HTTPS. Updated layout and CSS for better responsiveness and consistent design.
46 lines
1.3 KiB
JavaScript
46 lines
1.3 KiB
JavaScript
function resolveApiUrl() {
|
|
const raw = (import.meta.env.VITE_API_URL || "").trim();
|
|
const fallback = "/api/v1";
|
|
if (!raw) return fallback;
|
|
|
|
try {
|
|
const parsed = new URL(raw, window.location.origin);
|
|
if (window.location.protocol === "https:" && parsed.protocol === "http:") {
|
|
// Avoid mixed-content when UI is served over HTTPS.
|
|
parsed.protocol = "https:";
|
|
}
|
|
return parsed.toString().replace(/\/$/, "");
|
|
} catch {
|
|
return fallback;
|
|
}
|
|
}
|
|
|
|
const API_URL = resolveApiUrl();
|
|
|
|
export async function apiFetch(path, options = {}, tokens, onUnauthorized) {
|
|
const headers = {
|
|
"Content-Type": "application/json",
|
|
...(options.headers || {}),
|
|
};
|
|
if (tokens?.accessToken) {
|
|
headers.Authorization = `Bearer ${tokens.accessToken}`;
|
|
}
|
|
|
|
let res = await fetch(`${API_URL}${path}`, { ...options, headers });
|
|
if (res.status === 401 && tokens?.refreshToken && onUnauthorized) {
|
|
const refreshed = await onUnauthorized();
|
|
if (refreshed) {
|
|
headers.Authorization = `Bearer ${refreshed.accessToken}`;
|
|
res = await fetch(`${API_URL}${path}`, { ...options, headers });
|
|
}
|
|
}
|
|
if (!res.ok) {
|
|
const txt = await res.text();
|
|
throw new Error(txt || `HTTP ${res.status}`);
|
|
}
|
|
if (res.status === 204) return null;
|
|
return res.json();
|
|
}
|
|
|
|
export { API_URL };
|