const BASE = ""; export async function api(path, opts = {}) { const res = await fetch(BASE + path, { credentials: "same-origin", ...opts }); if (res.status === 401) { // session gone — tell App to show the login screen window.dispatchEvent(new CustomEvent("mb-unauth")); throw new Error("401 unauthorized"); } if (!res.ok) throw new Error(`${res.status} ${await res.text()}`); const ct = res.headers.get("content-type") || ""; return ct.includes("json") ? res.json() : res; } // -- auth -------------------------------------------------------------------- export const getMe = () => api("/api/me"); export const login = (username, password) => api("/api/login", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ username, password }) }); export const logout = () => api("/api/logout", { method: "POST" }); export const listUsers = () => api("/api/users"); export const addUser = (u) => api("/api/users", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(u) }); export const patchUser = (username, u) => api(`/api/users/${username}`, { method: "PATCH", headers: { "Content-Type": "application/json" }, body: JSON.stringify(u) }); export const delUser = (username) => api(`/api/users/${username}`, { method: "DELETE" }); export const createToken = (body) => api("/api/tokens", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body || {}) }); export const getSystem = () => api("/api/system"); export const getRecentJobs = () => api("/api/jobs/recent"); export const listAssets = () => api("/api/assets"); export const listOperators = () => api("/api/operators"); export const listJobs = () => api("/api/jobs"); export const deleteAsset = (id) => api(`/api/assets/${id}`, { method: "DELETE" }); export async function uploadFile(file) { const form = new FormData(); form.append("file", file, file.name || "pasted.png"); return api("/api/assets", { method: "POST", body: form }); } export const runJob = (operator, asset_id, params) => api("/api/jobs", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ operator, asset_id, params }), }); export const cancelJob = (id) => api(`/api/jobs/${id}/cancel`, { method: "POST" }); export const retryJob = (id) => api(`/api/jobs/${id}/retry`, { method: "POST" }); export const deleteJob = (id) => api(`/api/jobs/${id}`, { method: "DELETE" }); export const getSettings = () => api("/api/settings"); export const putSettings = (updates) => api("/api/settings", { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify(updates), }); export function connectWS(onMessage) { const proto = location.protocol === "https:" ? "wss" : "ws"; const ws = new WebSocket(`${proto}://${location.host}/ws`); ws.onmessage = (e) => onMessage(JSON.parse(e.data)); const ping = setInterval(() => ws.readyState === 1 && ws.send("ping"), 20000); ws.onclose = () => { clearInterval(ping); setTimeout(() => connectWS(onMessage), 2000); }; return ws; } export const assetFileURL = (id, member) => `/api/assets/${id}/file` + (member ? `?member=${encodeURIComponent(member)}` : "");