308 lines
14 KiB
JavaScript
308 lines
14 KiB
JavaScript
const express = require('express');
|
||
const router = express.Router();
|
||
const db = require('../db');
|
||
const { broadcastMessagesUpdate } = require('../wsHub');
|
||
const telegramBot = require('../services/telegramBot');
|
||
const emailBot = new (require('../services/emailBot'))();
|
||
const { isUserBlocked } = require('../utils/userUtils');
|
||
|
||
// GET /api/messages?userId=123
|
||
router.get('/', async (req, res) => {
|
||
const userId = req.query.userId;
|
||
const conversationId = req.query.conversationId;
|
||
try {
|
||
let result;
|
||
if (conversationId) {
|
||
result = await db.getQuery()(
|
||
`SELECT id, user_id, sender_type, content, channel, role, direction, created_at, attachment_filename, attachment_mimetype, attachment_size, attachment_data, metadata
|
||
FROM messages
|
||
WHERE conversation_id = $1
|
||
ORDER BY created_at ASC`,
|
||
[conversationId]
|
||
);
|
||
} else if (userId) {
|
||
result = await db.getQuery()(
|
||
`SELECT id, user_id, sender_type, content, channel, role, direction, created_at, attachment_filename, attachment_mimetype, attachment_size, attachment_data, metadata
|
||
FROM messages
|
||
WHERE user_id = $1
|
||
ORDER BY created_at ASC`,
|
||
[userId]
|
||
);
|
||
} else {
|
||
result = await db.getQuery()(
|
||
`SELECT id, user_id, sender_type, content, channel, role, direction, created_at, attachment_filename, attachment_mimetype, attachment_size, attachment_data, metadata
|
||
FROM messages
|
||
ORDER BY created_at ASC`
|
||
);
|
||
}
|
||
res.json(result.rows);
|
||
} catch (e) {
|
||
res.status(500).json({ error: 'DB error', details: e.message });
|
||
}
|
||
});
|
||
|
||
// POST /api/messages
|
||
router.post('/', async (req, res) => {
|
||
const { user_id, sender_type, content, channel, role, direction, attachment_filename, attachment_mimetype, attachment_size, attachment_data, metadata } = req.body;
|
||
try {
|
||
// Проверка блокировки пользователя
|
||
if (await isUserBlocked(user_id)) {
|
||
return res.status(403).json({ error: 'Пользователь заблокирован. Сообщение не принимается.' });
|
||
}
|
||
// Проверка наличия идентификатора для выбранного канала
|
||
if (channel === 'email') {
|
||
const emailIdentity = await db.getQuery()(
|
||
'SELECT provider_id FROM user_identities WHERE user_id = $1 AND provider = $2 LIMIT 1',
|
||
[user_id, 'email']
|
||
);
|
||
if (emailIdentity.rows.length === 0) {
|
||
return res.status(400).json({ error: 'У пользователя не указан email. Сообщение не отправлено.' });
|
||
}
|
||
}
|
||
if (channel === 'telegram') {
|
||
const tgIdentity = await db.getQuery()(
|
||
'SELECT provider_id FROM user_identities WHERE user_id = $1 AND provider = $2 LIMIT 1',
|
||
[user_id, 'telegram']
|
||
);
|
||
if (tgIdentity.rows.length === 0) {
|
||
return res.status(400).json({ error: 'У пользователя не привязан Telegram. Сообщение не отправлено.' });
|
||
}
|
||
}
|
||
if (channel === 'wallet' || channel === 'web3' || channel === 'web') {
|
||
const walletIdentity = await db.getQuery()(
|
||
'SELECT provider_id FROM user_identities WHERE user_id = $1 AND provider = $2 LIMIT 1',
|
||
[user_id, 'wallet']
|
||
);
|
||
if (walletIdentity.rows.length === 0) {
|
||
return res.status(400).json({ error: 'У пользователя не привязан кошелёк. Сообщение не отправлено.' });
|
||
}
|
||
}
|
||
// 1. Проверяем, есть ли беседа для user_id
|
||
let conversationResult = await db.getQuery()(
|
||
'SELECT * FROM conversations WHERE user_id = $1 ORDER BY updated_at DESC, created_at DESC LIMIT 1',
|
||
[user_id]
|
||
);
|
||
let conversation;
|
||
if (conversationResult.rows.length === 0) {
|
||
// 2. Если нет — создаём новую беседу
|
||
const title = `Чат с пользователем ${user_id}`;
|
||
const newConv = await db.getQuery()(
|
||
'INSERT INTO conversations (user_id, title, created_at, updated_at) VALUES ($1, $2, NOW(), NOW()) RETURNING *',
|
||
[user_id, title]
|
||
);
|
||
conversation = newConv.rows[0];
|
||
} else {
|
||
conversation = conversationResult.rows[0];
|
||
}
|
||
// 3. Сохраняем сообщение с conversation_id
|
||
const result = await db.getQuery()(
|
||
`INSERT INTO messages (user_id, conversation_id, sender_type, content, channel, role, direction, created_at, attachment_filename, attachment_mimetype, attachment_size, attachment_data, metadata)
|
||
VALUES ($1,$2,$3,$4,$5,$6,$7,NOW(),$8,$9,$10,$11,$12) RETURNING *`,
|
||
[user_id, conversation.id, sender_type, content, channel, role, direction, attachment_filename, attachment_mimetype, attachment_size, attachment_data, metadata]
|
||
);
|
||
// 4. Если это исходящее сообщение для Telegram — отправляем через бота
|
||
if (channel === 'telegram' && direction === 'out') {
|
||
try {
|
||
console.log(`[messages.js] Попытка отправки сообщения в Telegram для user_id=${user_id}`);
|
||
// Получаем Telegram ID пользователя
|
||
const tgIdentity = await db.getQuery()(
|
||
'SELECT provider_id FROM user_identities WHERE user_id = $1 AND provider = $2 LIMIT 1',
|
||
[user_id, 'telegram']
|
||
);
|
||
console.log(`[messages.js] Результат поиска Telegram ID:`, tgIdentity.rows);
|
||
if (tgIdentity.rows.length > 0) {
|
||
const telegramId = tgIdentity.rows[0].provider_id;
|
||
console.log(`[messages.js] Отправка сообщения в Telegram ID: ${telegramId}, текст: ${content}`);
|
||
const bot = await telegramBot.getBot();
|
||
try {
|
||
const sendResult = await bot.telegram.sendMessage(telegramId, content);
|
||
console.log(`[messages.js] Результат отправки в Telegram:`, sendResult);
|
||
} catch (sendErr) {
|
||
console.error(`[messages.js] Ошибка при отправке в Telegram:`, sendErr);
|
||
}
|
||
} else {
|
||
console.warn(`[messages.js] Не найден Telegram ID для user_id=${user_id}`);
|
||
}
|
||
} catch (err) {
|
||
console.error('[messages.js] Ошибка отправки сообщения в Telegram:', err);
|
||
}
|
||
}
|
||
// 5. Если это исходящее сообщение для Email — отправляем email
|
||
if (channel === 'email' && direction === 'out') {
|
||
try {
|
||
// Получаем email пользователя
|
||
const emailIdentity = await db.getQuery()(
|
||
'SELECT provider_id FROM user_identities WHERE user_id = $1 AND provider = $2 LIMIT 1',
|
||
[user_id, 'email']
|
||
);
|
||
if (emailIdentity.rows.length > 0) {
|
||
const email = emailIdentity.rows[0].provider_id;
|
||
await emailBot.sendEmail(email, 'Новое сообщение', content);
|
||
}
|
||
} catch (err) {
|
||
console.error('[messages.js] Ошибка отправки email:', err);
|
||
}
|
||
}
|
||
broadcastMessagesUpdate();
|
||
res.json({ success: true, message: result.rows[0] });
|
||
} catch (e) {
|
||
res.status(500).json({ error: 'DB error', details: e.message });
|
||
}
|
||
});
|
||
|
||
// POST /api/messages/mark-read
|
||
router.post('/mark-read', async (req, res) => {
|
||
try {
|
||
console.log('[DEBUG] /mark-read req.user:', req.user);
|
||
console.log('[DEBUG] /mark-read req.body:', req.body);
|
||
const adminId = req.user && req.user.id;
|
||
const { userId, lastReadAt } = req.body;
|
||
if (!adminId) {
|
||
console.error('[ERROR] /mark-read: adminId (req.user.id) is missing');
|
||
return res.status(401).json({ error: 'Unauthorized: adminId missing' });
|
||
}
|
||
if (!userId || !lastReadAt) {
|
||
console.error('[ERROR] /mark-read: userId or lastReadAt missing');
|
||
return res.status(400).json({ error: 'userId and lastReadAt required' });
|
||
}
|
||
await db.query(`
|
||
INSERT INTO admin_read_messages (admin_id, user_id, last_read_at)
|
||
VALUES ($1, $2, $3)
|
||
ON CONFLICT (admin_id, user_id) DO UPDATE SET last_read_at = EXCLUDED.last_read_at
|
||
`, [adminId, userId, lastReadAt]);
|
||
res.json({ success: true });
|
||
} catch (e) {
|
||
console.error('[ERROR] /mark-read:', e);
|
||
res.status(500).json({ error: e.message });
|
||
}
|
||
});
|
||
|
||
// GET /api/messages/read-status
|
||
router.get('/read-status', async (req, res) => {
|
||
try {
|
||
console.log('[DEBUG] /read-status req.user:', req.user);
|
||
console.log('[DEBUG] /read-status req.session:', req.session);
|
||
console.log('[DEBUG] /read-status req.session.userId:', req.session && req.session.userId);
|
||
const adminId = req.user && req.user.id;
|
||
if (!adminId) {
|
||
console.error('[ERROR] /read-status: adminId (req.user.id) is missing');
|
||
return res.status(401).json({ error: 'Unauthorized: adminId missing' });
|
||
}
|
||
const result = await db.query('SELECT user_id, last_read_at FROM admin_read_messages WHERE admin_id = $1', [adminId]);
|
||
console.log('[DEBUG] /read-status SQL result:', result.rows);
|
||
const map = {};
|
||
for (const row of result.rows) {
|
||
map[row.user_id] = row.last_read_at;
|
||
}
|
||
res.json(map);
|
||
} catch (e) {
|
||
console.error('[ERROR] /read-status:', e);
|
||
res.status(500).json({ error: e.message });
|
||
}
|
||
});
|
||
|
||
// GET /api/conversations?userId=123
|
||
router.get('/conversations', async (req, res) => {
|
||
const userId = req.query.userId;
|
||
if (!userId) return res.status(400).json({ error: 'userId required' });
|
||
try {
|
||
const result = await db.getQuery()(
|
||
'SELECT * FROM conversations WHERE user_id = $1 ORDER BY updated_at DESC, created_at DESC LIMIT 1',
|
||
[userId]
|
||
);
|
||
if (result.rows.length === 0) {
|
||
return res.status(404).json({ error: 'Conversation not found' });
|
||
}
|
||
res.json(result.rows[0]);
|
||
} catch (e) {
|
||
res.status(500).json({ error: 'DB error', details: e.message });
|
||
}
|
||
});
|
||
|
||
// Массовая рассылка сообщения во все каналы пользователя
|
||
router.post('/broadcast', async (req, res) => {
|
||
const { user_id, content } = req.body;
|
||
if (!user_id || !content) {
|
||
return res.status(400).json({ error: 'user_id и content обязательны' });
|
||
}
|
||
try {
|
||
// Получаем все идентификаторы пользователя
|
||
const identitiesRes = await db.getQuery()(
|
||
'SELECT provider, provider_id FROM user_identities WHERE user_id = $1',
|
||
[user_id]
|
||
);
|
||
const identities = identitiesRes.rows;
|
||
// --- Найти или создать беседу (conversation) ---
|
||
let conversationResult = await db.getQuery()(
|
||
'SELECT * FROM conversations WHERE user_id = $1 ORDER BY updated_at DESC, created_at DESC LIMIT 1',
|
||
[user_id]
|
||
);
|
||
let conversation;
|
||
if (conversationResult.rows.length === 0) {
|
||
const title = `Чат с пользователем ${user_id}`;
|
||
const newConv = await db.getQuery()(
|
||
'INSERT INTO conversations (user_id, title, created_at, updated_at) VALUES ($1, $2, NOW(), NOW()) RETURNING *',
|
||
[user_id, title]
|
||
);
|
||
conversation = newConv.rows[0];
|
||
} else {
|
||
conversation = conversationResult.rows[0];
|
||
}
|
||
const results = [];
|
||
let sent = false;
|
||
// Email
|
||
const email = identities.find(i => i.provider === 'email')?.provider_id;
|
||
if (email) {
|
||
try {
|
||
await emailBot.sendEmail(email, 'Новое сообщение', content);
|
||
// Сохраняем в messages с conversation_id
|
||
await db.getQuery()(
|
||
`INSERT INTO messages (user_id, conversation_id, sender_type, content, channel, role, direction, created_at, metadata)
|
||
VALUES ($1, $2, $3, $4, $5, $6, $7, NOW(), $8)`,
|
||
[user_id, conversation.id, 'admin', content, 'email', 'user', 'out', JSON.stringify({ broadcast: true })]
|
||
);
|
||
results.push({ channel: 'email', status: 'sent' });
|
||
sent = true;
|
||
} catch (err) {
|
||
results.push({ channel: 'email', status: 'error', error: err.message });
|
||
}
|
||
}
|
||
// Telegram
|
||
const telegram = identities.find(i => i.provider === 'telegram')?.provider_id;
|
||
if (telegram) {
|
||
try {
|
||
const bot = await telegramBot.getBot();
|
||
await bot.telegram.sendMessage(telegram, content);
|
||
await db.getQuery()(
|
||
`INSERT INTO messages (user_id, conversation_id, sender_type, content, channel, role, direction, created_at, metadata)
|
||
VALUES ($1, $2, $3, $4, $5, $6, $7, NOW(), $8)`,
|
||
[user_id, conversation.id, 'admin', content, 'telegram', 'user', 'out', JSON.stringify({ broadcast: true })]
|
||
);
|
||
results.push({ channel: 'telegram', status: 'sent' });
|
||
sent = true;
|
||
} catch (err) {
|
||
results.push({ channel: 'telegram', status: 'error', error: err.message });
|
||
}
|
||
}
|
||
// Wallet/web3
|
||
const wallet = identities.find(i => i.provider === 'wallet')?.provider_id;
|
||
if (wallet) {
|
||
// Здесь можно реализовать отправку через web3, если нужно
|
||
await db.getQuery()(
|
||
`INSERT INTO messages (user_id, conversation_id, sender_type, content, channel, role, direction, created_at, metadata)
|
||
VALUES ($1, $2, $3, $4, $5, $6, $7, NOW(), $8)`,
|
||
[user_id, conversation.id, 'admin', content, 'wallet', 'user', 'out', JSON.stringify({ broadcast: true })]
|
||
);
|
||
results.push({ channel: 'wallet', status: 'saved' });
|
||
sent = true;
|
||
}
|
||
if (!sent) {
|
||
return res.status(400).json({ error: 'У пользователя нет ни одного канала для рассылки.' });
|
||
}
|
||
res.json({ success: true, results });
|
||
} catch (e) {
|
||
res.status(500).json({ error: 'Broadcast error', details: e.message });
|
||
}
|
||
});
|
||
|
||
module.exports = router;
|