Update files
This commit is contained in:
parent
c3f2746723
commit
08ab000290
|
|
@ -2,7 +2,7 @@ const { parse, validate } = require('@telegram-apps/init-data-node');
|
|||
const crypto = require('crypto');
|
||||
const config = require('../config');
|
||||
|
||||
const MAX_AUTH_AGE_SECONDS = 5 * 60;
|
||||
const MAX_AUTH_AGE_SECONDS = 60 * 60; // 1 час
|
||||
|
||||
/**
|
||||
* Manual validation with base64 padding fix
|
||||
|
|
@ -101,7 +101,8 @@ function validateAndParseInitData(initDataRaw, botToken = null) {
|
|||
console.log('[Telegram] Parsed payload:', {
|
||||
hasUser: !!payload?.user,
|
||||
userId: payload?.user?.id,
|
||||
authDate: payload?.auth_date,
|
||||
auth_date: payload?.auth_date,
|
||||
authDate: payload?.authDate,
|
||||
allKeys: Object.keys(payload)
|
||||
});
|
||||
|
||||
|
|
@ -109,18 +110,27 @@ function validateAndParseInitData(initDataRaw, botToken = null) {
|
|||
throw new Error('Отсутствует пользователь в initData');
|
||||
}
|
||||
|
||||
// Check for auth_date - it might be authDate in parsed payload
|
||||
const authDate = Number(payload.auth_date || payload.authDate);
|
||||
// Check for authDate (camelCase from library) or auth_date (snake_case)
|
||||
const authDate = Number(payload.authDate || payload.auth_date);
|
||||
|
||||
if (!authDate) {
|
||||
console.error('[Telegram] Missing auth_date in payload:', payload);
|
||||
console.error('[Telegram] Missing authDate in payload:', payload);
|
||||
throw new Error('Отсутствует auth_date в initData');
|
||||
}
|
||||
|
||||
const now = Math.floor(Date.now() / 1000);
|
||||
const age = Math.abs(now - authDate);
|
||||
|
||||
if (Math.abs(now - authDate) > MAX_AUTH_AGE_SECONDS) {
|
||||
throw new Error('Данные авторизации устарели');
|
||||
console.log('[Telegram] Auth date check:', {
|
||||
authDate,
|
||||
now,
|
||||
age,
|
||||
maxAge: MAX_AUTH_AGE_SECONDS,
|
||||
expired: age > MAX_AUTH_AGE_SECONDS
|
||||
});
|
||||
|
||||
if (age > MAX_AUTH_AGE_SECONDS) {
|
||||
throw new Error(`Данные авторизации устарели (возраст: ${age}с, макс: ${MAX_AUTH_AGE_SECONDS}с)`);
|
||||
}
|
||||
|
||||
console.log('[Telegram] initData validation complete');
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { useState, useEffect, useRef } from 'react'
|
|||
import { initTelegramApp } from './utils/telegram'
|
||||
import { verifyAuth } from './utils/api'
|
||||
import { initTheme } from './utils/theme'
|
||||
import { startInitDataChecker, stopInitDataChecker } from './utils/initDataChecker'
|
||||
import Layout from './components/Layout'
|
||||
import Feed from './pages/Feed'
|
||||
import Search from './pages/Search'
|
||||
|
|
@ -28,6 +29,13 @@ function AppContent() {
|
|||
initAppCalled.current = true
|
||||
initApp()
|
||||
}
|
||||
|
||||
// Запустить проверку initData
|
||||
startInitDataChecker()
|
||||
|
||||
return () => {
|
||||
stopInitDataChecker()
|
||||
}
|
||||
}, [])
|
||||
|
||||
const waitForInitData = async () => {
|
||||
|
|
|
|||
|
|
@ -42,6 +42,36 @@ api.interceptors.request.use((config) => {
|
|||
return config;
|
||||
});
|
||||
|
||||
// Response interceptor для обработки устаревших токенов
|
||||
api.interceptors.response.use(
|
||||
(response) => response,
|
||||
(error) => {
|
||||
const status = error?.response?.status;
|
||||
const errorMessage = error?.response?.data?.error || '';
|
||||
|
||||
// Если токен устарел или невалиден - перезагрузить приложение
|
||||
if (status === 401 && (
|
||||
errorMessage.includes('устарели') ||
|
||||
errorMessage.includes('expired') ||
|
||||
errorMessage.includes('Неверная подпись')
|
||||
)) {
|
||||
console.warn('[API] Auth token expired or invalid, reloading app...');
|
||||
|
||||
// Показать уведомление пользователю
|
||||
const tg = window.Telegram?.WebApp;
|
||||
if (tg?.showAlert) {
|
||||
tg.showAlert('Сессия устарела. Перезагрузка...', () => {
|
||||
window.location.reload();
|
||||
});
|
||||
} else {
|
||||
setTimeout(() => window.location.reload(), 1000);
|
||||
}
|
||||
}
|
||||
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
|
||||
// Auth API
|
||||
export const signInWithTelegram = async (initData) => {
|
||||
const response = await api.post('/auth/signin', { initData })
|
||||
|
|
|
|||
|
|
@ -0,0 +1,130 @@
|
|||
/**
|
||||
* Утилита для проверки свежести initData
|
||||
* Предотвращает запросы с устаревшими токенами
|
||||
*/
|
||||
|
||||
const MAX_INIT_DATA_AGE = 55 * 60 * 1000; // 55 минут (до истечения часа на бекенде)
|
||||
const CHECK_INTERVAL = 5 * 60 * 1000; // Проверять каждые 5 минут
|
||||
|
||||
let checkTimer = null;
|
||||
let lastReloadTime = Date.now();
|
||||
|
||||
/**
|
||||
* Извлекает auth_date из initData
|
||||
*/
|
||||
function extractAuthDate(initData) {
|
||||
try {
|
||||
const params = new URLSearchParams(initData);
|
||||
const authDateStr = params.get('auth_date');
|
||||
return authDateStr ? parseInt(authDateStr, 10) : null;
|
||||
} catch (error) {
|
||||
console.error('[InitDataChecker] Failed to parse auth_date:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Проверяет, устарел ли initData
|
||||
*/
|
||||
function isInitDataExpired(initData) {
|
||||
const authDate = extractAuthDate(initData);
|
||||
if (!authDate) return false;
|
||||
|
||||
const authTime = authDate * 1000; // Конвертируем в миллисекунды
|
||||
const age = Date.now() - authTime;
|
||||
|
||||
return age > MAX_INIT_DATA_AGE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Перезагружает приложение, если initData устарел
|
||||
*/
|
||||
function checkAndReloadIfNeeded() {
|
||||
const tg = window.Telegram?.WebApp;
|
||||
const initData = tg?.initData;
|
||||
|
||||
if (!initData) {
|
||||
console.warn('[InitDataChecker] No initData available');
|
||||
return;
|
||||
}
|
||||
|
||||
if (isInitDataExpired(initData)) {
|
||||
console.warn('[InitDataChecker] InitData expired, reloading...');
|
||||
|
||||
// Предотвращаем частые перезагрузки (не чаще раза в минуту)
|
||||
const timeSinceLastReload = Date.now() - lastReloadTime;
|
||||
if (timeSinceLastReload < 60 * 1000) {
|
||||
console.warn('[InitDataChecker] Recent reload detected, skipping...');
|
||||
return;
|
||||
}
|
||||
|
||||
lastReloadTime = Date.now();
|
||||
|
||||
if (tg?.showAlert) {
|
||||
tg.showAlert('Обновление сессии...', () => {
|
||||
window.location.reload();
|
||||
});
|
||||
} else {
|
||||
window.location.reload();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Запускает периодическую проверку initData
|
||||
*/
|
||||
export function startInitDataChecker() {
|
||||
// Проверить сразу
|
||||
checkAndReloadIfNeeded();
|
||||
|
||||
// Проверять периодически
|
||||
if (checkTimer) {
|
||||
clearInterval(checkTimer);
|
||||
}
|
||||
|
||||
checkTimer = setInterval(checkAndReloadIfNeeded, CHECK_INTERVAL);
|
||||
|
||||
console.log('[InitDataChecker] Started with interval:', CHECK_INTERVAL / 1000, 'seconds');
|
||||
}
|
||||
|
||||
/**
|
||||
* Останавливает проверку
|
||||
*/
|
||||
export function stopInitDataChecker() {
|
||||
if (checkTimer) {
|
||||
clearInterval(checkTimer);
|
||||
checkTimer = null;
|
||||
console.log('[InitDataChecker] Stopped');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Получает информацию о текущем состоянии initData
|
||||
*/
|
||||
export function getInitDataInfo() {
|
||||
const tg = window.Telegram?.WebApp;
|
||||
const initData = tg?.initData;
|
||||
|
||||
if (!initData) {
|
||||
return { available: false };
|
||||
}
|
||||
|
||||
const authDate = extractAuthDate(initData);
|
||||
if (!authDate) {
|
||||
return { available: true, valid: false };
|
||||
}
|
||||
|
||||
const authTime = authDate * 1000;
|
||||
const age = Date.now() - authTime;
|
||||
const expired = age > MAX_INIT_DATA_AGE;
|
||||
|
||||
return {
|
||||
available: true,
|
||||
valid: true,
|
||||
authDate: new Date(authTime),
|
||||
age: Math.floor(age / 1000), // в секундах
|
||||
expired,
|
||||
remainingTime: expired ? 0 : Math.floor((MAX_INIT_DATA_AGE - age) / 1000) // в секундах
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -23,6 +23,36 @@ api.interceptors.request.use((config) => {
|
|||
return config;
|
||||
});
|
||||
|
||||
// Response interceptor для обработки устаревших токенов
|
||||
api.interceptors.response.use(
|
||||
(response) => response,
|
||||
(error) => {
|
||||
const status = error?.response?.status;
|
||||
const errorMessage = error?.response?.data?.error || '';
|
||||
|
||||
// Если токен устарел или невалиден - перезагрузить приложение
|
||||
if (status === 401 && (
|
||||
errorMessage.includes('устарели') ||
|
||||
errorMessage.includes('expired') ||
|
||||
errorMessage.includes('Неверная подпись')
|
||||
)) {
|
||||
console.warn('[Moderation API] Auth token expired or invalid, reloading app...');
|
||||
|
||||
// Показать уведомление пользователю
|
||||
const tg = window.Telegram?.WebApp;
|
||||
if (tg?.showAlert) {
|
||||
tg.showAlert('Сессия устарела. Перезагрузка...', () => {
|
||||
window.location.reload();
|
||||
});
|
||||
} else {
|
||||
setTimeout(() => window.location.reload(), 1000);
|
||||
}
|
||||
}
|
||||
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
|
||||
export const verifyAuth = () => api.post('/mod-app/auth/verify').then((res) => res.data.user)
|
||||
|
||||
export const fetchUsers = (params = {}) =>
|
||||
|
|
|
|||
Loading…
Reference in New Issue