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 loading = ref(false); // Состояние загрузки для операций аутентификации // Вычисляемое свойство для проверки, аутентифицирован ли пользователь 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); console.log('Статус блокировки:', response.data.blocked); // Проверяем, не заблокирован ли пользователь if (response.data.blocked) { console.log('[Auth] Пользователь заблокирован, выполняем принудительный выход'); // Показываем уведомление о блокировке const notificationEvent = new CustomEvent('show-toast', { detail: { message: `Ваш аккаунт заблокирован. ${response.data.blockReason || 'Обратитесь в поддержку.'}`, type: 'error', duration: 10000 } }); window.dispatchEvent(notificationEvent); // Выходим из системы await logout(); return; } else { // Если пользователь НЕ заблокирован, проверяем и очищаем любые флаги блокировки console.log('[Auth] Пользователь не заблокирован, проверяем наличие старых флагов блокировки'); // Очищаем флаги блокировки из localStorage, если они есть const blockFlags = ['accountBlocked', 'deviceBlocked', 'wasAccountBlocked']; blockFlags.forEach(flag => { if (localStorage.getItem(flag)) { console.log(`[Auth] Очищаем флаг блокировки: ${flag}`); localStorage.removeItem(flag); } }); // Проверяем URL на наличие параметра blocked и очищаем его при необходимости const currentUrl = new URL(window.location); if (currentUrl.searchParams.has('blocked')) { console.log('[Auth] Обнаружен параметр blocked в URL, пользователь не заблокирован - очищаем'); currentUrl.searchParams.delete('blocked'); // Обновляем URL без перезагрузки страницы window.history.replaceState({}, '', currentUrl.toString()); } // Сбрасываем флаг блокировки аккаунта в socketService try { const { resetAccountBlockedFlag } = await import('./services/socketService'); if (typeof resetAccountBlockedFlag === 'function') { resetAccountBlockedFlag(); console.log('[Auth] Сброшен флаг блокировки в socketService'); } } catch (error) { console.log('[Auth] Не удалось сбросить флаг в socketService:', error.message); } // Показываем уведомление о том, что блокировка снята (если ранее была) if (currentUrl.searchParams.has('unblocked') || sessionStorage.getItem('wasBlocked') === 'true') { const notificationEvent = new CustomEvent('show-toast', { detail: { message: 'Блокировка вашего аккаунта была снята. Добро пожаловать!', type: 'success', duration: 5000 } }); window.dispatchEvent(notificationEvent); // Очищаем флаг из sessionStorage sessionStorage.removeItem('wasBlocked'); // Очищаем параметр unblocked из URL if (currentUrl.searchParams.has('unblocked')) { currentUrl.searchParams.delete('unblocked'); window.history.replaceState({}, '', currentUrl.toString()); } } } // Сохраняем данные пользователя 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) { console.log('[Auth] Пользователь заблокирован согласно ответу сервера'); // Сохраняем флаг блокировки в sessionStorage для следующей сессии sessionStorage.setItem('wasBlocked', 'true'); // Показываем уведомление о блокировке const notificationEvent = new CustomEvent('show-toast', { detail: { message: error.response.data.message || 'Ваш аккаунт заблокирован администратором.', type: 'error', duration: 10000 } }); window.dispatchEvent(notificationEvent); } // Если токен невалиден или пользователь заблокирован, очищаем его и данные пользователя await logout(); // Вызываем logout, чтобы очистить всё } } else { console.log('Токен не найден, пользователь не загружен.'); } } // Функция входа async function login(credentials) { try { loading.value = true; const response = await api.login(credentials); if (response?.data) { setUserData(response.data, response.data.token); router.push('/'); // Перенаправляем на главную после успешного входа return response.data; } } catch (error) { console.error('[Auth] Ошибка входа:', error); // Обработка специального случая блокировки аккаунта if (error.response?.status === 403 && error.response?.data?.blocked) { const errorData = error.response.data; console.log('[Auth] Аккаунт заблокирован:', errorData.message); // Просто перенаправляем на страницу входа с параметром блокировки router.replace({ path: '/login', query: { blocked: 'true' } }); } throw error; } finally { loading.value = false; } } // Функция регистрации 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 notificationEvent = new CustomEvent('show-toast', { detail: { message: data?.message || 'Ваш аккаунт был заблокирован администратором.', type: 'error', duration: 10000 } }); window.dispatchEvent(notificationEvent); // Выходим из системы 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); // Показываем уведомление о разблокировке const notificationEvent = new CustomEvent('show-toast', { detail: { message: data?.message || 'Ваш аккаунт был разблокирован администратором.', type: 'success', duration: 5000 } }); window.dispatchEvent(notificationEvent); // Если пользователь уже на странице логина, добавляем параметр unblocked=true if (window.location.pathname === '/login') { router.replace({ path: '/login', query: { unblocked: 'true' } }); } else { // Перенаправляем на страницу входа с параметром разблокировки router.replace({ path: '/login', query: { unblocked: 'true' } }); } } // Экспортируем то, что понадобится в компонентах export function useAuth() { return { user, token, isAuthenticated, login, register, logout, fetchUser, // Эту функцию нужно будет вызвать при инициализации приложения handleAccountBlocked, // Добавляем функцию обработки блокировки аккаунта handleAccountUnblocked // Добавляем функцию обработки разблокировки аккаунта }; } // Экспортируем функции напрямую для использования в других частях приложения export { handleAccountBlocked, handleAccountUnblocked };