/
Krams
/
nodemon2
Обзор
Документация
Войти
/
Krams
/
nodemon2
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
backend/socket/mapChat.js
155 строк
5 KB
Иван Новиков
init
11 июн 2026, 13:08
11 июн 2026, 13:08
900f716
Код
Авторство
О чём код?
import { addMapChatMessage, deleteMapChatMessage, getAllMapChatMessages, getMapChatMessageById, updateMapChatMessage, } from '../services/MapChatService.js'; const MAX_TEXT_LENGTH = 800; /** @type {Record<string, {userId:string|number|null, name:string}>} */ let onlineBySocketId = {}; function safeName(value) { const s = (value ?? '').toString().trim(); return s.length ? s.slice(0, 80) : 'Гость'; } function safeText(value) { const s = (value ?? '').toString().trim(); if (!s) return ''; return s.length > MAX_TEXT_LENGTH ? s.slice(0, MAX_TEXT_LENGTH) : s; } function safeReplyTo(value) { if (!value || typeof value !== 'object') return null; const id = (value.id ?? '').toString().trim().slice(0, 64); const name = safeName(value.name); const text = safeText(value.text).slice(0, 220); const tsRaw = (value.ts ?? '').toString().trim(); const ts = tsRaw ? new Date(tsRaw).toISOString() : ''; if (!id || !text || !ts) return null; return { id, name, text, ts }; } function safeSystemRef(value) { if (!value || typeof value !== 'object') return null; const key = (value.key ?? '').toString().trim().slice(0, 120); const title = (value.title ?? '').toString().trim().slice(0, 220) || 'Системное сообщение'; const text = safeText(value.text).slice(0, 350); const occurredAtRaw = Number(value.occurredAt); const occurredAt = Number.isFinite(occurredAtRaw) && occurredAtRaw > 0 ? Math.floor(occurredAtRaw) : null; if (!key) return null; return { key, title, text, occurredAt }; } function safeId(value) { const id = (value ?? '').toString().trim().slice(0, 64); return id || ''; } function makeId() { return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`; } function getOnlineUsers() { return Object.values(onlineBySocketId); } function canManageMessage(message, actor) { if (!message || !actor) return false; const actorId = actor.userId; const msgUserId = message.userId; if (actorId !== null && typeof actorId !== 'undefined' && msgUserId !== null && typeof msgUserId !== 'undefined') { return String(actorId) === String(msgUserId); } return safeName(actor.name).toLowerCase() === safeName(message.name).toLowerCase(); } export function setupMapChatSocket(io) { io.on('connection', (socket) => { socket.on('map_chat:join', async (payload) => { const userId = payload?.userId ?? null; const name = safeName(payload?.name); socket.data.mapChatUser = { userId, name }; onlineBySocketId[socket.id] = { userId, name }; try { const history = await getAllMapChatMessages(); socket.emit('map_chat:history', history); } catch { socket.emit('map_chat:history', []); } io.emit('map_chat:presence', getOnlineUsers()); }); socket.on('map_chat:send', async (payload, ack) => { const done = (result) => { if (typeof ack === 'function') ack(result); }; const text = safeText(payload?.text); const replyTo = safeReplyTo(payload?.replyTo); const systemRef = safeSystemRef(payload?.systemRef); if (!text && !systemRef) { done({ ok: false, error: 'empty_message' }); return; } const user = socket.data.mapChatUser || onlineBySocketId[socket.id] || { userId: null, name: 'Гость' }; const nextMessage = { id: makeId(), userId: user.userId ?? null, name: safeName(user.name), text, ...(replyTo ? { replyTo } : {}), ...(systemRef ? { systemRef } : {}), }; try { const persistedMessage = await addMapChatMessage(nextMessage); io.emit('map_chat:message', persistedMessage); done({ ok: true, message: persistedMessage }); } catch (error) { done({ ok: false, error: String(error?.message || 'send_failed') }); } }); socket.on('map_chat:update', async (payload) => { const messageId = safeId(payload?.id); const text = safeText(payload?.text); if (!messageId || !text) return; const user = socket.data.mapChatUser || onlineBySocketId[socket.id] || { userId: null, name: 'Гость' }; try { const existing = await getMapChatMessageById(messageId); if (!existing || existing.isDeleted) return; if (!canManageMessage(existing, user)) return; const updated = await updateMapChatMessage(messageId, text); io.emit('map_chat:message_updated', updated); } catch { } }); socket.on('map_chat:delete', async (payload) => { const messageId = safeId(payload?.id); if (!messageId) return; const user = socket.data.mapChatUser || onlineBySocketId[socket.id] || { userId: null, name: 'Гость' }; try { const existing = await getMapChatMessageById(messageId); if (!existing || existing.isDeleted) return; if (!canManageMessage(existing, user)) return; const deleted = await deleteMapChatMessage(messageId); io.emit('map_chat:message_deleted', deleted); } catch { } }); socket.on('disconnect', () => { if (onlineBySocketId[socket.id]) { delete onlineBySocketId[socket.id]; io.emit('map_chat:presence', getOnlineUsers()); } }); }); }