docs
This commit is contained in:
@@ -13,6 +13,9 @@ import {
|
||||
import { CyberButton } from "@/components/ui/cyber-button";
|
||||
import { Dictionary } from "@/lib/IDict";
|
||||
|
||||
// Telegram Login Widget вызывает этот колбэк из глобального window после успешной
|
||||
// авторизации в самом Telegram — вешаем на него типизацию, т.к. виджет грузится
|
||||
// сторонним <script> и не завязан на React-модель.
|
||||
declare global {
|
||||
interface Window {
|
||||
onTelegramAuth: (user: any) => void;
|
||||
@@ -24,6 +27,23 @@ interface AuthClientProps {
|
||||
lang: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Экран авторизации через Telegram Login Widget.
|
||||
*
|
||||
* Логика в двух независимых ветках:
|
||||
* 1. Если в URL уже есть токен (боту редиректит юзера обратно с `?token=` или `#token=`,
|
||||
* либо токен уже лежит в localStorage) — сразу проверяем его на `/api/me`.
|
||||
* 2. Если токена нет — показываем статус "awaiting" и инжектим Telegram Widget script;
|
||||
* после успешного логина в Telegram виджет дергает `window.onTelegramAuth`, мы шлём
|
||||
* данные пользователя на `/api/auth/telegram`, получаем токен и сохраняем его.
|
||||
*
|
||||
* Токен читается и из query, и из hash, потому что Telegram Login Widget в разных
|
||||
* сценариях редиректа (виджет на этой же странице vs. редирект из бота) кладёт токен
|
||||
* то в query-параметр, то в хэш — приходится поддерживать оба варианта.
|
||||
*
|
||||
* Статусы: processing (идёт проверка) / granted (успех, редирект в профиль) /
|
||||
* denied (сеть недоступна или подпись невалидна) / awaiting (ждём логина в Telegram).
|
||||
*/
|
||||
export function AuthClient({ dict, lang }: AuthClientProps) {
|
||||
const router = useRouter();
|
||||
const [status, setStatus] = useState<
|
||||
@@ -35,6 +55,8 @@ export function AuthClient({ dict, lang }: AuthClientProps) {
|
||||
const widgetInjected = useRef(false);
|
||||
|
||||
useEffect(() => {
|
||||
// Защита от двойного запуска в React 18 Strict Mode (эффект монтируется дважды
|
||||
// в dev-режиме) — без этого флага мы бы дважды дернули /api/me и задвоили логи.
|
||||
if (initialized.current) return;
|
||||
initialized.current = true;
|
||||
|
||||
@@ -45,6 +67,10 @@ export function AuthClient({ dict, lang }: AuthClientProps) {
|
||||
const hash = window.location.hash;
|
||||
let token = localStorage.getItem("nrxp_token");
|
||||
|
||||
// Токен может прийти двумя путями: как query-параметр (?token=...) или
|
||||
// в hash-фрагменте (#token=...) — зависит от того, как именно бот/виджет
|
||||
// сформировал редирект. Проверяем оба варианта и в любом случае вычищаем
|
||||
// токен из URL через replaceState, чтобы он не остался в истории браузера.
|
||||
const searchParams = new URLSearchParams(window.location.search);
|
||||
const queryToken = searchParams.get("token");
|
||||
|
||||
@@ -105,6 +131,9 @@ export function AuthClient({ dict, lang }: AuthClientProps) {
|
||||
authenticate();
|
||||
}, [dict, lang, router]);
|
||||
|
||||
// Регистрируем глобальный колбэк для Telegram Login Widget: сам виджет
|
||||
// подключается сторонним script-тегом ниже (см. следующий useEffect) и после
|
||||
// логина в Telegram вызывает window.onTelegramAuth с данными пользователя.
|
||||
useEffect(() => {
|
||||
window.onTelegramAuth = async (user: any) => {
|
||||
setStatus("processing");
|
||||
@@ -138,6 +167,10 @@ export function AuthClient({ dict, lang }: AuthClientProps) {
|
||||
};
|
||||
}, [dict, lang, router]);
|
||||
|
||||
// Инжектим официальный Telegram Widget script только когда реально дошли до
|
||||
// экрана "awaiting" (нет токена) — виджет сам рисует кнопку логина в контейнере
|
||||
// и после успеха вызывает window.onTelegramAuth. widgetInjected защищает от
|
||||
// повторной вставки script-тега при повторных ререндерах.
|
||||
useEffect(() => {
|
||||
if (
|
||||
status === "awaiting" &&
|
||||
|
||||
Reference in New Issue
Block a user