Ctrl K
ComunidadGuías…

src-gui/js/api.js

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
// SPDX-License-Identifier: GPL-3.0-or-later
/* === Data fetching === */
/* Desktop detection must not rely on the injected Tauri global alone: on this
   stack (wry + WebKitGTK) it has been reported missing, and the page is served
   from tauri://localhost regardless. Any of these signals is enough. */
const isDesktop = () => {
  if (typeof window === "undefined") return false;
  if (window.__TAURI_INTERNALS__ || window.__TAURI__) return true;
  try {
    return location.protocol === "tauri:" || location.hostname === "tauri.localhost";
  } catch {
    return false;
  }
};
const STATIC_DATA = typeof window !== "undefined" && window.__DATA__ ? window.__DATA__ : null;

/* Ask the desktop shell's loopback service. Its errors carry the real reason
   (missing CLI, bad resource copy) — surface them, never swallow them. */
async function desktopFetch(path, q = "") {
  let res;
  try {
    res = await fetch("http://127.0.0.1:17321" + path + q, { cache: "no-store" });
  } catch {
    throw new Error("the Linux Doctor service is not reachable on 127.0.0.1:17321 (is the app still starting?)");
  }
  if (!res.ok) {
    const body = await res.text().catch(() => "");
    let msg = body;
    try { msg = JSON.parse(body).error || body; } catch {}
    throw new Error(msg || `report service error (HTTP ${res.status})`);
  }
  return res.json();
}

/* refresh=true forces a fresh scan; save=true records the run in history.
   Background polling passes neither (the servers serve a short cached report,
   and machine history only advances on an explicit run). */
async function fetchReport({ refresh = false, save = false } = {}) {
  if (STATIC_DATA) return STATIC_DATA;
  const p = new URLSearchParams();
  if (refresh) p.set("refresh", "1");
  if (save) p.set("save", "1");
  const q = p.toString() ? `?${p}` : "";
  if (isDesktop()) return desktopFetch("/report", q);
  const res = await fetch("/api/report" + q, { cache: "no-store" });
  if (!res.ok) throw new Error("HTTP " + res.status);
  return res.json();
}

async function fetchHistory() {
  if (STATIC_DATA) return [];
  if (isDesktop()) {
    try {
      const res = await fetch("http://127.0.0.1:17321/history", { cache: "no-store" });
      if (res.ok) return (await res.json()).runs || [];
    } catch {}
  }
  try {
    const res = await fetch("/api/history", { cache: "no-store" });
    if (res.ok) return (await res.json()).runs || [];
  } catch {}
  return [];
}

async function fetchChecks() {
  if (STATIC_DATA) return [];
  if (isDesktop()) {
    try {
      const res = await fetch("http://127.0.0.1:17321/checks", { cache: "no-store" });
      if (res.ok) return (await res.json()).checks || [];
    } catch {}
  }
  try {
    const res = await fetch("/api/checks", { cache: "no-store" });
    if (res.ok) return (await res.json()).checks || [];
  } catch {}
  return [];
}

/* check id → category map, fetched once and reused across renders.
   Fills the shared checksCategoryMap; never throws. */
async function loadCategoryMap() {
  try {
    const list = await fetchChecks();
    checksCategoryMap = new Map(list.map((c) => [c.id, c.category || "other"]));
  } catch {
    checksCategoryMap = new Map();
  }
  return checksCategoryMap;
}

/* Ignore list + history clearing. The desktop shell's loopback service answers
   the same shapes on 17321; in the browser the dashboard serves them directly.
   Both are best-effort: a missing route leaves the section empty, never throws. */
async function fetchIgnore() {
  if (STATIC_DATA) return { patterns: [], codes: [], path: "" };
  if (isDesktop()) {
    try {
      const res = await fetch("http://127.0.0.1:17321/api/ignore", { cache: "no-store" });
      if (res.ok) return await res.json();
    } catch {}
  }
  try {
    const res = await fetch("/api/ignore", { cache: "no-store" });
    if (res.ok) return await res.json();
  } catch {}
  return { patterns: [], codes: [], path: "" };
}

async function postJson(url, payload) {
  try {
    const res = await fetch(url, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(payload) });
    return res.ok && (await res.json()).ok !== false;
  } catch {
    return false;
  }
}

async function editIgnore(payload) {
  if (isDesktop() && await postJson("http://127.0.0.1:17321/api/ignore", payload)) return true;
  return postJson("/api/ignore", payload);
}

async function clearHistoryApi() {
  const tryPost = async (url) => {
    try {
      const res = await fetch(url, { method: "POST" });
      return res.ok && (await res.json()).ok !== false;
    } catch {
      return false;
    }
  };
  if (isDesktop() && await tryPost("http://127.0.0.1:17321/api/history/clear")) return true;
  return tryPost("/api/history/clear");
}