203 lines
5.9 KiB
JavaScript
203 lines
5.9 KiB
JavaScript
/**
|
|
* Orchestrator boot adapter: JWT, enterprise scope, API base for copied WESP static UI.
|
|
*/
|
|
(function (global) {
|
|
const cfg = global.__WESP_ORCH__ || {};
|
|
const API_BASE = cfg.apiBase || "/api/v1";
|
|
|
|
const AUTH_MAP = {
|
|
"/api/auth/change_credentials": "/api/v1/users/me/password",
|
|
};
|
|
|
|
const WESP_API_PREFIXES = [
|
|
"/api/admin/",
|
|
"/api/notifications",
|
|
"/api/updates/",
|
|
"/api/sync/",
|
|
"/api/feed_dispensers",
|
|
"/api/components",
|
|
"/api/recipes",
|
|
"/api/reports",
|
|
"/api/analytics/",
|
|
"/api/feed-quality/",
|
|
"/api/periods",
|
|
"/api/sklad",
|
|
"/api/feed-accounting",
|
|
"/api/daily-plan",
|
|
"/api/lab",
|
|
"/api/auth/",
|
|
];
|
|
|
|
const TOKEN_KEY = "compton.access_token";
|
|
const ENTERPRISE_KEY = "compton.enterprise_id";
|
|
|
|
function readToken() {
|
|
return (
|
|
(global.__WESP_ORCH__ && global.__WESP_ORCH__.accessToken) ||
|
|
global.localStorage.getItem(TOKEN_KEY) ||
|
|
""
|
|
);
|
|
}
|
|
|
|
function readEnterpriseId() {
|
|
return (
|
|
(global.__WESP_ORCH__ && global.__WESP_ORCH__.enterpriseId) ||
|
|
global.localStorage.getItem(ENTERPRISE_KEY) ||
|
|
""
|
|
);
|
|
}
|
|
|
|
function persistAccessToken(token, user) {
|
|
if (!token) return;
|
|
global.localStorage.setItem(TOKEN_KEY, token);
|
|
const entId = readEnterpriseId();
|
|
if (global.ComptonWespAuth && typeof global.ComptonWespAuth.persistSession === "function") {
|
|
global.ComptonWespAuth.persistSession(token, user || null, entId);
|
|
return;
|
|
}
|
|
global.__WESP_ORCH__ = Object.assign({}, global.__WESP_ORCH__ || {}, {
|
|
accessToken: token,
|
|
enterpriseId: entId,
|
|
});
|
|
}
|
|
|
|
let refreshInFlight = null;
|
|
|
|
function tryRefreshAccessToken() {
|
|
if (refreshInFlight) return refreshInFlight;
|
|
refreshInFlight = nativeFetch("/api/v1/auth/refresh", {
|
|
method: "POST",
|
|
credentials: "include",
|
|
headers: { "Content-Type": "application/json" },
|
|
})
|
|
.then(function (resp) {
|
|
if (!resp.ok) return null;
|
|
return resp.json();
|
|
})
|
|
.then(function (data) {
|
|
if (!data || !data.access_token) return null;
|
|
persistAccessToken(data.access_token, data.user);
|
|
return data.access_token;
|
|
})
|
|
.catch(function () {
|
|
return null;
|
|
})
|
|
.finally(function () {
|
|
refreshInFlight = null;
|
|
});
|
|
return refreshInFlight;
|
|
}
|
|
|
|
function shouldAttemptRefresh(url, init) {
|
|
if (!url || (init && init._authRetried)) return false;
|
|
if (url.indexOf("/api/v1/auth/refresh") >= 0) return false;
|
|
if (url.indexOf("/api/auth/login") >= 0) return false;
|
|
return true;
|
|
}
|
|
|
|
function retryWithToken(patched, authInit, token) {
|
|
const retryHeaders = new Headers((authInit && authInit.headers) || {});
|
|
retryHeaders.set("Authorization", "Bearer " + token);
|
|
const retryInit = Object.assign({}, authInit || {}, {
|
|
headers: retryHeaders,
|
|
_authRetried: true,
|
|
});
|
|
return nativeFetch(patched, retryInit);
|
|
}
|
|
|
|
function isExternalUrl(url) {
|
|
return /^https?:\/\//i.test(url);
|
|
}
|
|
|
|
function patchUrl(url) {
|
|
if (typeof url !== "string" || isExternalUrl(url)) return url;
|
|
for (const [from, to] of Object.entries(AUTH_MAP)) {
|
|
if (url === from || url.startsWith(from + "?")) {
|
|
return to + url.slice(from.length);
|
|
}
|
|
}
|
|
if (url.startsWith("/api/v1/sync/")) {
|
|
return url;
|
|
}
|
|
for (let i = 0; i < WESP_API_PREFIXES.length; i++) {
|
|
if (url.startsWith(WESP_API_PREFIXES[i])) {
|
|
return url;
|
|
}
|
|
}
|
|
if (url.startsWith("/api/") && !url.startsWith("/api/v1/")) {
|
|
const rewritten = API_BASE + url.slice(4);
|
|
return rewritten;
|
|
}
|
|
return url;
|
|
}
|
|
|
|
function withAuth(url, init) {
|
|
if (isExternalUrl(url)) {
|
|
return init || {};
|
|
}
|
|
const token = readToken();
|
|
const enterpriseId = readEnterpriseId();
|
|
const headers = new Headers((init && init.headers) || {});
|
|
if (token && !headers.has("Authorization")) {
|
|
headers.set("Authorization", "Bearer " + token);
|
|
}
|
|
if (enterpriseId && !headers.has("X-Enterprise-Id")) {
|
|
headers.set("X-Enterprise-Id", enterpriseId);
|
|
}
|
|
return Object.assign({}, init || {}, { headers });
|
|
}
|
|
|
|
function appendEnterpriseQuery(url) {
|
|
if (isExternalUrl(url)) return url;
|
|
const enterpriseId = readEnterpriseId();
|
|
if (!enterpriseId || url.indexOf("enterprise_id=") >= 0) {
|
|
return url;
|
|
}
|
|
const sep = url.indexOf("?") >= 0 ? "&" : "?";
|
|
return url + sep + "enterprise_id=" + encodeURIComponent(enterpriseId);
|
|
}
|
|
|
|
function needsEnterpriseScope(url) {
|
|
if (isExternalUrl(url)) return false;
|
|
for (let i = 0; i < WESP_API_PREFIXES.length; i++) {
|
|
if (url.startsWith(WESP_API_PREFIXES[i])) {
|
|
return url.indexOf("enterprise_id=") < 0;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function awaitOrchestratorReady() {
|
|
const ready = global.__WESP_ORCH__ && global.__WESP_ORCH__.ready;
|
|
return ready && typeof ready.then === "function" ? ready : Promise.resolve();
|
|
}
|
|
|
|
const nativeFetch = global.fetch.bind(global);
|
|
global.fetch = function (input, init) {
|
|
if (typeof input === "string") {
|
|
const scopeReady = needsEnterpriseScope(input) ? awaitOrchestratorReady() : Promise.resolve();
|
|
return scopeReady.then(function () {
|
|
const patched = appendEnterpriseQuery(patchUrl(input));
|
|
const authInit = withAuth(patched, init);
|
|
return nativeFetch(patched, authInit).then(function (resp) {
|
|
if (resp.status !== 401 || !shouldAttemptRefresh(patched, authInit)) {
|
|
return resp;
|
|
}
|
|
return tryRefreshAccessToken().then(function (newToken) {
|
|
if (!newToken) return resp;
|
|
return retryWithToken(patched, authInit, newToken);
|
|
});
|
|
});
|
|
});
|
|
}
|
|
return nativeFetch(input, withAuth("", init));
|
|
};
|
|
|
|
global.WespOrchestratorBoot = {
|
|
apiBase: API_BASE,
|
|
get enterpriseId() {
|
|
return readEnterpriseId();
|
|
},
|
|
};
|
|
})(window);
|