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 crypto = require('crypto');
|
||||||
const config = require('../config');
|
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
|
* Manual validation with base64 padding fix
|
||||||
|
|
@ -101,7 +101,8 @@ function validateAndParseInitData(initDataRaw, botToken = null) {
|
||||||
console.log('[Telegram] Parsed payload:', {
|
console.log('[Telegram] Parsed payload:', {
|
||||||
hasUser: !!payload?.user,
|
hasUser: !!payload?.user,
|
||||||
userId: payload?.user?.id,
|
userId: payload?.user?.id,
|
||||||
authDate: payload?.auth_date,
|
auth_date: payload?.auth_date,
|
||||||
|
authDate: payload?.authDate,
|
||||||
allKeys: Object.keys(payload)
|
allKeys: Object.keys(payload)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -109,18 +110,27 @@ function validateAndParseInitData(initDataRaw, botToken = null) {
|
||||||
throw new Error('Отсутствует пользователь в initData');
|
throw new Error('Отсутствует пользователь в initData');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for auth_date - it might be authDate in parsed payload
|
// Check for authDate (camelCase from library) or auth_date (snake_case)
|
||||||
const authDate = Number(payload.auth_date || payload.authDate);
|
const authDate = Number(payload.authDate || payload.auth_date);
|
||||||
|
|
||||||
if (!authDate) {
|
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');
|
throw new Error('Отсутствует auth_date в initData');
|
||||||
}
|
}
|
||||||
|
|
||||||
const now = Math.floor(Date.now() / 1000);
|
const now = Math.floor(Date.now() / 1000);
|
||||||
|
const age = Math.abs(now - authDate);
|
||||||
|
|
||||||
if (Math.abs(now - authDate) > MAX_AUTH_AGE_SECONDS) {
|
console.log('[Telegram] Auth date check:', {
|
||||||
throw new Error('Данные авторизации устарели');
|
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');
|
console.log('[Telegram] initData validation complete');
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import { useState, useEffect, useRef } from 'react'
|
||||||
import { initTelegramApp } from './utils/telegram'
|
import { initTelegramApp } from './utils/telegram'
|
||||||
import { verifyAuth } from './utils/api'
|
import { verifyAuth } from './utils/api'
|
||||||
import { initTheme } from './utils/theme'
|
import { initTheme } from './utils/theme'
|
||||||
|
import { startInitDataChecker, stopInitDataChecker } from './utils/initDataChecker'
|
||||||
import Layout from './components/Layout'
|
import Layout from './components/Layout'
|
||||||
import Feed from './pages/Feed'
|
import Feed from './pages/Feed'
|
||||||
import Search from './pages/Search'
|
import Search from './pages/Search'
|
||||||
|
|
@ -28,6 +29,13 @@ function AppContent() {
|
||||||
initAppCalled.current = true
|
initAppCalled.current = true
|
||||||
initApp()
|
initApp()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Запустить проверку initData
|
||||||
|
startInitDataChecker()
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
stopInitDataChecker()
|
||||||
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const waitForInitData = async () => {
|
const waitForInitData = async () => {
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,36 @@ api.interceptors.request.use((config) => {
|
||||||
return 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
|
// Auth API
|
||||||
export const signInWithTelegram = async (initData) => {
|
export const signInWithTelegram = async (initData) => {
|
||||||
const response = await api.post('/auth/signin', { 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;
|
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 verifyAuth = () => api.post('/mod-app/auth/verify').then((res) => res.data.user)
|
||||||
|
|
||||||
export const fetchUsers = (params = {}) =>
|
export const fetchUsers = (params = {}) =>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue