Files
multy-stub/server/routers/kfu-m-24-1/sber_mobile/messages.js
2025-06-14 16:12:03 +03:00

483 lines
22 KiB
JavaScript
Raw 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 router = require('express').Router();
const { getSupabaseClient } = require('./supabaseClient');
const { getIo } = require('../../../io'); // Импортируем Socket.IO
console.log(`📦 [Messages Module] Загружаем модуль messages.js...`);
console.log(`📦 [Messages Module] Время загрузки: ${new Date().toISOString()}`);
try {
const { moderationText } = require('./chat-ai-agent/chat-moderation'); // Импортируем функцию модерации
console.log(`✅ [Messages Module] Функция модерации загружена успешно`);
} catch (error) {
console.error(`❌ [Messages Module] Ошибка загрузки функции модерации:`, error);
}
try {
const MODERATION_CONFIG = require('./chat-ai-agent/moderation-config'); // Импортируем конфигурацию модерации
console.log(`✅ [Messages Module] Конфигурация модерации загружена:`, MODERATION_CONFIG);
} catch (error) {
console.error(`❌ [Messages Module] Ошибка загрузки конфигурации модерации:`, error);
}
const { moderationText } = require('./chat-ai-agent/chat-moderation'); // Импортируем функцию модерации
const MODERATION_CONFIG = require('./chat-ai-agent/moderation-config'); // Импортируем конфигурацию модерации
console.log(`📦 [Messages Module] Модуль messages.js загружен полностью`);
// Добавляем middleware для логирования всех запросов к messages роутеру
// Тестовый эндпоинт для проверки работы роутера
router.get('/messages/test', (req, res) => {
console.log(`🧪 [Messages Test] Тестовый эндпоинт вызван`);
console.log(`🧪 [Messages Test] Время: ${new Date().toISOString()}`);
res.json({
status: 'OK',
message: 'Messages router работает',
timestamp: new Date().toISOString(),
moderation_config: MODERATION_CONFIG
});
});
// Тестовый эндпоинт для немедленной проверки AI агента
router.post('/messages/test-ai', async (req, res) => {
console.log(`🧪 [AI Test] === ТЕСТИРОВАНИЕ AI АГЕНТА ===`);
console.log(`🧪 [AI Test] Время: ${new Date().toISOString()}`);
const { text } = req.body;
if (!text) {
return res.status(400).json({ error: 'text is required' });
}
console.log(`🧪 [AI Test] Тестируем текст: "${text}"`);
console.log(`🧪 [AI Test] Функция moderationText доступна: ${typeof moderationText}`);
try {
console.log(`🧪 [AI Test] Вызываем AI агент напрямую...`);
const startTime = Date.now();
const result = await moderationText('', text);
const endTime = Date.now();
console.log(`🧪 [AI Test] Результат от AI агента:`, result);
console.log(`🧪 [AI Test] Время выполнения: ${endTime - startTime}мс`);
const [comment, isApproved, finalMessage] = result;
res.json({
success: true,
text: text,
comment: comment,
isApproved: isApproved,
finalMessage: finalMessage,
processingTime: endTime - startTime,
timestamp: new Date().toISOString()
});
} catch (error) {
console.error(`❌ [AI Test] Ошибка тестирования AI агента:`, error);
console.error(`❌ [AI Test] Stack:`, error.stack);
res.status(500).json({
success: false,
error: error.message,
stack: error.stack,
timestamp: new Date().toISOString()
});
}
});
// Получить все сообщения в чате с информацией о пользователе
router.get('/messages', async (req, res) => {
try {
const { chat_id, limit = 50, offset = 0 } = req.query;
if (!chat_id) {
return res.status(400).json({ error: 'chat_id is required' });
}
const supabase = getSupabaseClient();
const { data, error } = await supabase
.from('messages')
.select(`
*,
user_profiles (
id,
full_name,
avatar_url
)
`)
.eq('chat_id', chat_id)
.order('created_at', { ascending: true })
.range(offset, offset + limit - 1);
if (error) {
return res.status(500).json({ error: 'Failed to fetch messages' });
}
// Получаем уникальные ID пользователей из сообщений, у которых нет профиля
const messagesWithoutProfiles = data.filter(msg => !msg.user_profiles);
const userIds = [...new Set(messagesWithoutProfiles.map(msg => msg.user_id))];
if (userIds.length > 0) {
const { data: profiles, error: profilesError } = await supabase
.from('user_profiles')
.select('id, full_name, avatar_url')
.in('id', userIds);
if (!profilesError && profiles) {
// Добавляем профили к сообщениям
data.forEach(message => {
if (!message.user_profiles) {
message.user_profiles = profiles.find(profile => profile.id === message.user_id) || null;
}
});
}
}
res.json(data);
} catch (err) {
res.status(500).json({ error: 'Unexpected error occurred' });
}
});
// Создать новое сообщение
router.post('/messages', async (req, res) => {
console.log(`🚀 [Message Send] === ВХОД В POST /messages ЭНДПОИНТ ===`);
console.log(`🚀 [Message Send] Время входа: ${new Date().toISOString()}`);
console.log(`🚀 [Message Send] Request method: ${req.method}`);
console.log(`🚀 [Message Send] Request URL: ${req.originalUrl || req.url}`);
console.log(`🚀 [Message Send] Request body:`, JSON.stringify(req.body, null, 2));
console.log(`🔌 [Message Send] Получаем Supabase клиент...`);
let supabase;
try {
supabase = getSupabaseClient();
console.log(`✅ [Message Send] Supabase клиент получен успешно`);
} catch (error) {
console.error(`❌ [Message Send] Ошибка получения Supabase клиента:`, error);
return res.status(500).json({ error: 'Database connection error' });
}
const { chat_id, user_id, text } = req.body;
console.log(`📤 [Message Send] Получен запрос на отправку сообщения:`);
console.log(`📤 [Message Send] Chat ID: ${chat_id}`);
console.log(`📤 [Message Send] User ID: ${user_id}`);
console.log(`📤 [Message Send] Text length: ${text ? text.length : 0} символов`);
console.log(`📤 [Message Send] Text preview: "${text ? (text.length > 100 ? text.substring(0, 100) + '...' : text) : 'empty'}"`);
if (!chat_id || !user_id || !text) {
console.log(`❌ [Message Send] Отклонен: отсутствуют обязательные поля`);
console.log(`❌ [Message Send] chat_id: ${chat_id}, user_id: ${user_id}, text: ${text}`);
return res.status(400).json({
error: 'chat_id, user_id, and text are required'
});
}
console.log(`💾 [Message Send] Сохраняем сообщение в Supabase...`);
// Создаем сообщение
const { data: newMessage, error } = await supabase
.from('messages')
.insert({ chat_id, user_id, text })
.select('*')
.single();
if (error) {
console.error(`❌ [Message Send] Ошибка сохранения в Supabase:`, error);
return res.status(400).json({ error: error.message });
}
console.log(`✅ [Message Send] Сообщение сохранено. ID: ${newMessage.id}`);
console.log(`📅 [Message Send] Время создания: ${newMessage.created_at}`);
console.log(`👤 [Message Send] Получаем профиль пользователя ${user_id}...`);
// Получаем профиль пользователя
const { data: userProfile, error: profileError } = await supabase
.from('user_profiles')
.select('id, full_name, avatar_url')
.eq('id', user_id)
.single();
if (profileError) {
console.log(`⚠️ [Message Send] Профиль пользователя не найден:`, profileError);
} else {
console.log(`✅ [Message Send] Профиль пользователя получен: ${userProfile.full_name || 'No name'}`);
}
// Объединяем сообщение с профилем
const data = {
...newMessage,
user_profiles: userProfile || null
};
console.log(`📊 [Message Send] Итоговые данные сообщения подготовлены`);
// === МОДЕРАЦИЯ ЧЕРЕЗ SUPABASE REAL-TIME ===
console.log(`🔄 [Message Send] Модерация будет выполняться через Supabase Real-time подписку`);
console.log(`🔄 [Message Send] Статус модерации: ${MODERATION_CONFIG.MODERATION_ENABLED ? 'включена' : 'отключена'}`);
console.log(`🔄 [Message Send] Задержка модерации: ${MODERATION_CONFIG.MODERATION_DELAY}мс`);
console.log(`🔄 [Message Send] После создания сообщения в БД сработает Supabase Real-time подписка в polling-chat.js`);
// Отправка через Socket.IO теперь происходит автоматически через Supabase Real-time подписку
// Это предотвращает дублирование сообщений
console.log(`✅ [Message Send] Сообщение успешно отправлено. Возвращаем ответ клиенту`);
console.log(`📤 [Message Send] === Процесс отправки сообщения завершен ===`);
res.json(data);
});
// Функция отложенной модерации сообщения
async function moderateMessage(messageId, messageText, chatId) {
const moderationStartTime = Date.now();
try {
console.log(`🔍 [Moderation] === НАЧАЛО МОДЕРАЦИИ СООБЩЕНИЯ ${messageId} ===`);
console.log(`🔍 [Moderation] Chat ID: ${chatId}`);
console.log(`🔍 [Moderation] Длина текста: ${messageText.length} символов`);
console.log(`🔍 [Moderation] Превью текста: "${messageText.length > 100 ? messageText.substring(0, 100) + '...' : messageText}"`);
console.log(`🔍 [Moderation] Время запуска: ${new Date().toISOString()}`);
// Вызываем функцию модерации
console.log(`🔍 [Moderation] Передаем сообщение AI агенту для анализа...`);
console.log(`🔍 [Moderation] Функция moderationText доступна: ${typeof moderationText}`);
console.log(`🔍 [Moderation] Тип сообщения: ${typeof messageText}`);
console.log(`🔍 [Moderation] Текст сообщения: "${messageText}"`);
let comment, isApproved, finalMessage;
try {
const result = await moderationText('', messageText);
console.log(`🔍 [Moderation] Результат от AI агента получен:`, result);
[comment, isApproved, finalMessage] = result;
console.log(`🔍 [Moderation] Распакованные значения: comment="${comment}", isApproved=${isApproved}, finalMessage="${finalMessage}"`);
} catch (moderationError) {
console.error(`❌ [Moderation] Ошибка при вызове AI агента:`, moderationError);
console.error(`❌ [Moderation] Stack trace:`, moderationError.stack);
// В случае ошибки одобряем сообщение
comment = '';
isApproved = true;
finalMessage = messageText;
console.log(`⚠️ [Moderation] Используем fallback значения из-за ошибки`);
}
const moderationTime = Date.now() - moderationStartTime;
console.log(`📝 [Moderation] === РЕЗУЛЬТАТ МОДЕРАЦИИ СООБЩЕНИЯ ${messageId} ===`);
console.log(`📝 [Moderation] Время модерации: ${moderationTime}мс`);
console.log(`📝 [Moderation] Решение: ${isApproved ? '✅ ОДОБРЕНО' : '❌ ОТКЛОНЕНО'}`);
console.log(`📝 [Moderation] Комментарий: "${comment || 'отсутствует'}"`);
console.log(`📝 [Moderation] Финальный текст: "${finalMessage}"`);
if (isApproved) {
console.log(`📝 [Moderation] Действие: сообщение остается без изменений`);
} else {
console.log(`📝 [Moderation] Действие: сообщение будет заменено в базе данных`);
}
// Если сообщение не прошло модерацию, обновляем его в базе данных
if (!isApproved) {
console.log(`💾 [Moderation] Начинаем обновление сообщения в базе данных...`);
const supabase = getSupabaseClient();
// Сначала получаем информацию о сообщении для получения chat_id
console.log(`💾 [Moderation] Получаем данные сообщения из базы...`);
const { data: messageData, error: fetchError } = await supabase
.from('messages')
.select('chat_id, user_id')
.eq('id', messageId)
.single();
if (fetchError) {
console.error(`❌ [Moderation] Ошибка получения данных сообщения ${messageId}:`, fetchError);
return;
}
console.log(`💾 [Moderation] Данные получены. Chat ID: ${messageData.chat_id}, User ID: ${messageData.user_id}`);
// Обновляем текст сообщения
console.log(`💾 [Moderation] Обновляем текст сообщения на: "${MODERATION_CONFIG.BLOCKED_MESSAGE_TEXT}"`);
const { data: updatedMessage, error } = await supabase
.from('messages')
.update({ text: MODERATION_CONFIG.BLOCKED_MESSAGE_TEXT })
.eq('id', messageId)
.select('*')
.single();
if (error) {
console.error(`❌ [Moderation] Ошибка обновления сообщения ${messageId}:`, error);
console.error(`❌ [Moderation] Детали ошибки:`, error);
} else {
console.log(`✅ [Moderation] Сообщение ${messageId} успешно обновлено в базе данных`);
console.log(`✅ [Moderation] Старый текст заменен на: "${updatedMessage.text}"`);
console.log(`✅ [Moderation] Время обновления: ${updatedMessage.updated_at || 'не указано'}`);
// Отправляем обновление через Socket.IO всем клиентам в чате
console.log(`📡 [Moderation] Начинаем отправку обновления через Socket.IO...`);
try {
const io = getIo();
if (io) {
console.log(`📡 [Moderation] Socket.IO подключение активно`);
// Получаем профиль пользователя для полной информации
console.log(`📡 [Moderation] Получаем профиль пользователя для обновления...`);
const { data: userProfile, error: profileError } = await supabase
.from('user_profiles')
.select('id, full_name, avatar_url')
.eq('id', messageData.user_id)
.single();
if (profileError) {
console.log(`⚠️ [Moderation] Ошибка получения профиля пользователя:`, profileError);
} else {
console.log(`✅ [Moderation] Профиль пользователя получен: ${userProfile.full_name || 'No name'}`);
}
const messageWithProfile = {
...updatedMessage,
user_profiles: userProfile || null
};
console.log(`📡 [Moderation] Отправляем обновление в комнату chat_${messageData.chat_id}...`);
io.to(`chat_${messageData.chat_id}`).emit('message_updated', messageWithProfile);
console.log(`📤 [Moderation] ✅ Обновление сообщения отправлено в чат ${messageData.chat_id}`);
console.log(`📤 [Moderation] Событие: message_updated`);
console.log(`📤 [Moderation] Получатели: все участники чата ${messageData.chat_id}`);
} else {
console.log(`⚠️ [Moderation] Socket.IO подключение недоступно`);
}
} catch (socketError) {
console.error(`❌ [Moderation] Ошибка отправки через Socket.IO:`, socketError);
console.error(`❌ [Moderation] Детали ошибки Socket.IO:`, socketError.message);
}
}
} else {
console.log(`✅ [Moderation] Сообщение ${messageId} прошло модерацию - никаких действий не требуется`);
}
const totalTime = Date.now() - moderationStartTime;
console.log(`🔍 [Moderation] === МОДЕРАЦИЯ СООБЩЕНИЯ ${messageId} ЗАВЕРШЕНА ===`);
console.log(`🔍 [Moderation] Общее время процесса: ${totalTime}мс`);
console.log(`🔍 [Moderation] Время завершения: ${new Date().toISOString()}`);
} catch (error) {
const totalTime = Date.now() - moderationStartTime;
console.error(`❌ [Moderation] === ОШИБКА МОДЕРАЦИИ СООБЩЕНИЯ ${messageId} ===`);
console.error(`❌ [Moderation] Время до ошибки: ${totalTime}мс`);
console.error(`❌ [Moderation] Тип ошибки: ${error.name || 'Unknown'}`);
console.error(`❌ [Moderation] Сообщение ошибки: ${error.message || 'Unknown error'}`);
console.error(`❌ [Moderation] Полная ошибка:`, error);
console.error(`❌ [Moderation] Стек ошибки:`, error.stack);
console.error(`❌ [Moderation] === КОНЕЦ ОБРАБОТКИ ОШИБКИ ===`);
}
}
// Получить конкретное сообщение
router.get('/messages/:message_id', async (req, res) => {
const supabase = getSupabaseClient();
const { message_id } = req.params;
// Получаем сообщение
const { data: message, error } = await supabase
.from('messages')
.select('*')
.eq('id', message_id)
.single();
if (error) return res.status(400).json({ error: error.message });
// Получаем профиль пользователя
const { data: userProfile } = await supabase
.from('user_profiles')
.select('id, full_name, avatar_url')
.eq('id', message.user_id)
.single();
// Объединяем сообщение с профилем
const data = {
...message,
user_profiles: userProfile || null
};
res.json(data);
});
// Получить последние сообщения для каждого чата (для списка чатов)
router.get('/chats/last-messages', async (req, res) => {
const supabase = getSupabaseClient();
const { building_id } = req.query;
if (!building_id) {
return res.status(400).json({ error: 'building_id required' });
}
// Получаем чаты и их последние сообщения через обычные запросы
const { data: chats, error: chatsError } = await supabase
.from('chats')
.select('*')
.eq('building_id', building_id);
if (chatsError) return res.status(400).json({ error: chatsError.message });
// Для каждого чата получаем последнее сообщение
const chatsWithMessages = await Promise.all(
chats.map(async (chat) => {
const { data: lastMessage } = await supabase
.from('messages')
.select(`
*,
user_profiles:user_id (
id,
full_name,
avatar_url
)
`)
.eq('chat_id', chat.id)
.order('created_at', { ascending: false })
.limit(1)
.single();
return {
...chat,
last_message: lastMessage || null
};
})
);
res.json(chatsWithMessages);
});
// Удалить сообщение (только для автора)
router.delete('/messages/:message_id', async (req, res) => {
const supabase = getSupabaseClient();
const { message_id } = req.params;
const { user_id } = req.body;
if (!user_id) {
return res.status(400).json({ error: 'user_id required' });
}
// Проверяем, что пользователь является автором сообщения
const { data: message, error: fetchError } = await supabase
.from('messages')
.select('user_id')
.eq('id', message_id)
.single();
if (fetchError) return res.status(400).json({ error: fetchError.message });
if (message.user_id !== user_id) {
return res.status(403).json({ error: 'You can only delete your own messages' });
}
const { error } = await supabase
.from('messages')
.delete()
.eq('id', message_id);
if (error) return res.status(400).json({ error: error.message });
res.json({ success: true, message: 'Message deleted successfully' });
});
module.exports = router;