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();
}