const LOGIN_KEY = "iron_access_token"; const loginCard = document.querySelector("#login-card"); const appCard = document.querySelector("#app-card"); const loginForm = document.querySelector("#login-form"); const loginError = document.querySelector("#login-error"); const appError = document.querySelector("#app-error"); const usernameInput = document.querySelector("#username"); const passwordInput = document.querySelector("#password"); const welcomeTitle = document.querySelector("#welcome-title"); const sessionUser = document.querySelector("#session-user"); const rootName = document.querySelector("#root-name"); const itemsCount = document.querySelector("#items-count"); const rootItems = document.querySelector("#root-items"); const backendCount = document.querySelector("#backend-count"); const backendsList = document.querySelector("#backends-list"); const refreshButton = document.querySelector("#refresh-button"); const logoutButton = document.querySelector("#logout-button"); function getToken() { return window.localStorage.getItem(LOGIN_KEY); } function setToken(token) { window.localStorage.setItem(LOGIN_KEY, token); } function clearToken() { window.localStorage.removeItem(LOGIN_KEY); } async function apiFetch(path, options = {}) { const headers = new Headers(options.headers || {}); const token = getToken(); if (token) { headers.set("Authorization", `Bearer ${token}`); } const response = await fetch(path, { ...options, headers }); if (!response.ok) { let message = `Request failed with status ${response.status}`; try { const payload = await response.json(); message = payload.error?.message || message; } catch { // keep fallback message } throw new Error(message); } return response; } function renderList(container, items, renderItem) { container.innerHTML = ""; if (items.length === 0) { const empty = document.createElement("li"); empty.textContent = "Nothing here yet."; container.append(empty); return; } for (const item of items) { container.append(renderItem(item)); } } function renderRootItem(item) { const element = document.createElement("li"); const left = document.createElement("div"); const right = document.createElement("div"); right.className = "item-meta"; left.innerHTML = `${item.name}`; if (item.kind === "file" && item.mime_type) { right.textContent = item.mime_type; } else { right.textContent = item.kind; } element.append(left, right); return element; } function renderBackendItem(item) { const element = document.createElement("li"); const left = document.createElement("div"); left.innerHTML = `${item.name}`; const right = document.createElement("div"); right.className = "item-meta"; right.textContent = `${item.type} ยท ${item.is_enabled ? "enabled" : "disabled"}`; element.append(left, right); return element; } function showLogin(errorMessage = "") { loginCard.hidden = false; appCard.hidden = true; loginError.hidden = !errorMessage; loginError.textContent = errorMessage; } function showApp() { loginCard.hidden = true; appCard.hidden = false; appError.hidden = true; } async function loadWorkspace() { try { const [meResponse, directoryResponse, backendResponse] = await Promise.all([ apiFetch("/api/auth/me"), apiFetch("/api/directories/dir_root/children"), apiFetch("/api/backends"), ]); const me = await meResponse.json(); const directory = await directoryResponse.json(); const backends = await backendResponse.json(); welcomeTitle.textContent = `Welcome, ${me.user.username}`; sessionUser.textContent = me.user.username; rootName.textContent = directory.directory.name; itemsCount.textContent = `${directory.items.length} items`; backendCount.textContent = String(backends.items.length); renderList(rootItems, directory.items, renderRootItem); renderList(backendsList, backends.items, renderBackendItem); showApp(); } catch (error) { if (String(error.message).includes("Authentication")) { clearToken(); showLogin("Your session expired. Please sign in again."); return; } appError.hidden = false; appError.textContent = error.message; } } loginForm.addEventListener("submit", async (event) => { event.preventDefault(); loginError.hidden = true; try { const response = await fetch("/api/auth/login", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ username: usernameInput.value, password: passwordInput.value, }), }); const payload = await response.json(); if (!response.ok) { throw new Error(payload.error?.message || "Unable to sign in."); } setToken(payload.access_token); passwordInput.value = ""; await loadWorkspace(); } catch (error) { showLogin(error.message); } }); refreshButton.addEventListener("click", async () => { await loadWorkspace(); }); logoutButton.addEventListener("click", async () => { try { await apiFetch("/api/auth/logout", { method: "POST" }); } catch { // best-effort logout } clearToken(); showLogin(); }); if (getToken()) { void loadWorkspace(); } else { showLogin(); }