mirror of
https://github.com/wasrusgen/zov-tech.git
synced 2026-06-03 17:04:48 +00:00
fix: 3 bugs found in audit
1. _xlsx_auth_manager: возвращал (tg_id, user) при успехе → callers делали `if err: return err` и возвращали dict пользователя вместо данных. Исправлено: возвращает (tg_id, None) при успешной авторизации. 2. Promise.all с 4 запросами: ошибка Drive (сервис-аккаунт не добавлен к файлу) роняла весь дашборд. Исправлено: складские запросы изолированы в отдельный .then/.catch — дашборд замеров отрисовывается независимо. 3. Секции склада теперь появляются с задержкой (non-blocking), а не блокируют отрисовку главного экрана. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
e97c84e126
commit
1b8f70e44a
@ -3140,7 +3140,7 @@ def _initial(name: str) -> str:
|
|||||||
|
|
||||||
|
|
||||||
def _xlsx_auth_manager(body: dict[str, Any]) -> tuple[Any, dict[str, Any] | None]:
|
def _xlsx_auth_manager(body: dict[str, Any]) -> tuple[Any, dict[str, Any] | None]:
|
||||||
"""Проверяет initData и возвращает (tg_id, user) для менеджера или (None, error_dict)."""
|
"""Проверяет initData и возвращает (tg_id, None) для менеджера или (None, error_dict)."""
|
||||||
cfg = get_config()
|
cfg = get_config()
|
||||||
auth = verify_init_data(body.get("initData") or "", cfg.bot_token)
|
auth = verify_init_data(body.get("initData") or "", cfg.bot_token)
|
||||||
if not auth or not auth.get("user"):
|
if not auth or not auth.get("user"):
|
||||||
@ -3153,7 +3153,7 @@ def _xlsx_auth_manager(body: dict[str, Any]) -> tuple[Any, dict[str, Any] | None
|
|||||||
user = sheets.find_user(tg_id)
|
user = sheets.find_user(tg_id)
|
||||||
if not user or not sheets.has_role(user, "manager"):
|
if not user or not sheets.has_role(user, "manager"):
|
||||||
return None, {"error": "only_manager"}
|
return None, {"error": "only_manager"}
|
||||||
return tg_id, user
|
return tg_id, None # успешно: вернуть None-ошибку, чтобы caller мог сделать if err:
|
||||||
|
|
||||||
|
|
||||||
def _parse_xlsx_groups(file_bytes: bytes, source_label: str) -> list[dict[str, Any]]:
|
def _parse_xlsx_groups(file_bytes: bytes, source_label: str) -> list[dict[str, Any]]:
|
||||||
|
|||||||
@ -184,25 +184,30 @@ async function renderManagerHome(me) {
|
|||||||
const pendingContainer = el(`<div id="pendingContainer"></div>`);
|
const pendingContainer = el(`<div id="pendingContainer"></div>`);
|
||||||
app.insertBefore(pendingContainer, todayContainer);
|
app.insertBefore(pendingContainer, todayContainer);
|
||||||
|
|
||||||
// Параллельно грузим реальные данные (измерения + pending + отгрузки + поступления)
|
// Параллельно грузим реальные данные (измерения + pending — критичные)
|
||||||
|
// Складские данные грузим отдельно, чтобы ошибка Drive не ломала весь дашборд
|
||||||
try {
|
try {
|
||||||
const authBody = { initData: tg?.initData || "", initDataUnsafe: tg?.initDataUnsafe || null };
|
const authBody = { initData: tg?.initData || "", initDataUnsafe: tg?.initDataUnsafe || null };
|
||||||
const [resM, resP, resS, resA] = await Promise.all([
|
const [resM, resP] = await Promise.all([
|
||||||
fetch(`${BACKEND_URL}/api/measurements`, { method: "POST", body: JSON.stringify(authBody) }),
|
fetch(`${BACKEND_URL}/api/measurements`, { method: "POST", body: JSON.stringify(authBody) }),
|
||||||
fetch(`${BACKEND_URL}/api/manager_pending`, { method: "POST", body: JSON.stringify(authBody) }),
|
fetch(`${BACKEND_URL}/api/manager_pending`, { method: "POST", body: JSON.stringify(authBody) }),
|
||||||
fetch(`${BACKEND_URL}/api/shipments`, { method: "POST", body: JSON.stringify(authBody) }),
|
|
||||||
fetch(`${BACKEND_URL}/api/arrivals`, { method: "POST", body: JSON.stringify(authBody) }),
|
|
||||||
]);
|
]);
|
||||||
const data = await resM.json();
|
const data = await resM.json();
|
||||||
const pendingData = await resP.json();
|
const pendingData = await resP.json();
|
||||||
const shipmentsData = await resS.json();
|
|
||||||
const arrivalsData = await resA.json();
|
|
||||||
|
|
||||||
renderManagerPending(pendingContainer, pendingData.pending || []);
|
renderManagerPending(pendingContainer, pendingData.pending || []);
|
||||||
renderManagerToday(todayContainer, data.measurements || [], firstName, greetingEl);
|
renderManagerToday(todayContainer, data.measurements || [], firstName, greetingEl);
|
||||||
renderManagerProjects(projectsContainer, data.measurements || []);
|
renderManagerProjects(projectsContainer, data.measurements || []);
|
||||||
renderManagerShipments(shipmentsContainer, shipmentsData.shipments || [], "📦 Отгрузки с завода");
|
|
||||||
renderManagerShipments(arrivalsContainer, arrivalsData.shipments || [], "📥 Поступление в СПб");
|
// Складские данные — не критичны; грузим после, ошибка не ломает дашборд
|
||||||
|
const authBodyStr = JSON.stringify(authBody);
|
||||||
|
Promise.all([
|
||||||
|
fetch(`${BACKEND_URL}/api/shipments`, { method: "POST", body: authBodyStr }).then(r => r.json()).catch(() => ({})),
|
||||||
|
fetch(`${BACKEND_URL}/api/arrivals`, { method: "POST", body: authBodyStr }).then(r => r.json()).catch(() => ({})),
|
||||||
|
]).then(([shipmentsData, arrivalsData]) => {
|
||||||
|
renderManagerShipments(shipmentsContainer, shipmentsData.shipments || [], "📦 Отгрузки с завода");
|
||||||
|
renderManagerShipments(arrivalsContainer, arrivalsData.shipments || [], "📥 Поступление в СПб");
|
||||||
|
}).catch(() => { /* тихо — дашборд уже отрисован */ });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
todayContainer.innerHTML = `<div class="error">Не удалось загрузить данные: ${escHtml(e.message)}</div>`;
|
todayContainer.innerHTML = `<div class="error">Не удалось загрузить данные: ${escHtml(e.message)}</div>`;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,14 +12,14 @@
|
|||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=Geist:wght@400;500;600&family=Newsreader:ital,wght@0,400..600;1,400..600&family=Instrument+Serif:ital@0;1&family=JetBrains+Mono:wght@400;500&family=Cormorant+Garamond:ital,wght@1,400;1,500;1,600&family=Caveat:wght@500;700&display=swap">
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=Geist:wght@400;500;600&family=Newsreader:ital,wght@0,400..600;1,400..600&family=Instrument+Serif:ital@0;1&family=JetBrains+Mono:wght@400;500&family=Cormorant+Garamond:ital,wght@1,400;1,500;1,600&family=Caveat:wght@500;700&display=swap">
|
||||||
<script src="https://telegram.org/js/telegram-web-app.js"></script>
|
<script src="https://telegram.org/js/telegram-web-app.js"></script>
|
||||||
<link rel="stylesheet" href="assets/styles.css?v=20260516c">
|
<link rel="stylesheet" href="assets/styles.css?v=20260516d">
|
||||||
<link rel="stylesheet" href="assets/podbor.css?v=20260516c">
|
<link rel="stylesheet" href="assets/podbor.css?v=20260516d">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<!-- Splash — лого @wasrusgen1 + опилки (16) + вращающийся диск -->
|
<!-- Splash — лого @wasrusgen1 + опилки (16) + вращающийся диск -->
|
||||||
<div class="loader splash" id="splash">
|
<div class="loader splash" id="splash">
|
||||||
<div class="brand-logo-wrap">
|
<div class="brand-logo-wrap">
|
||||||
<img class="brand-logo" src="assets/wasrusgen-logo.svg?v=20260516c" alt="@wasrusgen1">
|
<img class="brand-logo" src="assets/wasrusgen-logo.svg?v=20260516d" alt="@wasrusgen1">
|
||||||
<div class="splash-dust" aria-hidden="true">
|
<div class="splash-dust" aria-hidden="true">
|
||||||
<span class="dust d1"></span> <span class="dust d2"></span>
|
<span class="dust d1"></span> <span class="dust d2"></span>
|
||||||
<span class="dust d3"></span> <span class="dust d4"></span>
|
<span class="dust d3"></span> <span class="dust d4"></span>
|
||||||
@ -35,15 +35,15 @@
|
|||||||
<div class="brand-tagline-gold">CRM</div>
|
<div class="brand-tagline-gold">CRM</div>
|
||||||
</div>
|
</div>
|
||||||
<main id="app"></main>
|
<main id="app"></main>
|
||||||
<script src="assets/icons.js?v=20260516c"></script>
|
<script src="assets/icons.js?v=20260516d"></script>
|
||||||
<script src="assets/podbor.config.js?v=20260516c"></script>
|
<script src="assets/podbor.config.js?v=20260516d"></script>
|
||||||
<script src="assets/podbor.picts.js?v=20260516c"></script>
|
<script src="assets/podbor.picts.js?v=20260516d"></script>
|
||||||
<script src="assets/podbor.js?v=20260516c"></script>
|
<script src="assets/podbor.js?v=20260516d"></script>
|
||||||
<script src="assets/clients.js?v=20260516c"></script>
|
<script src="assets/clients.js?v=20260516d"></script>
|
||||||
<script src="assets/zamer-picts.js?v=20260516c"></script>
|
<script src="assets/zamer-picts.js?v=20260516d"></script>
|
||||||
<script src="assets/measurements.js?v=20260516c"></script>
|
<script src="assets/measurements.js?v=20260516d"></script>
|
||||||
<script src="assets/request.js?v=20260516c"></script>
|
<script src="assets/request.js?v=20260516d"></script>
|
||||||
<script src="assets/assembly.js?v=20260516c"></script>
|
<script src="assets/assembly.js?v=20260516d"></script>
|
||||||
<script src="assets/app.js?v=20260516c"></script>
|
<script src="assets/app.js?v=20260516d"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user