feat(chat): личный чат тьютор ↔ админ с вложениями
Диалоги 1:1 (один тред на тьютора), отдельный SSE-канал whatido:chat вместо общей ленты notifications, вложения с белым списком расширений и вставкой из буфера (Ctrl+V), очистка по chat_retention_days. Таблицы в бэкап, chat_attachments добавлен в sweepOrphanedUploads.
This commit is contained in:
@@ -107,6 +107,7 @@ function createRedis({ url, prefix } = {}) {
|
||||
const memory = createMemoryBackend();
|
||||
const handlers = new Map();
|
||||
const subscribed = new Set();
|
||||
const channelListeners = new Map();
|
||||
const stats = { hits: 0, misses: 0, writes: 0, drops: 0, errors: 0, publishes: 0, fallbackOps: 0 };
|
||||
const CONNECT_TIMEOUT_MS = Math.max(500, parseInt(process.env.REDIS_CONNECT_TIMEOUT_MS || '5000', 10) || 5000);
|
||||
let client = null;
|
||||
@@ -175,12 +176,23 @@ function createRedis({ url, prefix } = {}) {
|
||||
}
|
||||
}
|
||||
|
||||
function channelListener(channel) {
|
||||
let fn = channelListeners.get(channel);
|
||||
if (!fn) {
|
||||
fn = message => deliver(channel, message);
|
||||
channelListeners.set(channel, fn);
|
||||
}
|
||||
return fn;
|
||||
}
|
||||
|
||||
async function subscribeChannel(channel) {
|
||||
if (!sub || !sub.isOpen || subscribed.has(channel)) return;
|
||||
if (!sub || !sub.isOpen) return;
|
||||
if (subscribed.has(channel)) return;
|
||||
subscribed.add(channel);
|
||||
try {
|
||||
await sub.subscribe(channel, message => deliver(channel, message));
|
||||
subscribed.add(channel);
|
||||
await sub.subscribe(channel, channelListener(channel));
|
||||
} catch (e) {
|
||||
subscribed.delete(channel);
|
||||
noteError('subscribe', e);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user