import { ref, computed } from 'vue'; import api from './services/api'; // Наш API сервис import router from './router'; // Vue Router для перенаправлений // Реактивные переменные для состояния пользователя и токена const user = ref(null); // Данные пользователя (или null, если не вошел) const token = ref(localStorage.getItem('userToken') || null); // Токен из localStorage или null // Вычисляемое свойство для проверки, аутентифицирован ли пользователь const isAuthenticated = computed(() => !!token.value && !!user.value); // Функция для установки токена и данных пользователя function setUserData(userData, userToken) { user.value = userData; token.value = userToken; localStorage.setItem('userToken', userToken); // Сохраняем токен в localStorage // Устанавливаем токен в заголовки Axios для последующих запросов (если не используется интерсептор) // Однако, наш интерсептор в api.js уже делает это, так что эта строка может быть избыточна, // но не повредит, если интерсептор по какой-то причине не сработает сразу. // api.defaults.headers.common['Authorization'] = `Bearer ${userToken}`; } // Функция для загрузки данных пользователя, если токен есть (например, при обновлении страницы) async function fetchUser() { if (token.value) { try { console.log('Пытаемся загрузить пользователя с токеном из localStorage...'); const response = await api.getMe(); // Используем наш API сервис console.log('Ответ от /auth/me:', response); // Проверяем структуру ответа if (!response.data) { throw new Error('Неверный формат ответа от сервера'); } // Добавляем дополнительное логирование для отладки console.log('Получены данные пользователя:', response.data); console.log('Значение isAdmin в ответе:', response.data.isAdmin); // Сохраняем данные пользователя user.value = response.data; console.log('Пользователь успешно загружен:', user.value); console.log('isAuthenticated после загрузки пользователя:', isAuthenticated.value); console.log('Пользователь является администратором:', user.value.isAdmin ? 'Да' : 'Нет'); } catch (error) { console.error('Не удалось загрузить пользователя по токену:', error.response ? error.response.data : error.message); // Если токен невалиден, очищаем его и данные пользователя await logout(); // Вызываем logout, чтобы очистить всё } } else { console.log('Токен не найден, пользователь не загружен.'); } } // Функция входа async function login(credentials) { try { console.log('Попытка входа с учетными данными:', { email: credentials.email }); const response = await api.login(credentials); console.log('Ответ сервера при входе:', response); // Проверяем структуру данных в ответе if (!response.data || !response.data.token) { throw new Error('Неверный формат ответа от сервера: отсутствует токен'); } // response.data должен содержать { _id, name, email, token, message } const { token: userToken, ...userData } = response.data; // Убедимся, что userData содержит необходимые поля console.log('Данные пользователя для сохранения:', userData); // Перед сохранением данных console.log('isAuthenticated ДО входа:', isAuthenticated.value); console.log('Данные пользователя ДО входа:', user.value); // Сохраняем данные пользователя и токен setUserData(userData, userToken); // После сохранения данных, но до перенаправления console.log('isAuthenticated ПОСЛЕ входа:', isAuthenticated.value); console.log('Данные пользователя ПОСЛЕ входа:', user.value); console.log('Токен ПОСЛЕ входа:', token.value); // Принудительное обновление состояния setTimeout(() => { console.log('isAuthenticated после таймаута:', isAuthenticated.value); }, 0); router.push('/'); // Перенаправляем на главную после входа return true; } catch (error) { console.error('Ошибка входа:', error.response ? error.response.data : error.message); // Возвращаем сообщение об ошибке, чтобы отобразить в компоненте throw error.response ? error.response.data : new Error('Ошибка сети или сервера при входе'); } } // Функция регистрации async function register(userData) { try { const response = await api.register(userData); // response.data должен содержать { _id, name, email, token, message } const { token: userToken, ...newUserData } = response.data; setUserData(newUserData, userToken); // Обычно после регистрации мы либо сразу логиним пользователя, либо просим его войти. // Здесь мы его сразу "залогинили", сохранив токен и данные. router.push('/'); // Перенаправляем на главную (или на страницу подтверждения email, если бы она была) return true; } catch (error) { console.error('Ошибка регистрации:', error.response ? error.response.data : error.message); throw error.response ? error.response.data : new Error('Ошибка сети или сервера при регистрации'); } } // Функция выхода async function logout() { user.value = null; token.value = null; localStorage.removeItem('userToken'); // Удаляем токен из заголовков Axios (если устанавливали его напрямую) // delete apiClient.defaults.headers.common['Authorization']; // apiClient не экспортируется из api.js напрямую для этого // Наш интерсептор запросов в api.js перестанет добавлять токен, так как его не будет в localStorage. router.push('/login'); // Перенаправляем на страницу входа console.log('Пользователь вышел из системы.'); } // Функция обработки события блокировки аккаунта function handleAccountBlocked(data) { console.log('[Auth] Экстренная обработка блокировки аккаунта:', data); try { // Создаем объект с информацией о блокировке const blockInfo = { blocked: true, message: data?.message || 'Ваш аккаунт был заблокирован администратором.', reason: data?.reason || 'Нарушение правил сервиса', timestamp: new Date().toISOString() }; // Сохраняем информацию о блокировке в localStorage для отображения на странице логина localStorage.setItem('accountBlockedInfo', JSON.stringify(blockInfo)); // Сохраняем данные текущей сессии для последующего восстановления при разблокировке if (user.value && token.value) { // Сохраняем только необходимые данные для восстановления сессии const sessionBackup = { userId: user.value._id, email: user.value.email, name: user.value.name, token: token.value, timestamp: new Date().toISOString() }; // Сохраняем резервную копию данных сессии localStorage.setItem('blockedSessionBackup', JSON.stringify(sessionBackup)); console.log('[Auth] Данные сессии сохранены для последующего восстановления'); } // Немедленно очищаем все данные пользователя console.log('[Auth] Очистка данных пользователя из-за блокировки'); user.value = null; token.value = null; // Очистка localStorage - удаляем токен, но сохраняем резервную копию сессии localStorage.removeItem('userToken'); localStorage.removeItem('userSettings'); localStorage.removeItem('userPreferences'); // Удаляем токены доступа для API запросов if (typeof api.setAuthToken === 'function') { api.setAuthToken(null); console.log('[Auth] Токен авторизации API удален'); } // Пробуем отменить активные запросы if (typeof api.cancelActiveRequests === 'function') { api.cancelActiveRequests(); console.log('[Auth] Активные запросы API отменены'); } // Устанавливаем флаг в SessionStorage для индикации блокировки при перезагрузке страницы sessionStorage.setItem('forcedLogout', 'blocked'); // Принудительный перенаправление на страницу логина с флагом блокировки console.log('[Auth] Перенаправление на страницу входа с параметром blocked=true'); // Используем прямое изменение URL для полной перезагрузки страницы // Это гарантирует, что все состояния будут сброшены window.location.href = '/login?blocked=true&reason=' + encodeURIComponent(blockInfo.reason); // Не используем router.push, т.к. он не всегда работает корректно при критических ошибках // router.push({ name: 'Login', query: { blocked: 'true', reason: blockInfo.reason } }); console.log('[Auth] Пользователь был разлогинен из-за блокировки аккаунта.'); return true; // Возвращаем true, чтобы вызывающий код знал, что выход выполнен } catch (error) { console.error('[Auth] Критическая ошибка при обработке блокировки аккаунта:', error); // В случае любой ошибки, выполняем аварийный выход window.location.href = '/login?blocked=true&emergency=true'; return false; } } // Функция обработки события разблокировки аккаунта async function handleAccountUnblocked(data) { console.log('[Auth] Получено уведомление о разблокировке аккаунта:', data); // Удаляем информацию о блокировке, если она была сохранена localStorage.removeItem('accountBlockedInfo'); // Сохраняем информацию о разблокировке для отображения при следующем входе const unblockedInfo = { unblocked: true, message: data?.message || 'Ваш аккаунт был разблокирован.', timestamp: new Date().toISOString() }; localStorage.setItem('accountUnblockedInfo', JSON.stringify(unblockedInfo)); // Проверяем, есть ли сохраненная сессия для восстановления try { const sessionBackupStr = localStorage.getItem('blockedSessionBackup'); if (sessionBackupStr) { const sessionBackup = JSON.parse(sessionBackupStr); console.log('[Auth] Найдены данные для восстановления сессии:', sessionBackup); // Проверяем, что данные актуальны (не старше 30 дней) const backupTime = new Date(sessionBackup.timestamp); const now = new Date(); const thirtyDaysInMs = 30 * 24 * 60 * 60 * 1000; if (now - backupTime < thirtyDaysInMs && sessionBackup.userId && sessionBackup.token) { // Восстанавливаем сессию console.log('[Auth] Восстановление сессии пользователя после разблокировки'); // Устанавливаем токен в localStorage localStorage.setItem('userToken', sessionBackup.token); token.value = sessionBackup.token; // Пробуем загрузить актуальные данные пользователя try { console.log('[Auth] Загрузка актуальных данных пользователя'); await fetchUser(); if (user.value) { console.log('[Auth] Сессия успешно восстановлена, перенаправление в приложение'); // Показываем уведомление о восстановлении сессии const notificationEvent = new CustomEvent('show-toast', { detail: { message: 'Ваш аккаунт был разблокирован и сессия восстановлена.', type: 'success', duration: 5000 } }); window.dispatchEvent(notificationEvent); // Удаляем сохраненные данные сессии localStorage.removeItem('blockedSessionBackup'); // Перенаправление на главную страницу или на последнюю просмотренную страницу window.location.href = sessionBackup.lastPath || '/'; return true; } } catch (fetchError) { console.error('[Auth] Ошибка при загрузке данных пользователя:', fetchError); } } else { console.log('[Auth] Сохраненная сессия устарела или неполная, будет выполнен обычный вход'); // Удаляем устаревшие данные сессии localStorage.removeItem('blockedSessionBackup'); } } else { console.log('[Auth] Сохраненная сессия не найдена, показываем только уведомление о разблокировке'); } } catch (error) { console.error('[Auth] Ошибка при восстановлении сессии после разблокировки:', error); } // Если мы дошли до этой точки, значит автоматическое восстановление не удалось // или мы не нашли сохраненной сессии. Просто показываем уведомление. console.log('[Auth] Аккаунт пользователя разблокирован без автоматического входа'); // Если пользователь уже на странице логина, добавляем параметр unblocked=true if (window.location.pathname === '/login') { router.replace({ path: '/login', query: { unblocked: 'true' } }); } return false; } // Экспортируем то, что понадобится в компонентах export function useAuth() { return { user, token, isAuthenticated, login, register, logout, fetchUser, // Эту функцию нужно будет вызвать при инициализации приложения handleAccountBlocked, // Добавляем функцию обработки блокировки аккаунта handleAccountUnblocked // Добавляем функцию обработки разблокировки аккаунта }; }