const BASE = "/api"; let 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; } // Refresh token is cookie-managed server-side. export function setRefreshToken() {} let refreshPromise = null; function getLegacyRefreshToken() { return localStorage.getItem("refresh_token") || ""; } function clearLegacyRefreshToken() { localStorage.removeItem("refresh_token"); } async function requestRefresh(body) { const r = await fetch(BASE + "/auth/refresh", { method: "POST", headers: { "Content-Type": "application/json" }, credentials: "same-origin", body: body ? JSON.stringify(body) : undefined, }); if (!r.ok) return false; const data = await r.json(); setToken(data.access_token || ""); return !!data.access_token; } export async function tryRefreshSession() { if (refreshPromise) return refreshPromise; refreshPromise = (async () => { try { const cookieRefreshed = await requestRefresh(); if (cookieRefreshed) { clearLegacyRefreshToken(); return true; } const legacyRefreshToken = getLegacyRefreshToken(); if (!legacyRefreshToken) return false; const legacyRefreshed = await requestRefresh({ refresh_token: legacyRefreshToken }); if (legacyRefreshed) { clearLegacyRefreshToken(); return true; } clearLegacyRefreshToken(); return false; } 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; } const fetchOpts = { ...opts, headers }; if (path.startsWith("/auth/")) fetchOpts.credentials = "same-origin"; try { let r = await fetch(BASE + path, fetchOpts); if (r.status === 401 && !path.includes("/auth/refresh")) { const refreshed = await tryRefreshSession(); if (refreshed) { headers["Authorization"] = `Bearer ${token}`; r = await fetch(BASE + path, fetchOpts); } } if (r.status === 401) { setToken(""); 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 logout() { return request("/auth/logout", { method: "POST" }); } 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 }); }