Reflex/src/auth.js
Professional 60c524caf6 фикс
2025-05-26 20:24:08 +07:00

284 lines
14 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 };