nakama/backend/websocket.js

169 lines
4.6 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const { Server } = require('socket.io');
const config = require('./config');
const Notification = require('./models/Notification');
const { isModerationAdmin, normalizeUsername } = require('./services/moderationAdmin');
const { log } = require('./middleware/logger');
let io = null;
let moderationNamespace = null;
const connectedModerators = new Map();
// Инициализация 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}`);
});
});
registerModerationChat();
console.log('✅ WebSocket сервер инициализирован');
return io;
}
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) {
log('warn', 'Mod chat auth failed: no username/telegramId', { username, telegramId });
socket.emit('unauthorized');
return socket.disconnect(true);
}
// Проверить, является ли владельцем
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 });
socket.emit('unauthorized');
return socket.disconnect(true);
}
socket.data.authorized = true;
socket.data.username = username;
socket.data.telegramId = telegramId;
socket.data.isOwner = isOwner;
connectedModerators.set(socket.id, {
username,
telegramId,
isOwner
});
log('info', 'Mod chat auth success', { username, isOwner, isAdmin });
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();
});
});
}
// Отправить уведомление пользователю в 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
};