From f2398d359227a2557d13c1deba55ef50d440f8ce Mon Sep 17 00:00:00 2001 From: Untone Date: Mon, 29 Sep 2025 15:54:22 +0300 Subject: [PATCH] protected-route-fix --- panel/context/auth.tsx | 31 ++++++++++----- panel/styles.css | 75 +++++++++++++++++++++++++++++++++++++ panel/ui/ProtectedRoute.tsx | 16 ++++++-- panel/utils/auth.ts | 8 ++-- 4 files changed, 114 insertions(+), 16 deletions(-) diff --git a/panel/context/auth.tsx b/panel/context/auth.tsx index f2c005e6..b809e3c0 100644 --- a/panel/context/auth.tsx +++ b/panel/context/auth.tsx @@ -76,13 +76,19 @@ export const AuthProvider: Component = (props) => { // Инициализация авторизации при монтировании onMount(async () => { console.log('[AuthProvider] Performing auth initialization...') - + // 🍪 Для httpOnly cookies проверяем авторизацию через GraphQL запрос try { console.log('[AuthProvider] Checking authentication via GraphQL...') - - // Делаем тестовый запрос для проверки авторизации - const result = await query<{ me: { id: string } | null }>(`${location.origin}/graphql`, ` + + // Добавляем таймаут для запроса + const timeoutPromise = new Promise((_, reject) => + setTimeout(() => reject(new Error('Auth check timeout')), 10000) + ) + + const authPromise = query<{ me: { id: string } | null }>( + `${location.origin}/graphql`, + ` query CheckAuth { me { id @@ -90,8 +96,14 @@ export const AuthProvider: Component = (props) => { email } } - `) - + ` + ) + + // Делаем тестовый запрос для проверки авторизации с таймаутом + const result = (await Promise.race([authPromise, timeoutPromise])) as { + me: { id: string; name: string; email: string } | null + } + if (result?.me?.id) { console.log('[AuthProvider] User authenticated via httpOnly cookie:', result.me.id) setIsAuthenticated(true) @@ -102,10 +114,11 @@ export const AuthProvider: Component = (props) => { } catch (error) { console.log('[AuthProvider] Authentication check failed:', error) setIsAuthenticated(false) + } finally { + // Всегда устанавливаем ready в true, даже при ошибке + console.log('[AuthProvider] Auth initialization complete, ready for requests') + setIsReady(true) } - - console.log('[AuthProvider] Auth initialization complete, ready for requests') - setIsReady(true) }) const login = async (username: string, password: string) => { diff --git a/panel/styles.css b/panel/styles.css index 29495139..88a014b7 100644 --- a/panel/styles.css +++ b/panel/styles.css @@ -177,6 +177,81 @@ body { } } +/* Auth Error Screen */ +.auth-error-screen { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + min-height: 100vh; + background-color: var(--background-color); + padding: 2rem; +} + +.auth-error-content { + text-align: center; + max-width: 500px; + padding: 2rem; + background-color: var(--card-background); + border-radius: var(--border-radius); + border: 1px solid var(--border-color); + box-shadow: var(--shadow-sm); +} + +.auth-error-content h2 { + color: var(--danger-color); + font-size: var(--font-size-xl); + margin-bottom: 1rem; + font-weight: 600; +} + +.auth-error-content p { + color: var(--text-color-light); + font-size: var(--font-size-base); + margin-bottom: 2rem; + line-height: 1.6; +} + +.auth-error-actions { + display: flex; + flex-direction: column; + gap: 1rem; +} + +.auth-error-actions .btn { + padding: 0.75rem 1.5rem; + border-radius: var(--border-radius); + border: none; + font-size: var(--font-size-base); + font-weight: 500; + cursor: pointer; + transition: all 0.2s ease; + text-decoration: none; + display: inline-block; +} + +.auth-error-actions .btn-primary { + background-color: var(--primary-color); + color: white; +} + +.auth-error-actions .btn-primary:hover { + background-color: var(--primary-color-dark); + transform: translateY(-1px); +} + +.auth-error-actions .btn-secondary { + background-color: transparent; + color: var(--text-color-light); + border: 1px solid var(--border-color); +} + +.auth-error-actions .btn-secondary:hover { + background-color: var(--hover-color); + border-color: var(--primary-color); + color: var(--text-color); +} + .error-message { background-color: var(--danger-light); border-left: 4px solid var(--danger-color); diff --git a/panel/ui/ProtectedRoute.tsx b/panel/ui/ProtectedRoute.tsx index f1c09bee..199f22c8 100644 --- a/panel/ui/ProtectedRoute.tsx +++ b/panel/ui/ProtectedRoute.tsx @@ -29,9 +29,19 @@ export const ProtectedRoute = () => { -
-
Перенаправление на страницу входа...
+
+
+

Доступ запрещен

+

У вас нет прав доступа к админ-панели или ваша сессия истекла.

+
+ + +
+
} > diff --git a/panel/utils/auth.ts b/panel/utils/auth.ts index deff5909..baf29ce5 100644 --- a/panel/utils/auth.ts +++ b/panel/utils/auth.ts @@ -4,8 +4,8 @@ */ // Экспортируем константы для использования в других модулях -export const AUTH_TOKEN_KEY = 'auth_token' // localStorage fallback -export const SESSION_COOKIE_NAME = 'session_token' // ✅ httpOnly cookie от backend +export const AUTH_TOKEN_KEY = 'auth_token' // localStorage fallback +export const SESSION_COOKIE_NAME = 'session_token' // ✅ httpOnly cookie от backend export const CSRF_TOKEN_KEY = 'csrf_token' /** @@ -99,6 +99,6 @@ export function checkAuthStatus(): boolean { // Реальная проверка авторизации произойдет при первом GraphQL запросе // Если cookie недействителен, backend вернет ошибку авторизации console.log('[Auth] Using httpOnly cookie authentication - status will be verified by backend') - - return true // ✅ Полагаемся на httpOnly cookie + backend проверку + + return true // ✅ Полагаемся на httpOnly cookie + backend проверку }