2025-05-21 22:13:09 +07:00
|
|
|
|
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('Неверный формат ответа от сервера');
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-25 23:36:51 +07:00
|
|
|
|
// Добавляем дополнительное логирование для отладки
|
|
|
|
|
console.log('Получены данные пользователя:', response.data);
|
|
|
|
|
console.log('Значение isAdmin в ответе:', response.data.isAdmin);
|
|
|
|
|
|
2025-05-21 22:13:09 +07:00
|
|
|
|
// Сохраняем данные пользователя
|
|
|
|
|
user.value = response.data;
|
|
|
|
|
console.log('Пользователь успешно загружен:', user.value);
|
|
|
|
|
console.log('isAuthenticated после загрузки пользователя:', isAuthenticated.value);
|
2025-05-25 23:36:51 +07:00
|
|
|
|
console.log('Пользователь является администратором:', user.value.isAdmin ? 'Да' : 'Нет');
|
2025-05-21 22:13:09 +07:00
|
|
|
|
} 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('Пользователь вышел из системы.');
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-26 15:52:26 +07:00
|
|
|
|
// Функция обработки события блокировки аккаунта
|
|
|
|
|
function handleAccountBlocked(data) {
|
2025-05-26 16:56:49 +07:00
|
|
|
|
console.log('[Auth] Экстренная обработка блокировки аккаунта:', data);
|
2025-05-26 16:49:20 +07:00
|
|
|
|
|
2025-05-26 16:56:49 +07:00
|
|
|
|
try {
|
|
|
|
|
// Создаем объект с информацией о блокировке
|
|
|
|
|
const blockInfo = {
|
|
|
|
|
blocked: true,
|
|
|
|
|
message: data?.message || 'Ваш аккаунт был заблокирован администратором.',
|
|
|
|
|
reason: data?.reason || 'Нарушение правил сервиса',
|
|
|
|
|
timestamp: new Date().toISOString()
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Сохраняем информацию о блокировке в localStorage для отображения на странице логина
|
|
|
|
|
localStorage.setItem('accountBlockedInfo', JSON.stringify(blockInfo));
|
|
|
|
|
|
2025-05-26 19:00:11 +07:00
|
|
|
|
// Сохраняем данные текущей сессии для последующего восстановления при разблокировке
|
|
|
|
|
if (user.value && token.value) {
|
|
|
|
|
// Сохраняем только необходимые данные для восстановления сессии
|
|
|
|
|
const sessionBackup = {
|
|
|
|
|
userId: user.value._id,
|
|
|
|
|
email: user.value.email,
|
|
|
|
|
name: user.value.name,
|
|
|
|
|
token: token.value,
|
|
|
|
|
timestamp: new Date().toISOString()
|
|
|
|
|
};
|
|
|
|
|
// Сохраняем резервную копию данных сессии
|
|
|
|
|
localStorage.setItem('blockedSessionBackup', JSON.stringify(sessionBackup));
|
|
|
|
|
console.log('[Auth] Данные сессии сохранены для последующего восстановления');
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-26 16:56:49 +07:00
|
|
|
|
// Немедленно очищаем все данные пользователя
|
|
|
|
|
console.log('[Auth] Очистка данных пользователя из-за блокировки');
|
|
|
|
|
user.value = null;
|
|
|
|
|
token.value = null;
|
|
|
|
|
|
2025-05-26 19:00:11 +07:00
|
|
|
|
// Очистка localStorage - удаляем токен, но сохраняем резервную копию сессии
|
2025-05-26 16:56:49 +07:00
|
|
|
|
localStorage.removeItem('userToken');
|
|
|
|
|
localStorage.removeItem('userSettings');
|
|
|
|
|
localStorage.removeItem('userPreferences');
|
|
|
|
|
|
|
|
|
|
// Удаляем токены доступа для API запросов
|
|
|
|
|
if (typeof api.setAuthToken === 'function') {
|
|
|
|
|
api.setAuthToken(null);
|
|
|
|
|
console.log('[Auth] Токен авторизации API удален');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Пробуем отменить активные запросы
|
|
|
|
|
if (typeof api.cancelActiveRequests === 'function') {
|
|
|
|
|
api.cancelActiveRequests();
|
|
|
|
|
console.log('[Auth] Активные запросы API отменены');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Устанавливаем флаг в SessionStorage для индикации блокировки при перезагрузке страницы
|
|
|
|
|
sessionStorage.setItem('forcedLogout', 'blocked');
|
|
|
|
|
|
|
|
|
|
// Принудительный перенаправление на страницу логина с флагом блокировки
|
|
|
|
|
console.log('[Auth] Перенаправление на страницу входа с параметром blocked=true');
|
|
|
|
|
|
|
|
|
|
// Используем прямое изменение URL для полной перезагрузки страницы
|
|
|
|
|
// Это гарантирует, что все состояния будут сброшены
|
|
|
|
|
window.location.href = '/login?blocked=true&reason=' + encodeURIComponent(blockInfo.reason);
|
|
|
|
|
|
|
|
|
|
// Не используем router.push, т.к. он не всегда работает корректно при критических ошибках
|
|
|
|
|
// router.push({ name: 'Login', query: { blocked: 'true', reason: blockInfo.reason } });
|
|
|
|
|
|
|
|
|
|
console.log('[Auth] Пользователь был разлогинен из-за блокировки аккаунта.');
|
|
|
|
|
return true; // Возвращаем true, чтобы вызывающий код знал, что выход выполнен
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('[Auth] Критическая ошибка при обработке блокировки аккаунта:', error);
|
|
|
|
|
|
|
|
|
|
// В случае любой ошибки, выполняем аварийный выход
|
|
|
|
|
window.location.href = '/login?blocked=true&emergency=true';
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2025-05-26 15:52:26 +07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Функция обработки события разблокировки аккаунта
|
2025-05-26 19:00:11 +07:00
|
|
|
|
async function handleAccountUnblocked(data) {
|
|
|
|
|
console.log('[Auth] Получено уведомление о разблокировке аккаунта:', data);
|
2025-05-26 15:52:26 +07:00
|
|
|
|
|
|
|
|
|
// Удаляем информацию о блокировке, если она была сохранена
|
|
|
|
|
localStorage.removeItem('accountBlockedInfo');
|
|
|
|
|
|
2025-05-26 19:00:11 +07:00
|
|
|
|
// Сохраняем информацию о разблокировке для отображения при следующем входе
|
|
|
|
|
const unblockedInfo = {
|
2025-05-26 15:52:26 +07:00
|
|
|
|
unblocked: true,
|
|
|
|
|
message: data?.message || 'Ваш аккаунт был разблокирован.',
|
|
|
|
|
timestamp: new Date().toISOString()
|
2025-05-26 19:00:11 +07:00
|
|
|
|
};
|
|
|
|
|
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' } });
|
|
|
|
|
}
|
2025-05-26 15:52:26 +07:00
|
|
|
|
|
2025-05-26 19:00:11 +07:00
|
|
|
|
return false;
|
2025-05-26 15:52:26 +07:00
|
|
|
|
}
|
|
|
|
|
|
2025-05-21 22:13:09 +07:00
|
|
|
|
// Экспортируем то, что понадобится в компонентах
|
|
|
|
|
export function useAuth() {
|
|
|
|
|
return {
|
|
|
|
|
user,
|
|
|
|
|
token,
|
|
|
|
|
isAuthenticated,
|
|
|
|
|
login,
|
|
|
|
|
register,
|
|
|
|
|
logout,
|
|
|
|
|
fetchUser, // Эту функцию нужно будет вызвать при инициализации приложения
|
2025-05-26 15:52:26 +07:00
|
|
|
|
handleAccountBlocked, // Добавляем функцию обработки блокировки аккаунта
|
|
|
|
|
handleAccountUnblocked // Добавляем функцию обработки разблокировки аккаунта
|
2025-05-21 22:13:09 +07:00
|
|
|
|
};
|
|
|
|
|
}
|