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); // Проверяем, не заблокирован ли пользователь if (response.data.blocked) { console.log('[Auth] Пользователь заблокирован, выполняем принудительный выход'); // Сохраняем информацию о блокировке const blockedInfo = { blocked: true, message: 'Ваш аккаунт заблокирован администратором.', reason: response.data.blockReason || 'Нарушение правил сервиса', timestamp: new Date().toISOString() }; localStorage.setItem('accountBlockedInfo', JSON.stringify(blockedInfo)); // Выходим из системы await logout(); return; } // Проверяем, была ли разблокировка (если есть информация о предыдущей блокировке) const blockedInfoStr = localStorage.getItem('accountBlockedInfo'); if (blockedInfoStr) { console.log('[Auth] Обнаружена предыдущая блокировка, пользователь теперь разблокирован'); // Удаляем информацию о блокировке localStorage.removeItem('accountBlockedInfo'); // Показываем уведомление о разблокировке setTimeout(() => { const notificationEvent = new CustomEvent('show-toast', { detail: { message: 'Ваш аккаунт был разблокирован. Добро пожаловать обратно!', type: 'success', duration: 5000 } }); window.dispatchEvent(notificationEvent); }, 1000); } // Сохраняем данные пользователя 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); // Проверяем, не связана ли ошибка с блокировкой if (error.response && error.response.status === 403 && error.response.data.message && error.response.data.message.includes('заблокирован')) { console.log('[Auth] Пользователь заблокирован согласно ответу сервера'); // Сохраняем информацию о блокировке const blockedInfo = { blocked: true, message: error.response.data.message || 'Ваш аккаунт заблокирован администратором.', reason: error.response.data.reason || 'Нарушение правил сервиса', timestamp: new Date().toISOString() }; localStorage.setItem('accountBlockedInfo', JSON.stringify(blockedInfo)); } // Если токен невалиден, очищаем его и данные пользователя 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); // Проверяем, была ли предыдущая блокировка и произошла ли разблокировка const wasBlocked = localStorage.getItem('accountBlockedInfo'); const sessionBackup = localStorage.getItem('blockedSessionBackup'); if (wasBlocked || sessionBackup) { console.log('[Auth] Обнаружена предыдущая блокировка, пользователь теперь может войти - значит разблокирован'); // Очищаем данные о блокировке localStorage.removeItem('accountBlockedInfo'); localStorage.removeItem('blockedSessionBackup'); // Показываем уведомление о разблокировке setTimeout(() => { const notificationEvent = new CustomEvent('show-toast', { detail: { message: 'Ваш аккаунт был разблокирован. Добро пожаловать обратно!', type: 'success', duration: 5000 } }); window.dispatchEvent(notificationEvent); }, 1000); } // Перед сохранением данных 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('Пользователь вышел из системы.'); } // Функция обработки события блокировки аккаунта async function handleAccountBlocked(data) { console.log('[Auth] Получено уведомление о блокировке аккаунта:', data); try { // Сохраняем данные о блокировке const blockedInfo = { blocked: true, message: data?.message || 'Ваш аккаунт был заблокирован администратором.', reason: data?.reason || 'Нарушение правил сервиса', timestamp: data?.timestamp || new Date().toISOString() }; localStorage.setItem('accountBlockedInfo', JSON.stringify(blockedInfo)); // Сохраняем текущие данные сессии для последующего восстановления if (user.value && token.value) { const sessionBackup = { userId: user.value._id, userName: user.value.name, token: token.value, timestamp: new Date().toISOString(), lastPath: window.location.pathname }; // Сохраняем резервную копию данных сессии localStorage.setItem('blockedSessionBackup', JSON.stringify(sessionBackup)); console.log('[Auth] Сохранена резервная копия данных сессии для пользователя:', user.value.name); } else { console.log('[Auth] Невозможно сохранить сессию - пользователь не авторизован'); } // Выходим из системы await logout(); // Перенаправляем на страницу входа с параметром блокировки router.replace({ path: '/login', query: { blocked: 'true' } }); } catch (error) { console.error('[Auth] Ошибка при обработке блокировки аккаунта:', error); // В случае ошибки все равно выполняем logout и перенаправление try { await logout(); router.replace('/login?blocked=true'); } catch (logoutErr) { console.error('[Auth] Ошибка при выходе после блокировки:', logoutErr); // Принудительное перенаправление window.location.href = '/login?blocked=true'; } } } // Функция обработки события разблокировки аккаунта 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 // Добавляем функцию обработки разблокировки аккаунта }; } // Экспортируем функции напрямую для использования в других частях приложения export { handleAccountBlocked, handleAccountUnblocked };