фикс
This commit is contained in:
parent
f7d0d71858
commit
920168e7d1
@ -135,64 +135,63 @@ const toggleUserActive = async (req, res) => {
|
||||
|
||||
// Если пользователь был активным и теперь блокируется
|
||||
if (wasActive && !user.isActive) {
|
||||
// Находим активное соединение пользователя
|
||||
const activeUsers = Array.from(io.sockets.sockets).map(socket => socket[1]);
|
||||
const userSockets = activeUsers.filter(socket => {
|
||||
const userData = socket.handshake.query;
|
||||
return userData && userData.userId === userId;
|
||||
console.log(`[AdminController] Блокировка пользователя ${userId}. Причина: ${reason || 'Не указана'}`);
|
||||
|
||||
// Находим все активные сокеты пользователя - новый подход с более надежным поиском
|
||||
const activeSockets = Array.from(io.sockets.sockets.values()).filter(socket => {
|
||||
return socket &&
|
||||
socket.handshake &&
|
||||
socket.handshake.query &&
|
||||
socket.handshake.query.userId === userId;
|
||||
});
|
||||
|
||||
// Получаем информацию о пользователе из массива activeUsers
|
||||
// (этот массив хранится в server.js)
|
||||
const activeUsersArray = Array.from(io.sockets.adapter.rooms).filter(room => {
|
||||
return room[1].has(room[0]); // Фильтруем комнаты, где id комнаты совпадает с id сокета
|
||||
}).map(room => {
|
||||
const socket = io.sockets.sockets.get(room[0]);
|
||||
return socket && socket.handshake.query.userId
|
||||
? { userId: socket.handshake.query.userId, socketId: room[0] }
|
||||
: null;
|
||||
}).filter(Boolean);
|
||||
console.log(`[AdminController] Найдено активных сокетов пользователя: ${activeSockets.length}`);
|
||||
|
||||
// Находим сокет пользователя
|
||||
const userSocketInfo = activeUsersArray.find(u => u.userId === userId);
|
||||
|
||||
// Если пользователь онлайн, отправляем уведомление о блокировке
|
||||
if (userSocketInfo && userSocketInfo.socketId) {
|
||||
io.to(userSocketInfo.socketId).emit("account_blocked", {
|
||||
if (activeSockets.length > 0) {
|
||||
// Отправляем уведомление по всем активным сокетам пользователя
|
||||
activeSockets.forEach(socket => {
|
||||
console.log(`[AdminController] Отправка уведомления о блокировке на сокет: ${socket.id}`);
|
||||
socket.emit("account_blocked", {
|
||||
message: 'Ваш аккаунт был заблокирован администратором.',
|
||||
reason: reason || 'Нарушение правил сервиса',
|
||||
timestamp: new Date().toISOString()
|
||||
timestamp: new Date().toISOString(),
|
||||
blocked: true
|
||||
});
|
||||
console.log(`[AdminController] Отправлено уведомление о блокировке пользователю ${userId}`);
|
||||
});
|
||||
|
||||
// Принудительно отключаем все сокеты пользователя
|
||||
setTimeout(() => {
|
||||
activeSockets.forEach(socket => {
|
||||
console.log(`[AdminController] Принудительное отключение сокета: ${socket.id}`);
|
||||
socket.disconnect(true);
|
||||
});
|
||||
}, 1000); // Небольшая задержка, чтобы сообщение о блокировке успело доставиться
|
||||
} else {
|
||||
// Если пользователь не в сети, просто оставляем лог
|
||||
console.log(`[AdminController] Пользователь ${userId} заблокирован, но не находится в сети для отправки уведомления`);
|
||||
console.log(`[AdminController] Пользователь ${userId} не найден в активных соединениях`);
|
||||
}
|
||||
}
|
||||
// Если пользователь был неактивным и теперь разблокируется
|
||||
else if (!wasActive && user.isActive) {
|
||||
// Используем тот же подход для поиска сокетов пользователя
|
||||
const activeUsersArray = Array.from(io.sockets.adapter.rooms).filter(room => {
|
||||
return room[1].has(room[0]);
|
||||
}).map(room => {
|
||||
const socket = io.sockets.sockets.get(room[0]);
|
||||
return socket && socket.handshake.query.userId
|
||||
? { userId: socket.handshake.query.userId, socketId: room[0] }
|
||||
: null;
|
||||
}).filter(Boolean);
|
||||
console.log(`[AdminController] Разблокировка пользователя ${userId}`);
|
||||
|
||||
// Находим сокет пользователя (если он вдруг залогинился с другим статусом)
|
||||
const userSocketInfo = activeUsersArray.find(u => u.userId === userId);
|
||||
// То же самое для поиска сокетов пользователя
|
||||
const activeSockets = Array.from(io.sockets.sockets.values()).filter(socket => {
|
||||
return socket &&
|
||||
socket.handshake &&
|
||||
socket.handshake.query &&
|
||||
socket.handshake.query.userId === userId;
|
||||
});
|
||||
|
||||
// Если пользователь онлайн, отправляем уведомление о разблокировке
|
||||
if (userSocketInfo && userSocketInfo.socketId) {
|
||||
io.to(userSocketInfo.socketId).emit("account_unblocked", {
|
||||
if (activeSockets.length > 0) {
|
||||
activeSockets.forEach(socket => {
|
||||
console.log(`[AdminController] Отправка уведомления о разблокировке на сокет: ${socket.id}`);
|
||||
socket.emit("account_unblocked", {
|
||||
message: 'Ваш аккаунт был разблокирован администратором.',
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
console.log(`[AdminController] Отправлено уведомление о разблокировке пользователю ${userId}`);
|
||||
});
|
||||
} else {
|
||||
console.log(`[AdminController] Пользователь ${userId} разблокирован, но не находится в сети для отправки уведомления`);
|
||||
console.log(`[AdminController] Пользователь ${userId} не найден в активных соединениях для отправки уведомления о разблокировке`);
|
||||
}
|
||||
}
|
||||
|
||||
|
49
src/auth.js
49
src/auth.js
@ -129,8 +129,9 @@ async function logout() {
|
||||
|
||||
// Функция обработки события блокировки аккаунта
|
||||
function handleAccountBlocked(data) {
|
||||
console.log('Обработка блокировки аккаунта:', data);
|
||||
console.log('[Auth] Экстренная обработка блокировки аккаунта:', data);
|
||||
|
||||
try {
|
||||
// Создаем объект с информацией о блокировке
|
||||
const blockInfo = {
|
||||
blocked: true,
|
||||
@ -142,24 +143,50 @@ function handleAccountBlocked(data) {
|
||||
// Сохраняем информацию о блокировке в localStorage для отображения на странице логина
|
||||
localStorage.setItem('accountBlockedInfo', JSON.stringify(blockInfo));
|
||||
|
||||
// Немедленно выполняем выход пользователя - сначала очищаем данные
|
||||
// Немедленно очищаем все данные пользователя
|
||||
console.log('[Auth] Очистка данных пользователя из-за блокировки');
|
||||
user.value = null;
|
||||
token.value = null;
|
||||
|
||||
// Очистка localStorage - удаляем все ключи, которые могут содержать пользовательские данные
|
||||
localStorage.removeItem('userToken');
|
||||
localStorage.removeItem('userSettings');
|
||||
localStorage.removeItem('userPreferences');
|
||||
|
||||
// Удаляем токен из заголовков для будущих запросов API
|
||||
// Удаляем токены доступа для API запросов
|
||||
if (typeof api.setAuthToken === 'function') {
|
||||
api.setAuthToken(null);
|
||||
console.log('[Auth] Токен авторизации API удален');
|
||||
}
|
||||
|
||||
// Принудительно прерываем все активные запросы API, если они есть
|
||||
api.cancelActiveRequests && api.cancelActiveRequests();
|
||||
// Пробуем отменить активные запросы
|
||||
if (typeof api.cancelActiveRequests === 'function') {
|
||||
api.cancelActiveRequests();
|
||||
console.log('[Auth] Активные запросы API отменены');
|
||||
}
|
||||
|
||||
// Перенаправляем на страницу входа с уведомлением о блокировке
|
||||
router.push({
|
||||
name: 'Login',
|
||||
query: { blocked: 'true' }
|
||||
});
|
||||
// Устанавливаем флаг в SessionStorage для индикации блокировки при перезагрузке страницы
|
||||
sessionStorage.setItem('forcedLogout', 'blocked');
|
||||
|
||||
console.log('Пользователь был разлогинен из-за блокировки аккаунта.');
|
||||
// Принудительный перенаправление на страницу логина с флагом блокировки
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// Функция обработки события разблокировки аккаунта
|
||||
|
@ -1,5 +1,6 @@
|
||||
import axios from 'axios';
|
||||
import { useAuth } from '../auth'; // Импортируем функцию авторизации
|
||||
import { isAccountBlocked } from './socketService'; // Импортируем проверку блокировки из socketService
|
||||
|
||||
// Создаем экземпляр Axios с базовым URL нашего API
|
||||
// Настраиваем базовый URL в зависимости от окружения
|
||||
@ -7,6 +8,31 @@ import { useAuth } from '../auth'; // Импортируем функцию ав
|
||||
// Храним токены отмены запросов для возможности отмены при блокировке
|
||||
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.
|
||||
@ -26,6 +52,24 @@ const apiClient = axios.create({
|
||||
// (Опционально, но очень полезно) Перехватчик для добавления JWT токена к запросам
|
||||
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
|
||||
if (token) {
|
||||
console.log('[API] Добавление токена к запросу:', config.url);
|
||||
@ -42,11 +86,6 @@ apiClient.interceptors.request.use(
|
||||
const requestKey = `${config.method}-${config.url}`;
|
||||
cancelTokenSources.set(requestKey, source);
|
||||
|
||||
// Добавляем обработчик, который удаляет источник токена отмены после завершения запроса
|
||||
config.onFinally = () => {
|
||||
cancelTokenSources.delete(requestKey);
|
||||
};
|
||||
|
||||
return config;
|
||||
},
|
||||
(error) => {
|
||||
@ -70,6 +109,27 @@ apiClient.interceptors.response.use(
|
||||
cancelTokenSources.delete(requestKey);
|
||||
}
|
||||
|
||||
// Проверяем на блокировку аккаунта
|
||||
if (error.response && error.response.status === 403) {
|
||||
const errorData = error.response.data;
|
||||
// Проверка на признаки блокировки аккаунта в ответе
|
||||
if (errorData && (
|
||||
errorData.blocked === true ||
|
||||
errorData.message?.includes('заблокирован') ||
|
||||
errorData.message?.includes('blocked')
|
||||
)) {
|
||||
console.error('[API] Получено уведомление о блокировке аккаунта:', errorData);
|
||||
|
||||
// Вызываем обработчик блокировки аккаунта
|
||||
setTimeout(() => {
|
||||
const { handleAccountBlocked } = useAuth();
|
||||
if (typeof handleAccountBlocked === 'function') {
|
||||
handleAccountBlocked(errorData);
|
||||
}
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (error.response && error.response.status === 401) {
|
||||
console.error('[API] Неавторизованный запрос (401):', error.config.url);
|
||||
|
||||
@ -108,6 +168,7 @@ export default {
|
||||
// Добавляем новые утилитарные методы
|
||||
cancelActiveRequests,
|
||||
setAuthToken,
|
||||
checkAccountBlockStatus,
|
||||
|
||||
register(userData) {
|
||||
return apiClient.post('/auth/register', userData);
|
||||
|
@ -1,33 +1,28 @@
|
||||
import { io } from 'socket.io-client';
|
||||
import { useAuth } from '@/auth'; // Чтобы получить ID текущего пользователя
|
||||
import { useAuth } from '@/auth';
|
||||
|
||||
let socket;
|
||||
|
||||
// const SOCKET_URL = 'http://localhost:5000'; // Закомментируем, так как Vite будет проксировать
|
||||
// Для блокировки аккаунта, добавляем состояние
|
||||
let wasAccountBlocked = false;
|
||||
|
||||
// const SOCKET_URL = 'http://localhost:5000';
|
||||
// Для разработки через Vite proxy, URL должен быть относительным к домену фронтенда
|
||||
const SOCKET_URL = '/'; // Vite будет перехватывать /socket.io/ путь
|
||||
const SOCKET_URL = '/';
|
||||
|
||||
export const connectSocket = () => {
|
||||
const { user, isAuthenticated, handleAccountBlocked, handleAccountUnblocked } = useAuth();
|
||||
|
||||
if (wasAccountBlocked) {
|
||||
console.log('[SocketService] Попытка подключения отклонена - аккаунт был заблокирован');
|
||||
return null;
|
||||
}
|
||||
|
||||
if (isAuthenticated.value && user.value && user.value._id) {
|
||||
// Подключаемся только если пользователь аутентифицирован и есть ID
|
||||
// Передаем userId в query для идентификации на сервере при подключении (альтернатива событию addUser)
|
||||
// Однако, мы используем событие addUser, так что query здесь может быть избыточен, если сервер его не использует при connect.
|
||||
// Оставим его, если вдруг понадобится.
|
||||
// socket = io(SOCKET_URL, {
|
||||
// query: { userId: user.value._id }
|
||||
// });
|
||||
|
||||
// При использовании Vite proxy, путь для Socket.IO должен быть относительным,
|
||||
// а Vite добавит префикс /socket.io/ к target в proxy.
|
||||
// Однако, клиентская библиотека socket.io обычно сама добавляет /socket.io/
|
||||
// поэтому мы можем просто указать базовый URL (который будет проксироваться)
|
||||
// или указать полный путь, который будет проксирован.
|
||||
// Для ясности и чтобы соответствовать настройке proxy, можно использовать path.
|
||||
socket = io(SOCKET_URL, {
|
||||
path: '/socket.io/', // Явно указываем путь, который будет проксироваться
|
||||
transports: ['websocket', 'polling'] // Явно указываем транспорты для лучшей совместимости
|
||||
path: '/socket.io/',
|
||||
transports: ['websocket', 'polling']
|
||||
});
|
||||
|
||||
console.log('[SocketService] Попытка подключения к Socket.IO серверу через Vite proxy...');
|
||||
@ -40,32 +35,60 @@ export const connectSocket = () => {
|
||||
|
||||
socket.on('disconnect', (reason) => {
|
||||
console.log('[SocketService] Отключен от Socket.IO. Причина:', reason);
|
||||
|
||||
// Если отключение произошло из-за блокировки аккаунта,
|
||||
// не пытаемся автоматически переподключиться
|
||||
if (wasAccountBlocked && (reason === 'io server disconnect' || reason === 'transport close')) {
|
||||
console.log('[SocketService] Предотвращение переподключения из-за блокировки аккаунта');
|
||||
socket.disconnect();
|
||||
socket = null;
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('connect_error', (error) => {
|
||||
console.error('[SocketService] Ошибка подключения к Socket.IO:', error.message, error.data);
|
||||
});
|
||||
|
||||
// Добавляем обработчик для события блокировки аккаунта
|
||||
// ПЕРЕРАБОТАННЫЙ обработчик блокировки аккаунта
|
||||
socket.on('account_blocked', (data) => {
|
||||
console.log('[SocketService] Получено уведомление о блокировке аккаунта:', data);
|
||||
|
||||
// Немедленно отключаем сокет
|
||||
// Устанавливаем флаг блокировки аккаунта для предотвращения повторных подключений
|
||||
wasAccountBlocked = true;
|
||||
|
||||
// Принудительно прерываем соединение
|
||||
try {
|
||||
console.log('[SocketService] Принудительное отключение сокета из-за блокировки аккаунта');
|
||||
socket.disconnect();
|
||||
console.log('[SocketService] Сокет принудительно отключен из-за блокировки аккаунта');
|
||||
} catch (error) {
|
||||
console.error('[SocketService] Ошибка при отключении сокета:', error);
|
||||
}
|
||||
|
||||
// Сразу вызываем функцию обработки блокировки из auth.js, без таймаута
|
||||
// Вызываем обработчик блокировки для немедленного выхода
|
||||
if (typeof handleAccountBlocked === 'function') {
|
||||
// Оборачиваем в try-catch для надежности
|
||||
try {
|
||||
handleAccountBlocked(data);
|
||||
} catch (error) {
|
||||
console.error('[SocketService] Ошибка при вызове handleAccountBlocked:', error);
|
||||
// Аварийный выход в случае ошибки в обработчике
|
||||
window.location.href = '/login?blocked=true';
|
||||
}
|
||||
} else {
|
||||
console.error('[SocketService] handleAccountBlocked не определен!');
|
||||
// Принудительная перезагрузка страницы
|
||||
window.location.reload();
|
||||
}
|
||||
|
||||
// Разрываем все существующие соединения и очищаем состояние сокета
|
||||
// Дополнительно очищаем все соединения и состояние
|
||||
socket = null;
|
||||
});
|
||||
|
||||
// Добавляем обработчик для события разблокировки аккаунта
|
||||
// Обработчик разблокировки аккаунта
|
||||
socket.on('account_unblocked', (data) => {
|
||||
console.log('[SocketService] Получено уведомление о разблокировке аккаунта:', data);
|
||||
wasAccountBlocked = false;
|
||||
|
||||
// Сразу вызываем обработчик разблокировки, без таймаута
|
||||
if (typeof handleAccountUnblocked === 'function') {
|
||||
handleAccountUnblocked(data);
|
||||
} else {
|
||||
@ -73,12 +96,6 @@ export const connectSocket = () => {
|
||||
}
|
||||
});
|
||||
|
||||
// Можно здесь же слушать глобальные события, если нужно
|
||||
// socket.on('getUsers', (users) => {
|
||||
// console.log('[SocketService] Получен список активных пользователей:', users);
|
||||
// // Тут можно обновить какой-нибудь стор активных пользователей
|
||||
// });
|
||||
|
||||
return socket;
|
||||
} else {
|
||||
console.warn('[SocketService] Не удалось подключиться: пользователь не аутентифицирован или нет ID.');
|
||||
@ -95,34 +112,19 @@ export const disconnectSocket = () => {
|
||||
};
|
||||
|
||||
export const getSocket = () => {
|
||||
// Если аккаунт был заблокирован, возвращаем null
|
||||
if (wasAccountBlocked) {
|
||||
console.warn('[SocketService] getSocket: Аккаунт заблокирован, сокет недоступен');
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!socket) {
|
||||
console.warn('[SocketService] getSocket вызван, но сокет не инициализирован. Попытка подключения...');
|
||||
// Можно попробовать подключиться здесь, если это желаемое поведение,
|
||||
// но лучше управлять подключением более явно.
|
||||
// return connectSocket(); // Будь осторожен с рекурсией или множественными подключениями
|
||||
}
|
||||
return socket;
|
||||
};
|
||||
|
||||
// Функции для отправки и прослушивания специфичных событий можно добавить здесь
|
||||
// или использовать getSocket() в компонентах и вызывать socket.emit / socket.on там.
|
||||
|
||||
// Пример:
|
||||
// export const sendMessageOnSocket = (messageData) => {
|
||||
// const currentSocket = getSocket();
|
||||
// if (currentSocket) {
|
||||
// currentSocket.emit('sendMessage', messageData);
|
||||
// } else {
|
||||
// console.error('[SocketService] Не могу отправить сообщение: сокет не подключен.');
|
||||
// }
|
||||
// };
|
||||
|
||||
// export const listenForMessage = (callback) => {
|
||||
// const currentSocket = getSocket();
|
||||
// if (currentSocket) {
|
||||
// currentSocket.on('getMessage', callback);
|
||||
// // Возвращаем функцию для отписки
|
||||
// return () => currentSocket.off('getMessage', callback);
|
||||
// }
|
||||
// return () => {}; // Пустая функция отписки, если сокета нет
|
||||
// };
|
||||
// Новая функция для проверки статуса блокировки
|
||||
export const isAccountBlocked = () => {
|
||||
return wasAccountBlocked;
|
||||
};
|
Loading…
x
Reference in New Issue
Block a user