const BASE = "/api"; let token = localStorage.getItem("token") || ""; // Current user info populated after auth export let currentUser = { username: "", role: "", pages: [] }; export function setCurrentUser(u) { currentUser = u; } export function setToken(t) { token = t; if (t) localStorage.setItem("token", t); else localStorage.removeItem("token"); } export function getToken() { return token; } export function setRefreshToken(t) { if (t) localStorage.setItem("refresh_token", t); else localStorage.removeItem("refresh_token"); } function getRefreshToken() { return localStorage.getItem("refresh_token") || ""; } let refreshPromise = null; async function tryRefresh() { if (refreshPromise) return refreshPromise; const rt = getRefreshToken(); if (!rt) return false; refreshPromise = (async () => { try { const r = await fetch(BASE + "/auth/refresh", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ refresh_token: rt }), }); if (!r.ok) return false; const data = await r.json(); setToken(data.access_token); setRefreshToken(data.refresh_token); return true; } catch { return false; } finally { refreshPromise = null; } })(); return refreshPromise; } async function request(path, opts = {}) { const headers = opts.headers || {}; if (token) headers["Authorization"] = `Bearer ${token}`; if (opts.json) { headers["Content-Type"] = "application/json"; opts.body = JSON.stringify(opts.json); delete opts.json; } try { let r = await fetch(BASE + path, { ...opts, headers }); if (r.status === 401 && !path.includes("/auth/refresh")) { const refreshed = await tryRefresh(); if (refreshed) { headers["Authorization"] = `Bearer ${token}`; r = await fetch(BASE + path, { ...opts, headers }); } } if (r.status === 401) { setToken(""); setRefreshToken(""); throw new Error("Unauthorized"); } if (!r.ok) { const error = new Error(`HTTP ${r.status}`); error.status = r.status; try { error.body = await r.json(); } catch { } throw error; } return r.json(); } catch (e) { console.error(`API ${opts.method || "GET"} ${path}:`, e); throw e; } } export function get(path) { return request(path); } export function post(path, data) { return request(path, { method: "POST", json: data }); } export function del(path) { return request(path, { method: "DELETE" }); } export async function upload(path, file) { const headers = {}; if (token) headers["Authorization"] = `Bearer ${token}`; const form = new FormData(); form.append("file", file); const r = await fetch(`${BASE}/files/upload?path=${encodeURIComponent(path)}`, { method: "POST", headers, body: form, }); if (!r.ok) throw new Error(`HTTP ${r.status}`); return r.json(); } export function login(creds) { return request("/auth/login", { method: "POST", json: creds }); } export function checkAuth() { return request("/auth/me"); } export function getPreferences() { return get("/auth/preferences"); } export function savePreferences(data) { return request("/auth/preferences", { method: "PUT", json: data }); } export function getLiteLLMHealth() { return get("/litellm/health"); } export function getCcConnectHealth() { return get("/cc-connect/health"); } export function startCcConnect() { return post("/cc-connect/start"); } export function stopCcConnect() { return post("/cc-connect/stop"); } export function getInfoEngineItems(params = {}) { const query = new URLSearchParams(); if (params.limit !== undefined) query.set("limit", String(params.limit)); if (params.offset !== undefined) query.set("offset", String(params.offset)); if (params.source) query.set("source", params.source); if (params.tag) query.set("tag", params.tag); if (params.since) query.set("since", params.since); const suffix = query.toString() ? `?${query.toString()}` : ""; return get(`/info-engine/items${suffix}`); } export function getInfoEngineItem(id) { return get(`/info-engine/items/${id}`); } export function put(path, data) { return request(path, { method: "PUT", json: data }); }