284 lines
14 KiB
JavaScript
284 lines
14 KiB
JavaScript
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 }; |