91 lines
3.0 KiB
JavaScript
91 lines
3.0 KiB
JavaScript
import { fetchProjectInfo } from "./localClient.js";
|
|
import {
|
|
getSessionCredentials,
|
|
sessionCredentials,
|
|
setMcpSessionCredentials,
|
|
} from "./credentials.js";
|
|
|
|
const refreshInFlight = new Map();
|
|
|
|
export function isJwtExpiring(token, marginSeconds = 300) {
|
|
try {
|
|
const payload = token.split(".")[1];
|
|
if (!payload) return true;
|
|
const data = JSON.parse(Buffer.from(payload, "base64url").toString("utf8"));
|
|
return !data.exp || Math.floor(Date.now() / 1000) >= data.exp - marginSeconds;
|
|
} catch {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
export async function refreshSessionCredentials(
|
|
sessionId,
|
|
{ force = false, fetcher = fetchProjectInfo } = {},
|
|
) {
|
|
const current = await getSessionCredentials(sessionId);
|
|
if (!current?.project_dir) {
|
|
throw new Error("Project dir no disponible en esta sesion");
|
|
}
|
|
if (force && !current.mcp_secret && !process.env.ACAI_AUTH_HEADER) {
|
|
throw new Error("No hay autenticacion interna para autorizar la renovacion forzada");
|
|
}
|
|
|
|
const key = current.project_dir;
|
|
const existing = refreshInFlight.get(key);
|
|
if (existing) return existing;
|
|
|
|
const operation = (async () => {
|
|
let info;
|
|
try {
|
|
info = await fetcher(
|
|
{ project_dir: current.project_dir },
|
|
current.acai_user || null,
|
|
{
|
|
forceTokenRefresh: force,
|
|
mcpSecret: force ? current.mcp_secret : null,
|
|
},
|
|
);
|
|
} catch (error) {
|
|
throw new Error(error.response?.data?.error || error.message);
|
|
}
|
|
if (!info?.success) {
|
|
throw new Error(info?.error || "No se pudieron renovar las credenciales Acai");
|
|
}
|
|
if (!info.token || isJwtExpiring(info.token, 0)) {
|
|
throw new Error("El backend no devolvio un token Acai valido");
|
|
}
|
|
|
|
const fresh = {
|
|
...current,
|
|
token: info.token,
|
|
tokenHash: info.tokenHash || "",
|
|
website: info.domain || current.website,
|
|
web_url: info.web_url || current.web_url,
|
|
api_web_url: info.api_web_url || info.web_url || current.api_web_url,
|
|
forge_host: info.forge_host ?? current.forge_host,
|
|
project_dir: info.project_dir || current.project_dir,
|
|
mode: info.mode || current.mode,
|
|
};
|
|
sessionCredentials.set(sessionId, fresh);
|
|
setMcpSessionCredentials(sessionId, fresh);
|
|
return fresh;
|
|
})();
|
|
|
|
refreshInFlight.set(key, operation);
|
|
try {
|
|
return await operation;
|
|
} finally {
|
|
if (refreshInFlight.get(key) === operation) {
|
|
refreshInFlight.delete(key);
|
|
}
|
|
}
|
|
}
|
|
|
|
export async function ensureFreshSessionCredentials(sessionId) {
|
|
const current = await getSessionCredentials(sessionId);
|
|
if (!current?.token || !current.project_dir || !isJwtExpiring(current.token)) {
|
|
return current;
|
|
}
|
|
return refreshSessionCredentials(sessionId);
|
|
}
|