Reflex/src/auth.js
Professional a74cf5cb6c фикс
2025-05-26 19:16:21 +07:00

283 lines
15 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 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('Пользователь вышел из системы.');
}
// Функция обработки события блокировки аккаунта
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 // Добавляем функцию обработки разблокировки аккаунта
};
}