UNPKG

@mesh-tech/mesh-cli

Version:

CLI for Mesh platform development utilities

102 lines (101 loc) 3.67 kB
import { getContextConfig, getValidToken } from "../commands/login.js"; export function resolveRegistryBroker(context, getCfg = getContextConfig) { return resolveRegistryBrokerSource(context, getCfg).url; } export const PUBLISHED_BROKER_TIMEOUT_MS = 15_000; export const DERIVED_BROKER_TIMEOUT_MS = 4_000; export function resolveRegistryBrokerSource(context, getCfg = getContextConfig) { const cfg = getCfg(context); if (cfg?.registryBroker) return { url: cfg.registryBroker, source: "published" }; return { url: deriveRegistryBroker(context, getCfg), source: "derived" }; } export function deriveRegistryBroker(context, getCfg = getContextConfig) { const cfg = getCfg(context); if (!cfg?.issuer) return null; try { const u = new URL(cfg.issuer); const parts = u.hostname.split("."); parts[0] = "registry-auth"; return `https://${parts.join(".")}`; } catch { return null; } } export async function classifyBrokerResponse(res) { if (res.status === 401) return { ok: false, failure: { kind: "unauthenticated" } }; if (res.status === 403) { const body = (await res.json().catch(() => ({}))); return { ok: false, failure: { kind: "not-authorized", email: typeof body.email === "string" ? body.email : undefined, requiredRoles: Array.isArray(body.requiredRoles) ? body.requiredRoles.filter((r) => typeof r === "string") : undefined, requestUrl: typeof body.requestUrl === "string" ? body.requestUrl : undefined, }, }; } if (res.status !== 200) { return { ok: false, failure: { kind: "unavailable", detail: `broker returned ${res.status}` } }; } let body; try { body = (await res.json()); } catch (err) { return { ok: false, failure: { kind: "unavailable", detail: `malformed broker response: ${err.message}` }, }; } const { endpoint, authorizationToken, scope, expiresAt } = body; if (typeof endpoint !== "string" || !endpoint || typeof authorizationToken !== "string" || !authorizationToken) { return { ok: false, failure: { kind: "unavailable", detail: "broker response missing endpoint or authorizationToken" }, }; } return { ok: true, grant: { scope: typeof scope === "string" && scope ? scope : "@mesh-tech", endpoint, authorizationToken, expiresAt: typeof expiresAt === "string" ? expiresAt : undefined, }, }; } export async function fetchRegistryGrant(context, brokerUrl, deps) { const token = await deps.getValidToken(context); if (!token) return { ok: false, failure: { kind: "no-session" } }; const doFetch = deps.fetchFn ?? fetch; const controller = new AbortController(); const timer = setTimeout(() => controller.abort(), deps.timeoutMs ?? PUBLISHED_BROKER_TIMEOUT_MS); try { const res = await doFetch(`${brokerUrl.replace(/\/+$/, "")}/token`, { method: "POST", headers: { authorization: `Bearer ${token}` }, signal: controller.signal, }); return await classifyBrokerResponse(res); } catch (err) { return { ok: false, failure: { kind: "unavailable", detail: err.message }, }; } finally { clearTimeout(timer); } } export function realGrantDeps() { return { getValidToken: (c) => getValidToken(c) }; }