From e44312305c503efff0cffefb675beedbf9a0ce80 Mon Sep 17 00:00:00 2001 From: berkio_admin_gitea Date: Fri, 3 Jul 2026 18:38:23 +0000 Subject: [PATCH] =?UTF-8?q?=D0=9C=D0=BE=D0=B1=D0=B8=D0=BB=D1=8C=D0=BD?= =?UTF-8?q?=D0=B0=D1=8F=20=D1=88=D0=B0=D0=BF=D0=BA=D0=B0=20+=20=D0=BE?= =?UTF-8?q?=D0=BF=D0=BB=D0=B0=D1=82=D0=B0=20=D0=BF=D0=BE=D0=B4=D0=BF=D0=B8?= =?UTF-8?q?=D1=81=D0=BA=D0=B8=20=D1=87=D0=B5=D1=80=D0=B5=D0=B7=20Telegram?= =?UTF-8?q?=20(deep-link)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Мобильная шапка: скрыт подзаголовок, короткая метка «Войти», бейдж ★ — не вылезает за край - /subscribe?token=…: проверка HMAC-подписи из бота → сессия tg: → авто-открытие оплаты - Фронт: ?subscribe=1 открывает окно подписки после загрузки сессии Co-Authored-By: Claude Opus 4.8 --- app/main.py | 35 +++++++++++++++++++++++++++++++++ app/static/index.html | 45 +++++++++++++++++++++++++++++++++++++++---- 2 files changed, 76 insertions(+), 4 deletions(-) diff --git a/app/main.py b/app/main.py index 8de7f1c..93bd93d 100644 --- a/app/main.py +++ b/app/main.py @@ -69,6 +69,9 @@ VK_REDIRECT = f"{os.getenv('SITE_BASE_URL', 'https://pdf2scan.online')}/api # ── Админ-панель ────────────────────────────────────────────────────────────── ADMIN_TOKEN = os.getenv("ADMIN_TOKEN", "") +# ── Подписка через Telegram-бота (подписанные deep-link) ────────────────────── +SUB_LINK_SECRET = os.getenv("SUB_LINK_SECRET", "") # общий секрет бота и веба + # ── Вход по почте (magic-code через SMTP) ───────────────────────────────────── SMTP_HOST = os.getenv("SMTP_HOST", "") SMTP_PORT = int(os.getenv("SMTP_PORT", "465")) @@ -2131,6 +2134,38 @@ async def yandex_callback(request: Request, code: str = "", state: str = ""): return resp +def _verify_sub_token(token: str): + """Проверяет подписанный deep-link из бота: '..'. Возвращает tgid (int) или None.""" + if not SUB_LINK_SECRET or not token: + return None + try: + tgid_s, exp_s, sig = token.split(".", 2) + if float(exp_s) < time.time(): + return None + expect = hmac.new(SUB_LINK_SECRET.encode(), f"{tgid_s}.{exp_s}".encode(), hashlib.sha256).hexdigest() + if not hmac.compare_digest(sig, expect): + return None + return int(tgid_s) + except Exception: + return None + + +@app.get("/subscribe") +async def subscribe_via_bot(request: Request, token: str = "", name: str = ""): + """Deep-link из Telegram-бота: проверяем подпись, логиним как tg: (сессия), + ведём на главную с авто-открытием окна оплаты подписки.""" + tgid = _verify_sub_token(token) + if not tgid: + return RedirectResponse(f"{SITE_BASE_URL}/?login=error", status_code=302) + account_id = f"tg:{tgid}" + _db_ensure_account(account_id) + sess = _db_create_session(account_id, "telegram", (name or "").strip()[:64] or "Telegram", "") + resp = RedirectResponse(f"{SITE_BASE_URL}/?subscribe=1", status_code=302) + resp.set_cookie(SESSION_COOKIE, sess, max_age=SESSION_DAYS * 86400, + httponly=True, secure=True, samesite="lax", path="/") + return resp + + @app.get("/api/me") async def me(request: Request): sess = _db_get_session(request.cookies.get(SESSION_COOKIE)) diff --git a/app/static/index.html b/app/static/index.html index ae6a039..05fda9e 100644 --- a/app/static/index.html +++ b/app/static/index.html @@ -241,9 +241,12 @@ display: flex; align-items: center; gap: 6px; + white-space: nowrap; } #tg-login-header-btn:hover { background: var(--blue-hover); } + /* Короткий вариант метки кнопки входа — только на узких экранах */ + .login-mini { display: none; } /* ── Main card ── */ main { @@ -746,6 +749,21 @@ .bs { font-size: 12px; padding: 3px 8px; } } + /* ── Компактная шапка на телефонах ── */ + @media (max-width: 500px) { + header { padding: 8px 12px; gap: 8px; flex-wrap: wrap; } + .logo-subtitle { display: none; } /* длинный подзаголовок распирает шапку — прячем на телефоне */ + .logo-text { font-size: 18px; } + #header-auth { gap: 8px; } + #tg-login-header-btn { padding: 7px 12px; font-size: 12.5px; } + .login-full { display: none; } + .login-mini { display: inline; } + #tg-user-info { font-size: 13px; gap: 6px; } + #tg-user-name { display: inline-block; max-width: 84px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; vertical-align: middle; } + #sub-badge { padding: 4px 8px; } + #sub-badge-text { display: none; } /* дата — только в карточке ниже; в шапке оставляем ★ */ + } + /* ── SEO section ── */ #seo-section { width: 100%; @@ -851,7 +869,8 @@