2025-11-03 20:35:01 +00:00
|
|
|
|
const { Server } = require('socket.io');
|
|
|
|
|
|
const config = require('./config');
|
|
|
|
|
|
const Notification = require('./models/Notification');
|
2025-11-10 20:13:22 +00:00
|
|
|
|
const { isModerationAdmin, normalizeUsername } = require('./services/moderationAdmin');
|
|
|
|
|
|
const { log } = require('./middleware/logger');
|
2025-11-03 20:35:01 +00:00
|
|
|
|
|
|
|
|
|
|
let io = null;
|
2025-11-10 20:13:22 +00:00
|
|
|
|
let moderationNamespace = null;
|
|
|
|
|
|
const connectedModerators = new Map();
|
2025-11-03 20:35:01 +00:00
|
|
|
|
|
|
|
|
|
|
// Инициализация WebSocket сервера
|
|
|
|
|
|
function initWebSocket(server) {
|
|
|
|
|
|
const corsOrigin = config.corsOrigin === '*' ? '*' : config.corsOrigin.split(',');
|
|
|
|
|
|
|
|
|
|
|
|
io = new Server(server, {
|
|
|
|
|
|
cors: {
|
|
|
|
|
|
origin: corsOrigin,
|
|
|
|
|
|
methods: ['GET', 'POST'],
|
|
|
|
|
|
credentials: true
|
|
|
|
|
|
},
|
|
|
|
|
|
transports: ['websocket', 'polling'], // Поддержка обоих транспортов
|
|
|
|
|
|
pingTimeout: 60000,
|
|
|
|
|
|
pingInterval: 25000
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
io.on('connection', (socket) => {
|
|
|
|
|
|
console.log(`✅ WebSocket подключен: ${socket.id}`);
|
|
|
|
|
|
|
|
|
|
|
|
// Присоединиться к комнате пользователя
|
|
|
|
|
|
socket.on('join', (userId) => {
|
|
|
|
|
|
socket.join(`user_${userId}`);
|
|
|
|
|
|
console.log(`Пользователь ${userId} присоединился к комнате`);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// Отключение
|
|
|
|
|
|
socket.on('disconnect', () => {
|
|
|
|
|
|
console.log(`❌ WebSocket отключен: ${socket.id}`);
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2025-11-10 20:13:22 +00:00
|
|
|
|
registerModerationChat();
|
|
|
|
|
|
|
2025-11-03 20:35:01 +00:00
|
|
|
|
console.log('✅ WebSocket сервер инициализирован');
|
|
|
|
|
|
return io;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-10 20:13:22 +00:00
|
|
|
|
function registerModerationChat() {
|
|
|
|
|
|
if (!io || moderationNamespace) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
moderationNamespace = io.of('/mod-chat');
|
|
|
|
|
|
|
|
|
|
|
|
const broadcastOnline = () => {
|
|
|
|
|
|
const unique = Array.from(
|
|
|
|
|
|
new Map(
|
|
|
|
|
|
Array.from(connectedModerators.values()).map((value) => [value.username, value])
|
|
|
|
|
|
).values()
|
|
|
|
|
|
);
|
|
|
|
|
|
moderationNamespace.emit('online', unique);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
moderationNamespace.on('connection', (socket) => {
|
|
|
|
|
|
socket.data.authorized = false;
|
|
|
|
|
|
|
|
|
|
|
|
socket.on('auth', async (payload = {}) => {
|
|
|
|
|
|
const username = normalizeUsername(payload.username);
|
|
|
|
|
|
const telegramId = payload.telegramId;
|
|
|
|
|
|
|
|
|
|
|
|
if (!username || !telegramId) {
|
2025-11-11 00:33:22 +00:00
|
|
|
|
log('warn', 'Mod chat auth failed: no username/telegramId', { username, telegramId });
|
2025-11-10 20:13:22 +00:00
|
|
|
|
socket.emit('unauthorized');
|
|
|
|
|
|
return socket.disconnect(true);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-11 00:33:22 +00:00
|
|
|
|
// Проверить, является ли владельцем
|
|
|
|
|
|
const ownerUsernames = config.moderationOwnerUsernames || [];
|
|
|
|
|
|
const isOwner = ownerUsernames.includes(username);
|
|
|
|
|
|
|
|
|
|
|
|
// Проверить, является ли админом
|
|
|
|
|
|
const isAdmin = await isModerationAdmin({ username, telegramId });
|
|
|
|
|
|
|
|
|
|
|
|
if (!isOwner && !isAdmin) {
|
|
|
|
|
|
log('warn', 'Mod chat access denied', { username, telegramId });
|
2025-11-10 20:13:22 +00:00
|
|
|
|
socket.emit('unauthorized');
|
|
|
|
|
|
return socket.disconnect(true);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
socket.data.authorized = true;
|
|
|
|
|
|
socket.data.username = username;
|
|
|
|
|
|
socket.data.telegramId = telegramId;
|
2025-11-11 00:33:22 +00:00
|
|
|
|
socket.data.isOwner = isOwner;
|
|
|
|
|
|
|
2025-11-10 20:13:22 +00:00
|
|
|
|
connectedModerators.set(socket.id, {
|
|
|
|
|
|
username,
|
2025-11-11 00:33:22 +00:00
|
|
|
|
telegramId,
|
|
|
|
|
|
isOwner
|
2025-11-10 20:13:22 +00:00
|
|
|
|
});
|
|
|
|
|
|
|
2025-11-11 00:33:22 +00:00
|
|
|
|
log('info', 'Mod chat auth success', { username, isOwner, isAdmin });
|
2025-11-10 20:13:22 +00:00
|
|
|
|
socket.emit('ready');
|
|
|
|
|
|
broadcastOnline();
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
socket.on('message', (payload = {}) => {
|
|
|
|
|
|
if (!socket.data.authorized) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const text = (payload.text || '').trim();
|
|
|
|
|
|
if (!text) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const message = {
|
|
|
|
|
|
id: `${Date.now()}-${Math.round(Math.random() * 1e6)}`,
|
|
|
|
|
|
username: socket.data.username,
|
|
|
|
|
|
telegramId: socket.data.telegramId,
|
|
|
|
|
|
text,
|
|
|
|
|
|
createdAt: new Date().toISOString()
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
moderationNamespace.emit('message', message);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
socket.on('disconnect', () => {
|
|
|
|
|
|
connectedModerators.delete(socket.id);
|
|
|
|
|
|
broadcastOnline();
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-03 20:35:01 +00:00
|
|
|
|
// Отправить уведомление пользователю в real-time
|
|
|
|
|
|
function sendNotification(userId, notification) {
|
|
|
|
|
|
if (io) {
|
|
|
|
|
|
io.to(`user_${userId}`).emit('notification', notification);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Отправить обновление поста
|
|
|
|
|
|
function sendPostUpdate(postId, data) {
|
|
|
|
|
|
if (io) {
|
|
|
|
|
|
io.emit('post_update', { postId, ...data });
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Отправить новый комментарий
|
|
|
|
|
|
function sendNewComment(postId, comment) {
|
|
|
|
|
|
if (io) {
|
|
|
|
|
|
io.emit('new_comment', { postId, comment });
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Отправить информацию о том, кто онлайн
|
|
|
|
|
|
function broadcastOnlineUsers(users) {
|
|
|
|
|
|
if (io) {
|
|
|
|
|
|
io.emit('online_users', users);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
module.exports = {
|
|
|
|
|
|
initWebSocket,
|
|
|
|
|
|
sendNotification,
|
|
|
|
|
|
sendPostUpdate,
|
|
|
|
|
|
sendNewComment,
|
|
|
|
|
|
broadcastOnlineUsers
|
|
|
|
|
|
};
|
|
|
|
|
|
|