авторизация fixed

This commit is contained in:
2025-12-18 08:33:12 +03:00
parent 4e4571b3db
commit b5b9504019
6 changed files with 161 additions and 293 deletions

View File

@@ -9,8 +9,8 @@ import { DraftsList } from "./pages/DraftsList";
import { SettingsPage } from "./pages/SettingsPage";
import { UNAUTHORIZED_EVENT } from "./services/api";
// Компонент заглушки для 401 ошибки
const UnauthorizedScreen = () => (
// Компонент-заглушка для внешних браузеров
const NotInTelegramScreen = () => (
<div
style={{
height: "100vh",
@@ -18,15 +18,16 @@ const UnauthorizedScreen = () => (
alignItems: "center",
justifyContent: "center",
background: "#fff",
padding: 20,
}}
>
<Result
status="403"
title="Доступ запрещен"
subTitle="Мы не нашли вас в базе данных. Пожалуйста, запустите бота и настройте сервер."
status="warning"
title="Доступ ограничен"
subTitle="Пожалуйста, откройте это приложение через официального Telegram бота @RmserBot для корректной работы."
extra={
<Button type="primary" href="https://t.me/RmserBot" target="_blank">
Перейти в бота @RmserBot
Перейти в бота
</Button>
}
/>
@@ -35,20 +36,47 @@ const UnauthorizedScreen = () => (
function App() {
const [isUnauthorized, setIsUnauthorized] = useState(false);
const tg = window.Telegram?.WebApp;
// Проверяем, есть ли данные от Telegram
const isInTelegram = !!tg?.initData;
useEffect(() => {
const handleUnauthorized = () => setIsUnauthorized(true);
// Подписываемся на событие из api.ts
window.addEventListener(UNAUTHORIZED_EVENT, handleUnauthorized);
if (tg) {
tg.expand(); // Расширяем приложение на все окно
}
return () => {
window.removeEventListener(UNAUTHORIZED_EVENT, handleUnauthorized);
};
}, []);
}, [tg]);
// Если открыто не в Telegram — блокируем всё
if (!isInTelegram) {
return <NotInTelegramScreen />;
}
// Если бэкенд вернул 401
if (isUnauthorized) {
return <UnauthorizedScreen />;
return (
<div
style={{
height: "100vh",
display: "flex",
alignItems: "center",
justifyContent: "center",
}}
>
<Result
status="403"
title="Ошибка доступа"
subTitle="Не удалось подтвердить вашу личность. Попробуйте обновить страницу внутри Telegram."
/>
</div>
);
}
return (
@@ -56,14 +84,11 @@ function App() {
<BrowserRouter>
<Routes>
<Route path="/" element={<AppLayout />}>
{/* Если Dashboard удален, можно сделать редирект на invoices */}
<Route index element={<Navigate to="/invoices" replace />} />
<Route path="ocr" element={<OcrLearning />} />
<Route path="invoices" element={<DraftsList />} />
<Route path="invoice/:id" element={<InvoiceDraftPage />} />
<Route path="settings" element={<SettingsPage />} />
<Route path="*" element={<Navigate to="/" replace />} />
</Route>
</Routes>

View File

@@ -1,4 +1,5 @@
import axios from 'axios';
import { notification } from 'antd';
import type {
CatalogItem,
CreateInvoiceRequest,
@@ -30,9 +31,6 @@ const API_URL = import.meta.env.VITE_API_URL || 'http://localhost:8080/api';
// Телеграм объект
const tg = window.Telegram?.WebApp;
// ID для локальной разработки (Fallback)
const DEBUG_USER_ID = 665599275;
// Событие для глобальной обработки 401
export const UNAUTHORIZED_EVENT = 'rms_unauthorized';
@@ -43,31 +41,42 @@ const apiClient = axios.create({
},
});
// --- Request Interceptor (Авторизация) ---
// --- Request Interceptor (Авторизация через initData) ---
apiClient.interceptors.request.use((config) => {
// 1. Пробуем взять ID из Telegram WebApp
// 2. Ищем в URL параметрах (удобно для тестов в браузере: ?_tg_id=123)
// 3. Используем хардкод для локальной разработки
const urlParams = new URLSearchParams(window.location.search);
const paramId = urlParams.get('_tg_id');
const userId = tg?.initDataUnsafe?.user?.id || paramId || DEBUG_USER_ID;
const initData = tg?.initData;
if (userId) {
config.headers['X-Telegram-User-ID'] = userId.toString();
// Если initData пустая — мы не в Telegram. Блокируем запрос.
if (!initData) {
console.error('Запрос заблокирован: приложение запущено вне Telegram.');
return Promise.reject(new Error('MISSING_TELEGRAM_DATA'));
}
// Устанавливаем заголовок согласно новым требованиям
config.headers['Authorization'] = `Bearer ${initData}`;
return config;
});
// --- Response Interceptor (Обработка ошибок) ---
// --- Response Interceptor (Обработка ошибок и уведомления) ---
apiClient.interceptors.response.use(
(response) => response,
(error) => {
if (error.response && error.response.status === 401) {
// Генерируем кастомное событие, которое поймает App.tsx
// Глобальное уведомление об ошибке авторизации
notification.error({
message: 'Ошибка авторизации',
description: 'Ваша сессия в Telegram истекла или данные неверны. Попробуйте перезапустить бота.',
placement: 'top',
});
window.dispatchEvent(new Event(UNAUTHORIZED_EVENT));
}
// Если запрос был отменен нами (нет initData), не выводим стандартную ошибку API
if (error.message === 'MISSING_TELEGRAM_DATA') {
return Promise.reject(error);
}
console.error('API Error:', error);
return Promise.reject(error);
}

View File

@@ -1,7 +1,7 @@
/// <reference types="vite/client" />
interface TelegramWebApp {
initData: string;
initData: string; // Сырая строка с параметрами и хешем
initDataUnsafe: {
user?: {
id: number;
@@ -10,11 +10,9 @@ interface TelegramWebApp {
username?: string;
language_code?: string;
};
// ... другие поля по необходимости
};
close: () => void;
expand: () => void;
// ... другие методы
}
interface Window {