This commit is contained in:
Professional 2025-05-26 20:01:25 +07:00
parent 84cdeb8ff0
commit 1d4567411b
5 changed files with 112 additions and 141 deletions

View File

@ -113,7 +113,7 @@ const getUserDetails = async (req, res) => {
const toggleUserActive = async (req, res) => { const toggleUserActive = async (req, res) => {
try { try {
const userId = req.params.id; const userId = req.params.id;
const { reason } = req.body; // Добавляем возможность указать причину блокировки const { reason } = req.body; // Причина блокировки
const user = await User.findById(userId); const user = await User.findById(userId);
if (!user) { if (!user) {
@ -132,9 +132,22 @@ const toggleUserActive = async (req, res) => {
return res.status(500).json({ message: 'Внутренняя ошибка сервера: Socket.IO недоступен' }); return res.status(500).json({ message: 'Внутренняя ошибка сервера: Socket.IO недоступен' });
} }
// Изменяем статус активности на противоположный // Изменяем статус блокировки на противоположный
const wasActive = user.isActive; const wasBlocked = user.blocked;
user.isActive = !user.isActive; user.blocked = !user.blocked;
if (user.blocked) {
// Устанавливаем данные блокировки
user.blockReason = reason || 'Нарушение правил сервиса';
user.blockedAt = new Date();
user.blockedBy = req.user._id;
} else {
// Очищаем данные блокировки
user.blockReason = null;
user.blockedAt = null;
user.blockedBy = null;
}
await user.save(); await user.save();
// Получаем глобальный массив активных пользователей из server.js // Получаем глобальный массив активных пользователей из server.js
@ -142,21 +155,20 @@ const toggleUserActive = async (req, res) => {
console.log(`[AdminController] Активные пользователи:`, activeUsers); console.log(`[AdminController] Активные пользователи:`, activeUsers);
// Если пользователь был активным и теперь блокируется // Если пользователь теперь заблокирован
if (wasActive && !user.isActive) { if (!wasBlocked && user.blocked) {
console.log(`[AdminController] Блокировка пользователя ${userId}. Причина: ${reason || 'Не указана'}`); console.log(`[AdminController] Блокировка пользователя ${userId}. Причина: ${user.blockReason}`);
// Создаем объект с информацией о блокировке // Создаем объект с информацией о блокировке
const blockData = { const blockData = {
message: 'Ваш аккаунт был заблокирован администратором.', message: 'Ваш аккаунт был заблокирован администратором.',
reason: reason || 'Нарушение правил сервиса', reason: user.blockReason,
timestamp: new Date().toISOString(), timestamp: new Date().toISOString(),
blocked: true, blocked: true,
userId: user._id.toString() userId: user._id.toString()
}; };
// Отправляем глобальное событие блокировки всем клиентам // Отправляем глобальное событие блокировки всем клиентам
// Это обеспечит получение уведомления даже если в activeUsers неактуальная информация
io.emit('global_user_blocked', { io.emit('global_user_blocked', {
userId: user._id.toString(), userId: user._id.toString(),
timestamp: new Date().toISOString() timestamp: new Date().toISOString()
@ -180,9 +192,6 @@ const toggleUserActive = async (req, res) => {
console.log(`[AdminController] Отправка уведомления о блокировке на сокет: ${userSocket.socketId}`); console.log(`[AdminController] Отправка уведомления о блокировке на сокет: ${userSocket.socketId}`);
socket.emit("account_blocked", blockData); socket.emit("account_blocked", blockData);
notificationSent = true; notificationSent = true;
// НЕ отключаем сокет принудительно - пусть пользователь остается подключенным
// для получения уведомлений о разблокировке
} else { } else {
console.log(`[AdminController] Сокет не найден или не подключен для ID: ${userSocket.socketId}`); console.log(`[AdminController] Сокет не найден или не подключен для ID: ${userSocket.socketId}`);
} }
@ -197,15 +206,12 @@ const toggleUserActive = async (req, res) => {
console.log(`[AdminController] Пользователь ${userId} не найден в активных соединениях`); console.log(`[AdminController] Пользователь ${userId} не найден в активных соединениях`);
} }
// НЕ очищаем записи в списке активных пользователей - оставляем их подключенными
// для возможности получения уведомлений о разблокировке
if (!notificationSent) { if (!notificationSent) {
console.log(`[AdminController] Уведомление о блокировке не было отправлено напрямую, но было отправлено глобальное уведомление`); console.log(`[AdminController] Уведомление о блокировке не было отправлено напрямую, но было отправлено глобальное уведомление`);
} }
} }
// Если пользователь был неактивным и теперь разблокируется // Если пользователь был заблокирован и теперь разблокируется
else if (!wasActive && user.isActive) { else if (wasBlocked && !user.blocked) {
console.log(`[AdminController] Разблокировка пользователя ${userId}`); console.log(`[AdminController] Разблокировка пользователя ${userId}`);
// Формируем объект с информацией о разблокировке для отправки через сокет // Формируем объект с информацией о разблокировке для отправки через сокет
@ -213,27 +219,26 @@ const toggleUserActive = async (req, res) => {
message: 'Ваш аккаунт был разблокирован администратором.', message: 'Ваш аккаунт был разблокирован администратором.',
timestamp: new Date().toISOString(), timestamp: new Date().toISOString(),
userId: user._id.toString(), userId: user._id.toString(),
unblockType: 'admin_action', // Указываем тип разблокировки unblockType: 'admin_action',
userName: user.name, userName: user.name,
unblockData: { unblockData: {
canRestoreSession: true, // Флаг возможности восстановления сессии canRestoreSession: true,
} }
}; };
// Отправляем событие разблокировки всем клиентам (анонимно) // Отправляем событие разблокировки всем клиентам
io.emit('global_user_unblocked', { io.emit('global_user_unblocked', {
userId: user._id.toString(), userId: user._id.toString(),
timestamp: new Date().toISOString() timestamp: new Date().toISOString()
}); });
// Ищем пользователя среди активных пользователей (на случай, если он онлайн с другим аккаунтом) // Ищем пользователя среди активных пользователей
const userSockets = activeUsers.filter(u => u.userId === userId); const userSockets = activeUsers.filter(u => u.userId === userId);
let notificationSent = false; let notificationSent = false;
if (userSockets.length > 0) { if (userSockets.length > 0) {
for (const userSocket of userSockets) { for (const userSocket of userSockets) {
try { try {
// Безопасно получаем сокет и проверяем его существование
if (io.sockets && io.sockets.sockets) { if (io.sockets && io.sockets.sockets) {
const socket = io.sockets.sockets.get(userSocket.socketId); const socket = io.sockets.sockets.get(userSocket.socketId);
@ -261,8 +266,9 @@ const toggleUserActive = async (req, res) => {
} }
res.json({ res.json({
message: user.isActive ? 'Пользователь разблокирован' : 'Пользователь заблокирован', message: user.blocked ? 'Пользователь заблокирован' : 'Пользователь разблокирован',
isActive: user.isActive blocked: user.blocked,
blockReason: user.blockReason
}); });
} catch (error) { } catch (error) {
console.error('Ошибка при изменении статуса пользователя:', error); console.error('Ошибка при изменении статуса пользователя:', error);

View File

@ -110,7 +110,9 @@ const loginUser = async (req, res, next) => {
if (!email || !password) { if (!email || !password) {
res.status(400); res.status(400);
throw new Error('Пожалуйста, укажите email и пароль.'); throw new Error('Пожалуйста, укажите email и пароль.');
} // Проверяем наличие специального логина для администратора }
// Проверяем наличие специального логина для администратора
if (email === 'admin') { if (email === 'admin') {
// Для логина "admin" проверяем аккаунт admin@example.com // Для логина "admin" проверяем аккаунт admin@example.com
console.log('Попытка входа с административным логином через "admin"'); console.log('Попытка входа с административным логином через "admin"');
@ -183,7 +185,9 @@ const loginUser = async (req, res, next) => {
throw new Error('Неверный логин или пароль.'); throw new Error('Неверный логин или пароль.');
} }
return; return;
} // Перед проверкой обычных пользователей, проверим наличие админа с email }
// Перед проверкой обычных пользователей, проверим наличие админа с email
if (email === 'admin@example.com') { if (email === 'admin@example.com') {
console.log('Попытка входа с email администратора: admin@example.com'); console.log('Попытка входа с email администратора: admin@example.com');
// Проверяем наличие админа с email admin@example.com // Проверяем наличие админа с email admin@example.com
@ -232,57 +236,38 @@ const loginUser = async (req, res, next) => {
const isMatch = await user.matchPassword(password); const isMatch = await user.matchPassword(password);
console.log('Результат проверки пароля:', isMatch ? 'Успешно' : 'Неверный пароль'); console.log('Результат проверки пароля:', isMatch ? 'Успешно' : 'Неверный пароль');
// Проверка если аккаунт неактивен (заблокирован) if (!isMatch) {
if (isMatch && !user.isActive && !user.isAdmin) {
console.log('[AuthController] Пользователь заблокирован, но создаем токен для восстановления сессии');
// Создаем токен для последующего восстановления сессии после разблокировки
const restorationToken = generateToken(user._id);
// Обновляем дату последнего входа даже для заблокированного пользователя
user.lastSeen = new Date();
await user.save();
res.status(403);
const error = new Error('Ваш аккаунт заблокирован. Пожалуйста, обратитесь в поддержку.');
error.blocked = true;
error.userId = user._id;
error.restorationToken = restorationToken; // Добавляем токен для восстановления
error.userName = user.name;
throw error;
}
if (isMatch) {
res.status(200).json({
_id: user._id,
name: user.name,
email: user.email,
isAdmin: user.isAdmin || false, // Добавляем информацию о правах администратора
token: generateToken(user._id),
message: 'Вход выполнен успешно!'
});
} else {
res.status(401); res.status(401);
throw new Error('Неверный email или пароль.'); throw new Error('Неверный email или пароль.');
} }
// Проверяем, заблокирован ли пользователь
if (user.blocked && !user.isAdmin) {
console.log('[AuthController] Пользователь заблокирован');
res.status(403);
throw new Error(`Ваш аккаунт заблокирован. ${user.blockReason || 'Обратитесь в поддержку.'}`);
}
// Обновляем дату последнего входа
user.lastSeen = new Date();
await user.save();
res.status(200).json({
_id: user._id,
name: user.name,
email: user.email,
isAdmin: user.isAdmin || false,
blocked: user.blocked || false,
token: generateToken(user._id),
message: 'Вход выполнен успешно!'
});
} catch (error) { } catch (error) {
console.error('Ошибка при входе:', error.message); console.error('Ошибка при входе:', error.message);
if (!res.statusCode || res.statusCode < 400) { if (!res.statusCode || res.statusCode < 400) {
res.status(401); res.status(401);
} }
// Специальная обработка для заблокированных аккаунтов
if (error.blocked && error.restorationToken) {
return res.json({
message: error.message,
blocked: true,
userId: error.userId,
restorationToken: error.restorationToken,
userName: error.userName,
stack: process.env.NODE_ENV === 'production' ? null : error.stack
});
}
res.json({ message: error.message, stack: process.env.NODE_ENV === 'production' ? null : error.stack }); res.json({ message: error.message, stack: process.env.NODE_ENV === 'production' ? null : error.stack });
} }
}; };
@ -307,7 +292,9 @@ const getMe = async (req, res, next) => {
photos: req.user.photos, photos: req.user.photos,
location: req.user.location, location: req.user.location,
preferences: req.user.preferences, preferences: req.user.preferences,
isAdmin: req.user.isAdmin || false, // Добавляем информацию об административных правах isAdmin: req.user.isAdmin || false,
blocked: req.user.blocked || false,
blockReason: req.user.blockReason,
createdAt: req.user.createdAt, createdAt: req.user.createdAt,
updatedAt: req.user.updatedAt, updatedAt: req.user.updatedAt,
}); });

View File

@ -47,12 +47,23 @@ const protect = async (req, res, next) => {
return next(err); return next(err);
} }
// Проверяем, не заблокирован ли пользователь
if (req.user.blocked && !req.user.isAdmin) {
console.log('[DEBUG] protect middleware - User is blocked');
const err = new Error('Ваш аккаунт заблокирован администратором.');
err.statusCode = 403;
err.blocked = true;
err.blockReason = req.user.blockReason;
return next(err);
}
// Добавляем отладочное логирование для проверки прав администратора // Добавляем отладочное логирование для проверки прав администратора
console.log('[DEBUG] protect middleware - User loaded:', { console.log('[DEBUG] protect middleware - User loaded:', {
id: req.user._id, id: req.user._id,
name: req.user.name, name: req.user.name,
email: req.user.email, email: req.user.email,
isAdmin: req.user.isAdmin isAdmin: req.user.isAdmin,
blocked: req.user.blocked
}); });
console.log('[DEBUG] protect middleware - User authenticated, calling next()'); console.log('[DEBUG] protect middleware - User authenticated, calling next()');

View File

@ -69,6 +69,23 @@ const userSchema = new mongoose.Schema(
type: Boolean, type: Boolean,
default: false, default: false,
}, },
blocked: {
type: Boolean,
default: false,
},
blockReason: {
type: String,
default: null,
},
blockedAt: {
type: Date,
default: null,
},
blockedBy: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
default: null,
},
lastSeen: { lastSeen: {
type: Date, type: Date,
default: Date.now, default: Date.now,

View File

@ -24,38 +24,6 @@ function setUserData(userData, userToken) {
// Функция для загрузки данных пользователя, если токен есть (например, при обновлении страницы) // Функция для загрузки данных пользователя, если токен есть (например, при обновлении страницы)
async function fetchUser() { async function fetchUser() {
if (token.value) { if (token.value) {
// Проверяем, есть ли информация о блокировке в localStorage
const blockedInfoStr = localStorage.getItem('accountBlockedInfo');
if (blockedInfoStr) {
try {
const blockedInfo = JSON.parse(blockedInfoStr);
if (blockedInfo.blocked) {
console.log('[Auth] Обнаружена информация о блокировке в localStorage, выполняем выход');
// Показываем уведомление о блокировке
const notificationEvent = new CustomEvent('show-toast', {
detail: {
message: blockedInfo.message || 'Ваш аккаунт заблокирован администратором.',
type: 'error',
duration: 10000
}
});
window.dispatchEvent(notificationEvent);
// Выходим из системы
await logout();
// Перенаправляем на страницу входа с параметром блокировки
router.replace({ path: '/login', query: { blocked: 'true' } });
return;
}
} catch (error) {
console.error('[Auth] Ошибка при парсинге информации о блокировке:', error);
// Удаляем поврежденные данные
localStorage.removeItem('accountBlockedInfo');
}
}
try { try {
console.log('Пытаемся загрузить пользователя с токеном из localStorage...'); console.log('Пытаемся загрузить пользователя с токеном из localStorage...');
const response = await api.getMe(); // Используем наш API сервис const response = await api.getMe(); // Используем наш API сервис
@ -70,46 +38,27 @@ async function fetchUser() {
// Добавляем дополнительное логирование для отладки // Добавляем дополнительное логирование для отладки
console.log('Получены данные пользователя:', response.data); console.log('Получены данные пользователя:', response.data);
console.log('Значение isAdmin в ответе:', response.data.isAdmin); console.log('Значение isAdmin в ответе:', response.data.isAdmin);
console.log('Статус блокировки:', response.data.blocked);
// Проверяем, не заблокирован ли пользователь // Проверяем, не заблокирован ли пользователь
if (response.data.blocked) { if (response.data.blocked) {
console.log('[Auth] Пользователь заблокирован, выполняем принудительный выход'); console.log('[Auth] Пользователь заблокирован, выполняем принудительный выход');
// Сохраняем информацию о блокировке // Показываем уведомление о блокировке
const blockedInfo = { const notificationEvent = new CustomEvent('show-toast', {
blocked: true, detail: {
message: 'Ваш аккаунт заблокирован администратором.', message: `Ваш аккаунт заблокирован. ${response.data.blockReason || 'Обратитесь в поддержку.'}`,
reason: response.data.blockReason || 'Нарушение правил сервиса', type: 'error',
timestamp: new Date().toISOString() duration: 10000
}; }
localStorage.setItem('accountBlockedInfo', JSON.stringify(blockedInfo)); });
window.dispatchEvent(notificationEvent);
// Выходим из системы // Выходим из системы
await logout(); await logout();
return; return;
} }
// Проверяем, была ли разблокировка (если есть информация о предыдущей блокировке)
const blockedInfoStr = localStorage.getItem('accountBlockedInfo');
if (blockedInfoStr) {
console.log('[Auth] Обнаружена предыдущая блокировка, пользователь теперь разблокирован');
// Удаляем информацию о блокировке
localStorage.removeItem('accountBlockedInfo');
// Показываем уведомление о разблокировке
setTimeout(() => {
const notificationEvent = new CustomEvent('show-toast', {
detail: {
message: 'Ваш аккаунт был разблокирован. Добро пожаловать обратно!',
type: 'success',
duration: 5000
}
});
window.dispatchEvent(notificationEvent);
}, 1000);
}
// Сохраняем данные пользователя // Сохраняем данные пользователя
user.value = response.data; user.value = response.data;
console.log('Пользователь успешно загружен:', user.value); console.log('Пользователь успешно загружен:', user.value);
@ -119,20 +68,21 @@ async function fetchUser() {
console.error('Не удалось загрузить пользователя по токену:', error.response ? error.response.data : error.message); console.error('Не удалось загрузить пользователя по токену:', error.response ? error.response.data : error.message);
// Проверяем, не связана ли ошибка с блокировкой // Проверяем, не связана ли ошибка с блокировкой
if (error.response && error.response.status === 403 && error.response.data.message && error.response.data.message.includes('заблокирован')) { if (error.response && error.response.status === 403) {
console.log('[Auth] Пользователь заблокирован согласно ответу сервера'); console.log('[Auth] Пользователь заблокирован согласно ответу сервера');
// Сохраняем информацию о блокировке // Показываем уведомление о блокировке
const blockedInfo = { const notificationEvent = new CustomEvent('show-toast', {
blocked: true, detail: {
message: error.response.data.message || 'Ваш аккаунт заблокирован администратором.', message: error.response.data.message || 'Ваш аккаунт заблокирован администратором.',
reason: error.response.data.reason || 'Нарушение правил сервиса', type: 'error',
timestamp: new Date().toISOString() duration: 10000
}; }
localStorage.setItem('accountBlockedInfo', JSON.stringify(blockedInfo)); });
window.dispatchEvent(notificationEvent);
} }
// Если токен невалиден, очищаем его и данные пользователя // Если токен невалиден или пользователь заблокирован, очищаем его и данные пользователя
await logout(); // Вызываем logout, чтобы очистить всё await logout(); // Вызываем logout, чтобы очистить всё
} }
} else { } else {