Интегрирован wesp в сайт
CI / quality (push) Canceled after 0s

This commit is contained in:
влад
2026-07-17 12:57:18 +03:00
parent 5dfa06ddbe
commit 355c0ef9f1
883 changed files with 194576 additions and 177 deletions
@@ -189,7 +189,8 @@ function collectSettingsFromForm() {
}
async function loadSettings() {
const resp = await fetch("/api/feed-quality/settings");
const settingsUrl = "/api/feed-quality/settings";
const resp = await fetch(settingsUrl);
if (!resp.ok) throw new Error("Не удалось загрузить настройки");
const data = await resp.json();
currentSettings = data.settings || data;
@@ -9,6 +9,10 @@ export function createRecipesOperationsController({
showRecipeEdit,
loadRecipes,
loadPeriods,
selectPeriod,
persistRecipe,
toggleUnloadingBlocks,
resetDeletedEntities,
ensureComponentsLoaded,
setUnloadingLinkBroken,
initializeUnloadingLinkButton,
@@ -185,8 +189,15 @@ export function createRecipesOperationsController({
}
try {
setSelectedPeriod(periodId);
if (typeof selectPeriod === "function") {
await selectPeriod(periodId);
} else {
setSelectedPeriod(periodId);
}
setSelectedRecipe(null);
if (typeof resetDeletedEntities === "function") {
resetDeletedEntities();
}
await ensureComponentsLoaded();
@@ -228,6 +239,9 @@ export function createRecipesOperationsController({
}
setUnloadingLinkBroken(Boolean(copiedRecipeData.unloading_link_broken));
if (typeof toggleUnloadingBlocks === "function") {
toggleUnloadingBlocks();
}
initializeUnloadingLinkButton();
updateTripPercentFieldState();
@@ -247,17 +261,19 @@ export function createRecipesOperationsController({
showRecipeEdit(true);
setTimeout(() => {
Promise.resolve(recalculateWeights()).catch((err) => console.error("Ошибка при расчете:", err));
}, 100);
setTimeout(() => {
Promise.resolve(updateTotalValues()).catch((err) => console.error("Ошибка при расчете:", err));
}, 100);
await Promise.all([
Promise.resolve(recalculateWeights()).catch((err) => console.error("Ошибка при расчете:", err)),
Promise.resolve(updateTotalValues?.()).catch((err) => console.error("Ошибка при расчете:", err)),
]);
if (typeof persistRecipe !== "function") {
throw new Error("Сохранение рейса недоступно");
}
await persistRecipe(true);
notyf.success("Рейс вставлен из буфера");
} catch (error) {
console.error("Ошибка при вставке рейса:", error);
notyf.error("Ошибка при вставке рейса");
notyf.error(error.message || "Ошибка при вставке рейса");
}
}
@@ -285,9 +301,10 @@ export function createRecipesOperationsController({
trip_percent: recipe.trip_percent ?? recipe.tripPercent ?? 100,
ingredients: (recipe.ingredients || []).map((ing) => ({
component_id: ing.component_id,
weight_per_head: ing.weight_per_head,
amount: ing.amount,
dry_matter: ing.dry_matter,
weight_per_head: ing.weight_per_head ?? ing.weightPerHead,
amount: ing.amount ?? ing.tripWeight,
dry_matter: ing.dry_matter ?? ing.dryMatter,
dry_matter_per_head: ing.dry_matter_per_head ?? ing.dryMatterPerHead,
order: ing.order,
})),
unloading_link_broken: Boolean(
@@ -5,26 +5,6 @@
const TOKEN_KEY = "compton.access_token";
const ENTERPRISE_KEY = "compton.enterprise_id";
const USER_KEY = "compton.user";
const DEBUG_ENDPOINT = "http://127.0.0.1:7898/ingest/d209dffb-c63e-477b-8b08-a57520eaee9b";
const SESSION = "785e22";
function debugLog(hypothesisId, message, data) {
// #region agent log
fetch(DEBUG_ENDPOINT, {
method: "POST",
headers: { "Content-Type": "application/json", "X-Debug-Session-Id": SESSION },
body: JSON.stringify({
sessionId: SESSION,
runId: "login-fix",
hypothesisId,
location: "wesp-orchestrator-auth-bridge.js",
message,
data,
timestamp: Date.now(),
}),
}).catch(function () {});
// #endregion
}
function readJson(key) {
try {
@@ -42,20 +22,29 @@
"";
const user = readJson(USER_KEY);
let resolveReady;
const ready = new Promise(function (resolve) {
resolveReady = resolve;
});
function markReady() {
if (typeof resolveReady === "function") {
resolveReady();
resolveReady = null;
}
}
global.__WESP_ORCH__ = Object.assign({}, global.__WESP_ORCH__ || {}, {
apiBase: "/api/v1",
accessToken: token,
enterpriseId: enterpriseId,
userEmail: user && user.email ? user.email : "",
ready: ready,
});
debugLog("H1", "auth bridge init", {
hasToken: Boolean(token),
hasEnterprise: Boolean(enterpriseId),
path: global.location.pathname,
});
if (token && !enterpriseId) {
if (enterpriseId || !token) {
markReady();
} else {
fetch("/api/v1/enterprise/enterprises", {
headers: { Authorization: "Bearer " + token },
})
@@ -65,16 +54,13 @@
.then(function (rows) {
const entId = Array.isArray(rows) && rows[0] && rows[0].id ? String(rows[0].id) : "";
if (!entId) {
debugLog("H1", "enterprise resolve failed", { count: Array.isArray(rows) ? rows.length : -1 });
return;
}
global.localStorage.setItem(ENTERPRISE_KEY, entId);
global.__WESP_ORCH__ = Object.assign({}, global.__WESP_ORCH__ || {}, { enterpriseId: entId });
debugLog("H1", "enterprise resolved async", { enterpriseId: entId });
})
.catch(function () {
debugLog("H1", "enterprise resolve error", {});
});
.catch(function () {})
.finally(markReady);
}
global.ComptonWespAuth = {
@@ -21,15 +21,88 @@
"/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) || "";
return (
(global.__WESP_ORCH__ && global.__WESP_ORCH__.accessToken) ||
global.localStorage.getItem(TOKEN_KEY) ||
""
);
}
function readEnterpriseId() {
return (global.__WESP_ORCH__ && global.__WESP_ORCH__.enterpriseId) || "";
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) {
@@ -52,7 +125,8 @@
}
}
if (url.startsWith("/api/") && !url.startsWith("/api/v1/")) {
return API_BASE + url.slice(4);
const rewritten = API_BASE + url.slice(4);
return rewritten;
}
return url;
}
@@ -83,11 +157,38 @@
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 patched = appendEnterpriseQuery(patchUrl(input));
return nativeFetch(patched, withAuth(patched, init));
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));
};
@@ -2,27 +2,6 @@
* Orchestrator login adapter for WESP login.html — persist JWT + enterprise after /api/auth/login.
*/
(function (global) {
const DEBUG_ENDPOINT = "http://127.0.0.1:7898/ingest/d209dffb-c63e-477b-8b08-a57520eaee9b";
const SESSION = "785e22";
function debugLog(hypothesisId, message, data) {
// #region agent log
fetch(DEBUG_ENDPOINT, {
method: "POST",
headers: { "Content-Type": "application/json", "X-Debug-Session-Id": SESSION },
body: JSON.stringify({
sessionId: SESSION,
runId: "login-fix",
hypothesisId,
location: "wesp-orchestrator-login.js",
message,
data,
timestamp: Date.now(),
}),
}).catch(function () {});
// #endregion
}
async function resolveEnterpriseId(accessToken) {
try {
const resp = await fetch("/api/v1/enterprise/enterprises", {
@@ -40,7 +19,6 @@
const token = payload && payload.access_token ? String(payload.access_token) : "";
const user = payload && payload.user ? payload.user : null;
if (!token || !user) {
debugLog("H2", "login missing token/user", { hasToken: Boolean(token), hasUser: Boolean(user) });
return false;
}
const enterpriseId = await resolveEnterpriseId(token);
@@ -52,7 +30,6 @@
enterpriseId: enterpriseId,
userEmail: user.email || "",
});
debugLog("H1", "session persisted", { hasEnterprise: Boolean(enterpriseId), email: user.email || "" });
return Boolean(enterpriseId);
}
@@ -62,24 +39,18 @@
if (url.indexOf("/api/auth/login") === -1) {
return nativeFetch(input, init);
}
debugLog("H3", "wesp login request", { url: url.split("?")[0] });
return nativeFetch(input, init).then(async function (response) {
const cloned = response.clone();
let data = {};
try {
data = await cloned.json();
} catch (_err) {
debugLog("H3", "login response not json", { status: response.status });
return response;
}
if (response.ok && data.status === "success") {
await persistOrchestratorSession(data);
} else {
debugLog("H3", "login failed", { status: response.status, bodyStatus: data.status });
}
return response;
});
};
debugLog("H4", "orchestrator login adapter ready", { path: global.location.pathname });
})(window);