@mesh-tech/mesh-cli
Version:
CLI for Mesh platform development utilities
102 lines (101 loc) • 3.67 kB
JavaScript
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) };
}