This commit is contained in:
Professional 2025-05-26 20:16:53 +07:00
parent 1d4567411b
commit a80bb560db
4 changed files with 51 additions and 290 deletions

View File

@ -98,31 +98,6 @@ async function login(credentials) {
if (response?.data) {
setUserData(response.data, response.data.token);
// Очищаем данные о предыдущей блокировке при успешном входе
const wasBlocked = localStorage.getItem('accountBlockedInfo');
const sessionBackup = localStorage.getItem('blockedSessionBackup');
if (wasBlocked || sessionBackup) {
console.log('[Auth] Обнаружена предыдущая блокировка, пользователь теперь может войти - значит разблокирован');
// Очищаем данные о блокировке
localStorage.removeItem('accountBlockedInfo');
localStorage.removeItem('blockedSessionBackup');
// Показываем уведомление о разблокировке
setTimeout(() => {
const notificationEvent = new CustomEvent('show-toast', {
detail: {
message: 'Ваш аккаунт был разблокирован. Добро пожаловать обратно!',
type: 'success',
duration: 5000
}
});
window.dispatchEvent(notificationEvent);
}, 1000);
}
router.push('/'); // Перенаправляем на главную после успешного входа
return response.data;
}
@ -132,30 +107,10 @@ async function login(credentials) {
// Обработка специального случая блокировки аккаунта
if (error.response?.status === 403 && error.response?.data?.blocked) {
const errorData = error.response.data;
console.log('[Auth] Аккаунт заблокирован, сохраняем данные для восстановления сессии');
console.log('[Auth] Аккаунт заблокирован:', errorData.message);
// Сохраняем данные о блокировке
const blockedInfo = {
blocked: true,
message: errorData.message || 'Ваш аккаунт заблокирован администратором.',
timestamp: new Date().toISOString()
};
localStorage.setItem('accountBlockedInfo', JSON.stringify(blockedInfo));
// Сохраняем данные для восстановления сессии, если есть токен восстановления
if (errorData.restorationToken && errorData.userId && errorData.userName) {
const sessionBackup = {
userId: errorData.userId,
userName: errorData.userName,
token: errorData.restorationToken,
timestamp: new Date().toISOString(),
lastPath: '/', // По умолчанию главная страница
credentials: credentials // Сохраняем credentials для повторного входа
};
localStorage.setItem('blockedSessionBackup', JSON.stringify(sessionBackup));
console.log('[Auth] Сохранены данные для восстановления сессии заблокированного пользователя');
}
// Просто перенаправляем на страницу входа с параметром блокировки
router.replace({ path: '/login', query: { blocked: 'true' } });
}
throw error;
@ -198,33 +153,6 @@ 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] Невозможно сохранить сессию - пользователь не авторизован');
}
// Показываем уведомление о блокировке
const notificationEvent = new CustomEvent('show-toast', {
detail: {
@ -260,87 +188,23 @@ async function handleAccountBlocked(data) {
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: 'Ваш аккаунт был разблокирован и сессия восстановлена.',
message: data?.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' } });
} else {
// Перенаправляем на страницу входа с параметром разблокировки
router.replace({ path: '/login', query: { unblocked: 'true' } });
}
return false;
}
// Экспортируем то, что понадобится в компонентах

View File

@ -1,76 +1,22 @@
import axios from 'axios';
import { useAuth } from '../auth'; // Импортируем функцию авторизации
import { isAccountBlocked } from './socketService'; // Импортируем проверку блокировки из socketService
// Создаем экземпляр Axios с базовым URL нашего API
// Настраиваем базовый URL в зависимости от окружения
// Храним токены отмены запросов для возможности отмены при блокировке
const cancelTokenSources = new Map();
// Проверка блокировки аккаунта из localStorage
const checkAccountBlockStatus = () => {
try {
const blockedInfo = localStorage.getItem('accountBlockedInfo');
if (blockedInfo) {
const blockedData = JSON.parse(blockedInfo);
if (blockedData?.blocked === true) {
console.warn('[API] Обнаружен заблокированный аккаунт, запросы невозможны');
return true;
}
}
// Проверяем также флаг из socketService
if (isAccountBlocked && isAccountBlocked()) {
console.warn('[API] Аккаунт заблокирован согласно socketService, запросы невозможны');
return true;
}
return false;
} catch (e) {
console.error('[API] Ошибка при проверке статуса блокировки:', e);
return false;
}
};
// Определяем базовый URL в зависимости от среды выполнения
const getBaseUrl = () => {
// Всегда используем относительный путь /api.
// В режиме разработки Vite dev server будет проксировать эти запросы согласно настройке server.proxy в vite.config.js.
// В продакшене предполагается, что веб-сервер (например, Nginx) настроен аналогично для обработки /api.
// return '/api';
return import.meta.env.VITE_API_BASE_URL; // Используем переменную окружения Vite
};
const apiClient = axios.create({
baseURL: getBaseUrl(),
baseURL: import.meta.env.VITE_API_BASE_URL || '/api',
timeout: 10000,
headers: {
'Content-Type': 'application/json',
},
});
// (Опционально, но очень полезно) Перехватчик для добавления JWT токена к запросам
// Храним токены отмены запросов для возможности отмены при блокировке
const cancelTokenSources = new Map();
// Интерсептор запросов - добавляет токен авторизации к каждому запросу
apiClient.interceptors.request.use(
(config) => {
// Проверяем, заблокирован ли аккаунт, ПЕРЕД выполнением запроса
if (checkAccountBlockStatus()) {
// Если аккаунт заблокирован, отменяем запрос
const source = axios.CancelToken.source();
config.cancelToken = source.token;
source.cancel('Запрос отменен из-за блокировки аккаунта');
console.error('[API] Запрос отменен из-за блокировки аккаунта:', config.url);
// Перенаправляем на страницу логина, если пользователь пытается выполнять запросы
setTimeout(() => {
if (window.location.pathname !== '/login') {
window.location.href = '/login?blocked=true';
}
}, 0);
return Promise.reject(new Error('Аккаунт заблокирован'));
}
const token = localStorage.getItem('userToken'); // Предполагаем, что токен хранится в localStorage
const token = localStorage.getItem('userToken');
if (token) {
console.log('[API] Добавление токена к запросу:', config.url);
config.headers['Authorization'] = `Bearer ${token}`;
@ -94,7 +40,7 @@ apiClient.interceptors.request.use(
}
);
// (Опционально) Перехватчик ответов для обработки глобальных ошибок, например, 401
// Интерсептор ответов для обработки глобальных ошибок
apiClient.interceptors.response.use(
(response) => {
// Удаляем источник токена отмены после успешного ответа
@ -134,7 +80,6 @@ apiClient.interceptors.response.use(
console.error('[API] Неавторизованный запрос (401):', error.config.url);
// Обходим замыкание, чтобы избежать проблем с импортом
// При использовании такого подхода, logout будет вызван после текущего цикла выполнения JS
setTimeout(() => {
const { logout } = useAuth();
logout();
@ -165,10 +110,9 @@ const setAuthToken = (token) => {
// Экспортируем функции для конкретных эндпоинтов
export default {
// Добавляем новые утилитарные методы
// Утилитарные методы
cancelActiveRequests,
setAuthToken,
checkAccountBlockStatus,
register(userData) {
return apiClient.post('/auth/register', userData);
@ -176,7 +120,7 @@ export default {
login(credentials) {
return apiClient.post('/auth/login', credentials);
},
getMe() { // Для получения данных пользователя
getMe() {
return apiClient.get('/auth/me');
},
updateUserProfile(profileData) {
@ -195,11 +139,10 @@ export default {
uploadUserProfilePhoto(formData) {
return apiClient.post('/users/profile/photo', formData, {
headers: {
'Content-Type': 'multipart/form-data', // Важно для FormData
'Content-Type': 'multipart/form-data',
},
});
},
// Новые методы для управления фотографиями
setMainPhoto(photoId) {
return apiClient.put(`/users/profile/photo/${photoId}/set-main`);
},
@ -207,14 +150,12 @@ export default {
return apiClient.delete(`/users/profile/photo/${photoId}`);
},
getUserConversations() {
return apiClient.get('/conversations'); // GET /api/conversations
return apiClient.get('/conversations');
},
getMessagesForConversation(conversationId) {
return apiClient.get(`/conversations/${conversationId}/messages`); // GET /api/conversations/:id/messages
},
deleteMessage(conversationId, messageId) {
return apiClient.delete(`/conversations/${conversationId}/messages/${messageId}`);
return apiClient.get(`/conversations/${conversationId}/messages`);
},
deleteMessage(conversationId, message
editMessage(conversationId, messageId, newText) {
return apiClient.put(`/conversations/${conversationId}/messages/${messageId}`, { text: newText });
},

View File

@ -77,29 +77,11 @@ export const connectSocket = () => {
// Сбрасываем флаг блокировки
wasAccountBlocked = false;
// Сохраняем информацию о разблокировке для последующего восстановления сессии
const unblockData = {
unblocked: true,
message: data?.message || 'Ваш аккаунт был разблокирован администратором.',
timestamp: data?.timestamp || new Date().toISOString(),
userId: data?.userId || user.value?._id,
unblockType: data?.unblockType || 'server_notification',
canRestoreSession: data?.unblockData?.canRestoreSession || true
};
// Сохраняем данные разблокировки в localStorage
try {
localStorage.setItem('accountUnblockedInfo', JSON.stringify(unblockData));
console.log('[SocketService] Информация о разблокировке сохранена в localStorage');
} catch (storageError) {
console.error('[SocketService] Ошибка при сохранении информации о разблокировке:', storageError);
}
// Вызываем обработчик разблокировки
if (typeof handleAccountUnblocked === 'function') {
try {
console.log('[SocketService] Вызов обработчика разблокировки аккаунта');
handleAccountUnblocked(unblockData);
handleAccountUnblocked(data);
} catch (error) {
console.error('[SocketService] Ошибка при обработке разблокировки аккаунта:', error);
@ -130,11 +112,7 @@ export const connectSocket = () => {
console.log('[SocketService] Это уведомление относится к текущему пользователю, сбрасываем флаг блокировки');
wasAccountBlocked = false;
// Проверяем наличие сохраненных данных о блокировке
const blockedInfoStr = localStorage.getItem('accountBlockedInfo');
if (blockedInfoStr) {
console.log('[SocketService] Найдена информация о блокировке, вызываем обработчик разблокировки');
// Вызываем обработчик разблокировки
if (typeof handleAccountUnblocked === 'function') {
try {
// Создаем объект с данными разблокировки
@ -154,7 +132,6 @@ export const connectSocket = () => {
}
}
}
}
});
// Обработчик глобальной блокировки

View File

@ -135,32 +135,11 @@ const enableScroll = () => {
onMounted(async () => {
preventScroll(); // Блокируем прокрутку при монтировании компонента
// Проверяем, есть ли информация о блокировке в localStorage
const storedBlockInfo = localStorage.getItem('accountBlockedInfo');
if (storedBlockInfo) {
try {
const parsedInfo = JSON.parse(storedBlockInfo);
blockedInfo.value = parsedInfo;
// Удаляем дублирующее сообщение об ошибке, так как эта информация уже
// отображается в блоке blocked-account-message выше кнопки "Войти"
} catch (e) {
console.error('Ошибка при парсинге информации о блокировке:', e);
}
}
// Проверяем наличие информации о разблокировке только для отображения уведомления
const unblockedInfoStr = localStorage.getItem('accountUnblockedInfo');
if (unblockedInfoStr) {
try {
const unblockedInfo = JSON.parse(unblockedInfoStr);
// Отображаем уведомление о разблокировке
errorMessage.value = unblockedInfo.message || 'Ваш аккаунт был разблокирован. Пожалуйста, войдите в систему.';
// Удаляем информацию о разблокировке, чтобы сообщение не появлялось повторно
localStorage.removeItem('accountUnblockedInfo');
} catch (e) {
console.error('[LoginView] Ошибка при парсинге информации о разблокировке:', e);
}
// Проверяем query параметры для отображения сообщений
if (route.query.blocked === 'true') {
errorMessage.value = 'Ваш аккаунт заблокирован администратором.';
} else if (route.query.unblocked === 'true') {
errorMessage.value = 'Ваш аккаунт был разблокирован. Пожалуйста, войдите в систему.';
}
});