postgres
This commit is contained in:
313
server.js
313
server.js
@@ -9,6 +9,7 @@ require('dotenv').config();
|
||||
const { db, logActivity, createUserTaskFolder, saveTaskMetadata, updateTaskMetadata, checkTaskAccess } = require('./database');
|
||||
const authService = require('./auth');
|
||||
const adminRouter = require('./admin-server');
|
||||
const postgresLogger = require('./postgres');
|
||||
|
||||
const app = express();
|
||||
const PORT = process.env.PORT || 3000;
|
||||
@@ -237,42 +238,98 @@ async function sendTaskNotifications(type, taskId, taskTitle, taskDescription, a
|
||||
if (!process.env.NOTIFICATION_SERVICE_URL ||
|
||||
!process.env.NOTIFICATION_SERVICE_LOGIN ||
|
||||
!process.env.NOTIFICATION_SERVICE_PASSWORD) {
|
||||
console.log('Настройки сервиса уведомлений не заданы');
|
||||
console.log('⚠️ Настройки сервиса уведомлений не заданы');
|
||||
|
||||
// Логируем в PostgreSQL даже если уведомления не отправляются
|
||||
await logNotificationToPostgres({
|
||||
type,
|
||||
taskId,
|
||||
taskTitle,
|
||||
taskDescription,
|
||||
authorId,
|
||||
comment,
|
||||
status,
|
||||
userName,
|
||||
error: 'Сервис уведомлений не настроен'
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const participants = await new Promise((resolve, reject) => {
|
||||
db.all(`
|
||||
SELECT t.created_by as user_id, u.name as user_name, u.login as user_login, u.email, 'creator' as role
|
||||
console.log(`📢 Начинаем отправку уведомлений для задачи ${taskId}:`);
|
||||
console.log(` Тип: ${type}, Автор действия: ${authorId}, Название: ${taskTitle}`);
|
||||
|
||||
// Получаем заказчика (создателя задачи) ОТДЕЛЬНО
|
||||
const creator = await new Promise((resolve, reject) => {
|
||||
db.get(`
|
||||
SELECT t.created_by as user_id, u.name as user_name, u.login as user_login, u.email
|
||||
FROM tasks t
|
||||
LEFT JOIN users u ON t.created_by = u.id
|
||||
WHERE t.id = ?
|
||||
|
||||
UNION
|
||||
|
||||
SELECT ta.user_id, u.name as user_name, u.login as user_login, u.email, 'assignee' as role
|
||||
FROM task_assignments ta
|
||||
LEFT JOIN users u ON ta.user_id = u.id
|
||||
WHERE ta.task_id = ?
|
||||
`, [taskId, taskId], (err, rows) => {
|
||||
`, [taskId], (err, row) => {
|
||||
if (err) reject(err);
|
||||
else resolve(rows);
|
||||
else resolve(row);
|
||||
});
|
||||
});
|
||||
|
||||
if (!participants || participants.length === 0) {
|
||||
console.log('Нет участников для уведомления');
|
||||
return;
|
||||
// Получаем исполнителей ОТДЕЛЬНО
|
||||
const assignees = await new Promise((resolve, reject) => {
|
||||
db.all(`
|
||||
SELECT ta.user_id, u.name as user_name, u.login as user_login, u.email
|
||||
FROM task_assignments ta
|
||||
LEFT JOIN users u ON ta.user_id = u.id
|
||||
WHERE ta.task_id = ?
|
||||
`, [taskId], (err, rows) => {
|
||||
if (err) reject(err);
|
||||
else resolve(rows || []);
|
||||
});
|
||||
});
|
||||
|
||||
// Собираем всех участников
|
||||
const participants = [];
|
||||
if (creator) {
|
||||
participants.push({
|
||||
...creator,
|
||||
role: 'creator',
|
||||
is_creator: true
|
||||
});
|
||||
}
|
||||
|
||||
if (assignees && assignees.length > 0) {
|
||||
assignees.forEach(assignee => {
|
||||
participants.push({
|
||||
...assignee,
|
||||
role: 'assignee',
|
||||
is_creator: false
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Получаем информацию об авторе действия
|
||||
const author = await new Promise((resolve, reject) => {
|
||||
db.get("SELECT name FROM users WHERE id = ?", [authorId], (err, row) => {
|
||||
db.get("SELECT name, login FROM users WHERE id = ?", [authorId], (err, row) => {
|
||||
if (err) reject(err);
|
||||
else resolve(row);
|
||||
});
|
||||
});
|
||||
|
||||
const authorName = author ? author.name : 'Система';
|
||||
const authorLogin = author ? author.login : 'system';
|
||||
|
||||
// Логируем в PostgreSQL
|
||||
const postgresLogIds = await logNotificationToPostgres({
|
||||
type,
|
||||
taskId,
|
||||
taskTitle,
|
||||
taskDescription,
|
||||
authorId,
|
||||
authorName,
|
||||
authorLogin,
|
||||
participants,
|
||||
comment,
|
||||
status,
|
||||
userName
|
||||
});
|
||||
|
||||
let subject, content;
|
||||
|
||||
@@ -323,15 +380,26 @@ async function sendTaskNotifications(type, taskId, taskTitle, taskDescription, a
|
||||
break;
|
||||
|
||||
default:
|
||||
console.log(`⚠️ Неизвестный тип уведомления: ${type}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Фильтруем получателей: исключаем автора действия
|
||||
const recipientIds = participants
|
||||
.filter(p => p.user_id !== authorId)
|
||||
.filter(p => {
|
||||
const shouldExclude = p.user_id === authorId;
|
||||
if (shouldExclude) {
|
||||
console.log(` ✋ Исключаем автора действия: ${p.user_name} (ID: ${p.user_id})`);
|
||||
}
|
||||
return !shouldExclude;
|
||||
})
|
||||
.map(p => p.user_id);
|
||||
|
||||
if (recipientIds.length === 0) {
|
||||
console.log('Нет получателей для уведомления (все участники - автор изменения)');
|
||||
console.log('❌ Нет получателей для уведомления (все участники - автор изменения)');
|
||||
|
||||
// Обновляем статус в PostgreSQL
|
||||
await updatePostgresLogStatus(postgresLogIds, 'no_recipients', 'Нет получателей после фильтрации');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -347,27 +415,144 @@ async function sendTaskNotifications(type, taskId, taskTitle, taskDescription, a
|
||||
formData.append('recipients', JSON.stringify(recipientIds));
|
||||
formData.append('deliveryMethods', JSON.stringify(['email', 'telegram', 'vk']));
|
||||
|
||||
const response = await fetch(process.env.NOTIFICATION_SERVICE_URL, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Authorization': `Basic ${authHeader}`
|
||||
},
|
||||
body: formData
|
||||
});
|
||||
console.log(`🚀 Отправляем запрос на сервис уведомлений...`);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
try {
|
||||
const response = await fetch(process.env.NOTIFICATION_SERVICE_URL, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Authorization': `Basic ${authHeader}`
|
||||
},
|
||||
body: formData
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
console.log(`✅ Уведомления успешно отправлены для задачи ${taskId}`);
|
||||
|
||||
// Обновляем статус в PostgreSQL
|
||||
await updatePostgresLogStatus(postgresLogIds, 'sent', null, new Date().toISOString());
|
||||
|
||||
console.log(` Результат от сервиса:`, result);
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Ошибка отправки уведомлений:', error);
|
||||
|
||||
// Обновляем статус в PostgreSQL
|
||||
await updatePostgresLogStatus(postgresLogIds, 'failed', error.message);
|
||||
|
||||
console.error(' Детали ошибки:', {
|
||||
taskId,
|
||||
type,
|
||||
authorId,
|
||||
errorMessage: error.message,
|
||||
stack: error.stack
|
||||
});
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
console.log(`✅ Уведомления отправлены для задачи ${taskId}:`, {
|
||||
type: type,
|
||||
recipients: recipientIds.length,
|
||||
authorExcluded: authorId
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('❌ Общая ошибка при обработке уведомлений:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// Вспомогательные функции для работы с PostgreSQL
|
||||
async function logNotificationToPostgres(data) {
|
||||
try {
|
||||
const {
|
||||
type,
|
||||
taskId,
|
||||
taskTitle,
|
||||
taskDescription,
|
||||
authorId,
|
||||
authorName,
|
||||
authorLogin,
|
||||
participants = [],
|
||||
comment = '',
|
||||
status = '',
|
||||
userName = '',
|
||||
error = ''
|
||||
} = data;
|
||||
|
||||
// Создаем сообщение
|
||||
let messageContent = '';
|
||||
switch (type) {
|
||||
case 'created':
|
||||
messageContent = `Создана новая задача: ${taskTitle}`;
|
||||
break;
|
||||
case 'updated':
|
||||
messageContent = `Обновлена задача: ${taskTitle}`;
|
||||
break;
|
||||
case 'rework':
|
||||
messageContent = `Задача возвращена на доработку: ${taskTitle}. Комментарий: ${comment}`;
|
||||
break;
|
||||
case 'closed':
|
||||
messageContent = `Задача закрыта: ${taskTitle}`;
|
||||
break;
|
||||
case 'status_changed':
|
||||
messageContent = `Изменен статус задачи: ${taskTitle}. Новый статус: ${getStatusText(status)}`;
|
||||
break;
|
||||
}
|
||||
|
||||
// Логируем для каждого получателя отдельно
|
||||
const recipientsToNotify = participants.filter(p => p.user_id !== authorId);
|
||||
const logIds = [];
|
||||
|
||||
for (const recipient of recipientsToNotify) {
|
||||
const logId = await postgresLogger.logNotification({
|
||||
taskId,
|
||||
taskTitle,
|
||||
taskDescription,
|
||||
notificationType: type,
|
||||
authorId,
|
||||
authorName,
|
||||
authorLogin,
|
||||
recipientId: recipient.user_id,
|
||||
recipientName: recipient.user_name,
|
||||
recipientLogin: recipient.user_login,
|
||||
messageContent: `${messageContent}\n\nЗадача: ${taskTitle}\nОписание: ${taskDescription || 'Без описания'}\nАвтор: ${authorName}`,
|
||||
messageSubject: getNotificationSubject(type, taskTitle),
|
||||
deliveryMethods: ['email', 'telegram', 'vk'],
|
||||
comments: error ? `Ошибка: ${error}` : comment
|
||||
});
|
||||
|
||||
if (logId) {
|
||||
logIds.push(logId);
|
||||
}
|
||||
}
|
||||
|
||||
return logIds;
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Ошибка отправки уведомлений:', error);
|
||||
console.error('❌ Ошибка логирования в PostgreSQL:', error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
async function updatePostgresLogStatus(logIds, status, errorMessage = null, sentAt = null) {
|
||||
if (!logIds || logIds.length === 0) return;
|
||||
|
||||
for (const logId of logIds) {
|
||||
await postgresLogger.updateNotificationStatus(logId, status, errorMessage, sentAt);
|
||||
}
|
||||
}
|
||||
|
||||
function getNotificationSubject(type, taskTitle) {
|
||||
switch (type) {
|
||||
case 'created':
|
||||
return `Новая задача: ${taskTitle}`;
|
||||
case 'updated':
|
||||
return `Обновлена задача: ${taskTitle}`;
|
||||
case 'rework':
|
||||
return `Задача возвращена на доработку: ${taskTitle}`;
|
||||
case 'closed':
|
||||
return `Задача закрыта: ${taskTitle}`;
|
||||
case 'status_changed':
|
||||
return `Изменен статус задачи: ${taskTitle}`;
|
||||
default:
|
||||
return `Уведомление по задаче: ${taskTitle}`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1403,7 +1588,65 @@ app.get('/admin', (req, res) => {
|
||||
}
|
||||
res.sendFile(path.join(__dirname, 'public/admin.html'));
|
||||
});
|
||||
// API для получения логов уведомлений
|
||||
app.get('/api/notification-logs', requireAuth, async (req, res) => {
|
||||
try {
|
||||
const {
|
||||
taskId,
|
||||
status,
|
||||
startDate,
|
||||
endDate,
|
||||
limit = 50,
|
||||
offset = 0
|
||||
} = req.query;
|
||||
|
||||
const logs = await postgresLogger.getNotifications({
|
||||
taskId: taskId ? parseInt(taskId) : null,
|
||||
userId: req.session.user.id,
|
||||
status,
|
||||
startDate,
|
||||
endDate,
|
||||
limit: parseInt(limit),
|
||||
offset: parseInt(offset)
|
||||
});
|
||||
|
||||
res.json(logs);
|
||||
} catch (error) {
|
||||
console.error('Ошибка получения логов уведомлений:', error);
|
||||
res.status(500).json({ error: 'Ошибка получения логов' });
|
||||
}
|
||||
});
|
||||
|
||||
// API для получения статистики
|
||||
app.get('/api/notification-stats', requireAuth, async (req, res) => {
|
||||
try {
|
||||
const { period = 'day' } = req.query;
|
||||
|
||||
if (req.session.user.role !== 'admin') {
|
||||
return res.status(403).json({ error: 'Недостаточно прав' });
|
||||
}
|
||||
|
||||
const stats = await postgresLogger.getStatistics(period);
|
||||
res.json(stats);
|
||||
} catch (error) {
|
||||
console.error('Ошибка получения статистики:', error);
|
||||
res.status(500).json({ error: 'Ошибка получения статистики' });
|
||||
}
|
||||
});
|
||||
|
||||
// API для проверки состояния PostgreSQL
|
||||
app.get('/api/postgres-health', requireAuth, async (req, res) => {
|
||||
try {
|
||||
if (req.session.user.role !== 'admin') {
|
||||
return res.status(403).json({ error: 'Недостаточно прав' });
|
||||
}
|
||||
|
||||
const health = await postgresLogger.healthCheck();
|
||||
res.json(health);
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
app.listen(PORT, () => {
|
||||
console.log(`CRM сервер запущен на порту ${PORT}`);
|
||||
console.log(`Откройте http://localhost:${PORT} в браузере`);
|
||||
|
||||
Reference in New Issue
Block a user