harden telegram webapp production readiness
This commit is contained in:
@@ -16,9 +16,9 @@
|
||||
<div class="auth-panel">
|
||||
<p class="eyebrow">Drivers</p>
|
||||
<h1>Гараж</h1>
|
||||
<p>Войди через Telegram, чтобы привязать гараж к твоему chat_id.</p>
|
||||
<p id="authMessage">Это приложение открывается через Telegram-бота. Откройте Mini App из Telegram.</p>
|
||||
<div id="telegramLoginSlot" class="telegram-login-slot"></div>
|
||||
<a id="telegramLoginLink" class="telegram-login-link hidden" href="#" target="_blank" rel="noreferrer">Войти через Telegram</a>
|
||||
<a id="telegramLoginLink" class="telegram-login-link hidden" href="#" target="_blank" rel="noreferrer">Открыть бота</a>
|
||||
</div>
|
||||
</div>
|
||||
<main class="shell">
|
||||
@@ -102,8 +102,8 @@
|
||||
<strong>ТО / ремонт</strong>
|
||||
</button>
|
||||
<button class="action-card" data-action="scan">
|
||||
<span>Скан чека</span>
|
||||
<strong>OCR</strong>
|
||||
<span>Разбор чека</span>
|
||||
<strong>Текст</strong>
|
||||
</button>
|
||||
</section>
|
||||
|
||||
@@ -220,7 +220,7 @@
|
||||
<button class="menu-row" id="openCarProfileBtn">Параметры автомобиля</button>
|
||||
<button class="menu-row" id="openSettingsBtn">Локаль и валюта</button>
|
||||
<button class="menu-row" id="openNotificationsBtn">Уведомления</button>
|
||||
<button class="menu-row" id="openScanBtn">Сканировать чек</button>
|
||||
<button class="menu-row" id="openScanBtn">Разобрать чек</button>
|
||||
|
||||
<section class="drawer-section hidden" id="settingsSection">
|
||||
<h2>Настройки</h2>
|
||||
@@ -357,7 +357,7 @@
|
||||
<div class="scan-modal hidden" id="scanModal">
|
||||
<div class="scan-panel">
|
||||
<div class="section-head">
|
||||
<h2>Скан чека</h2>
|
||||
<h2>Разбор чека</h2>
|
||||
<button class="icon-btn" id="closeScanBtn" aria-label="Закрыть">×</button>
|
||||
</div>
|
||||
<form id="ocrForm" class="scan-form">
|
||||
@@ -368,9 +368,9 @@
|
||||
<input id="receiptCameraInput" class="hidden-file" type="file" accept="image/*" capture="environment" />
|
||||
<input id="receiptFileInput" class="hidden-file" type="file" accept="image/*,.pdf,.txt" />
|
||||
<div id="receiptFileName" class="file-hint">Файл не выбран</div>
|
||||
<button type="submit">Распознать</button>
|
||||
<button type="submit">Разобрать текст</button>
|
||||
</form>
|
||||
<div id="ocrResult" class="tip-card">После распознавания поля заправки заполнятся автоматически.</div>
|
||||
<div id="ocrResult" class="tip-card">Сейчас разбираем текстовые чеки. OCR по фото будет подключен отдельно.</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -408,8 +408,13 @@ function formData(form) {
|
||||
}
|
||||
|
||||
async function api(path, options = {}) {
|
||||
const headers = { "Content-Type": "application/json", ...(options.headers || {}) };
|
||||
if (tg?.initData) headers["X-Telegram-Init-Data"] = tg.initData;
|
||||
if (!tg?.initData && state.authConfig?.allow_dev_auth) {
|
||||
headers["X-Dev-Telegram-Id"] = localStorage.getItem("driversDevTelegramId") || "1";
|
||||
}
|
||||
const response = await fetch(`/api${path}`, {
|
||||
headers: { "Content-Type": "application/json", ...(options.headers || {}) },
|
||||
headers,
|
||||
...options,
|
||||
});
|
||||
if (!response.ok) {
|
||||
@@ -497,13 +502,14 @@ async function ensureUser() {
|
||||
hideAuthOverlay();
|
||||
return;
|
||||
}
|
||||
const stored = localStorage.getItem("driversUser");
|
||||
if (stored) {
|
||||
state.user = JSON.parse(stored);
|
||||
if (state.authConfig?.allow_dev_auth) {
|
||||
const devId = localStorage.getItem("driversDevTelegramId") || "1";
|
||||
localStorage.setItem("driversDevTelegramId", devId);
|
||||
state.user = await api("/users/me");
|
||||
hideAuthOverlay();
|
||||
return;
|
||||
}
|
||||
await showTelegramLogin();
|
||||
showTelegramOpenHint();
|
||||
throw new Error("Требуется вход через Telegram");
|
||||
}
|
||||
|
||||
@@ -512,22 +518,33 @@ function hideAuthOverlay() {
|
||||
document.body.classList.remove("auth-required");
|
||||
}
|
||||
|
||||
async function showTelegramLogin() {
|
||||
function showTelegramOpenHint() {
|
||||
const overlay = document.querySelector("#authOverlay");
|
||||
const slot = document.querySelector("#telegramLoginSlot");
|
||||
const link = document.querySelector("#telegramLoginLink");
|
||||
const message = document.querySelector("#authMessage");
|
||||
overlay?.classList.remove("hidden");
|
||||
document.body.classList.add("auth-required");
|
||||
if (!slot || slot.dataset.ready) return;
|
||||
const botUsername = state.authConfig?.bot_username;
|
||||
if (message) {
|
||||
message.textContent = "Это приложение открывается через Telegram-бота. Откройте Mini App из Telegram.";
|
||||
}
|
||||
if (slot) slot.textContent = "";
|
||||
if (!botUsername) {
|
||||
slot.textContent = "Telegram Login временно недоступен";
|
||||
return;
|
||||
}
|
||||
if (link) {
|
||||
link.href = `https://t.me/${botUsername}?start=web_login`;
|
||||
link.href = `https://t.me/${botUsername}`;
|
||||
link.classList.remove("hidden");
|
||||
}
|
||||
}
|
||||
|
||||
async function showTelegramLogin() {
|
||||
showTelegramOpenHint();
|
||||
const slot = document.querySelector("#telegramLoginSlot");
|
||||
if (!slot || slot.dataset.ready) return;
|
||||
const botUsername = state.authConfig?.bot_username;
|
||||
if (!botUsername) return;
|
||||
window.onTelegramAuth = async (user) => {
|
||||
state.user = await api("/users/telegram-login", {
|
||||
method: "POST",
|
||||
@@ -1424,7 +1441,11 @@ document.querySelector("#ocrForm").addEventListener("submit", async (event) => {
|
||||
await runAction(formButton, "Распознаю чек...", async () => {
|
||||
const payload = new FormData();
|
||||
payload.append("file", file);
|
||||
const response = await fetch("/api/ocr/fuel-receipt", { method: "POST", body: payload });
|
||||
const response = await fetch("/api/ocr/parse-text-receipt", {
|
||||
method: "POST",
|
||||
headers: tg?.initData ? { "X-Telegram-Init-Data": tg.initData } : {},
|
||||
body: payload,
|
||||
});
|
||||
if (!response.ok) throw new Error(await response.text());
|
||||
const result = await response.json();
|
||||
document.querySelector("#ocrResult").textContent = `${result.message} ${Math.round((result.confidence || 0) * 100)}%`;
|
||||
|
||||
Reference in New Issue
Block a user