AkurAI Build
Menu

Public repository / e1dca3a75db23be353f8e6445134eba8af6e0797

AkurAI-Platform / frontend/portal.js

Latest change 6401b9edc6e7367bac167df29ad7cbad46e44b68 - fix: verify managed platform lifecycle and portal (AKURAI-PLATFORM-10, AKURAI-PLATFORM-11, AKURAI-PLATFORM-12, AKURAI-PLATFORM-13, AKURAI-PLATFORM-14, AKURAI-PLATFORM-16, AKURAI-PLATFORM-18) by Ólafur Búi Ólafsson

const portal = document.querySelector("[data-control-plane]");

if (portal) {
  const state = portal.querySelector("[data-portal-state]");
  const organizations = portal.querySelector("[data-portal-organizations]");
  const refresh = portal.querySelector("[data-portal-refresh]");

  function setState(message, tone = "") {
    state.textContent = message;
    state.dataset.tone = tone;
  }

  function text(value) {
    return String(value ?? "—");
  }

  function card(label, value) {
    const item = document.createElement("li");
    item.innerHTML = `<span>${label}</span><strong></strong>`;
    item.querySelector("strong").textContent = text(value);
    return item;
  }

  async function api(path, init) {
    const response = await fetch(path, { credentials: "same-origin", headers: { accept: "application/json", ...(init?.headers ?? {}) }, ...init });
    if (!response.ok) throw Object.assign(new Error("request_failed"), { status: response.status });
    return response.json();
  }

  async function loadOrganization(id, root) {
    setState("Loading organization control plane…");
    try {
      const [organization, members, entitlements, installations, domains, backups, invoices, audit, subscription, deliveries] = await Promise.all([
        api(`/api/organizations/${encodeURIComponent(id)}`),
        api(`/api/organizations/${encodeURIComponent(id)}/members`),
        api(`/api/organizations/${encodeURIComponent(id)}/entitlements`),
        api(`/api/organizations/${encodeURIComponent(id)}/installations`),
        api(`/api/organizations/${encodeURIComponent(id)}/domains`),
        api(`/api/organizations/${encodeURIComponent(id)}/backups`),
        api(`/api/organizations/${encodeURIComponent(id)}/invoices`),
        api(`/api/organizations/${encodeURIComponent(id)}/audit`),
        api(`/api/organizations/${encodeURIComponent(id)}/subscription-management`),
        api(`/api/organizations/${encodeURIComponent(id)}/subscription-management/deliveries`),
      ]);
      const facts = root.querySelector("[data-portal-facts]");
      facts.replaceChildren(
        card("Role", organization.role),
        card("Plan", subscription.status),
        card("Members", members.memberships.length),
        card("Entitlements", entitlements.entitlements.length),
        card("Deployment health", `${installations.installations.filter((item) => item.status === "installed").length}/${installations.installations.length} installed`),
        card("Domains", domains.domains.length),
        card("Verified backups", backups.backups.filter((item) => item.status === "verified").length),
        card("Invoices", invoices.invoices.length),
        card("Immutable audit events", audit.lifecycle.length + audit.payments.length),
        card("Recovery deliveries", deliveries.deliveries.length),
      );
      root.querySelector("[data-portal-recovery]").hidden = false;
      setState("Organization control plane loaded.", "ok");
    } catch (error) {
      setState(error.status === 401 ? "Sign in with AkurAI ID to load your organizations." : "The organization control plane is temporarily unavailable.", "error");
    }
  }

  async function load() {
    organizations.replaceChildren();
    setState("Loading your organizations…");
    try {
      const result = await api("/api/organizations");
      if (!result.organizations.length) {
        setState("No organizations are linked to this AkurAI ID yet.");
        return;
      }
      for (const organization of result.organizations) {
        const root = document.createElement("article");
        root.className = "portal-organization";
        const title = document.createElement("h3");
        title.textContent = organization.name;
        const open = document.createElement("button");
        open.className = "btn btn-secondary";
        open.type = "button";
        open.textContent = "Open control plane";
        const facts = document.createElement("ul");
        facts.className = "portal-facts";
        facts.dataset.portalFacts = "";
        const recovery = document.createElement("button");
        recovery.className = "btn btn-tertiary";
        recovery.type = "button";
        recovery.hidden = true;
        recovery.dataset.portalRecovery = "";
        recovery.textContent = "Send a new management link to the verified customer email";
        recovery.addEventListener("click", async () => {
          recovery.disabled = true;
          try {
            await api(`/api/organizations/${encodeURIComponent(organization.organizationId)}/subscription-management/recovery`, { method: "POST", headers: { "content-type": "application/json" }, body: "{}" });
            setState("A management link is being delivered to the verified customer email.", "ok");
          } catch {
            setState("Delivery was not accepted. Its failure is recorded and can be retried.", "error");
          } finally {
            recovery.disabled = false;
          }
        });
        open.addEventListener("click", () => loadOrganization(organization.organizationId, root));
        root.append(title, open, facts, recovery);
        organizations.append(root);
      }
      setState("Choose an organization to load its control plane.", "ok");
    } catch (error) {
      setState(error.status === 401 ? "Sign in with AkurAI ID to access your organization control plane." : "The organization portal is temporarily unavailable.", "error");
    }
  }

  refresh.addEventListener("click", load);
  load();
}