// admin-server.js const express = require('express'); const router = express.Router(); // Middleware для проверки прав администратора const requireAdmin = (req, res, next) => { if (!req.session.user || req.session.user.role !== 'admin') { return res.status(403).json({ error: 'Недостаточно прав' }); } next(); }; // Статистика router.get('/admin/stats', requireAdmin, async (req, res) => { try { const { getDb } = require('./database'); const db = getDb(); // Получаем статистику по задачам const tasksStats = await new Promise((resolve, reject) => { db.get(` SELECT COUNT(*) as totalTasks, COUNT(CASE WHEN status = 'active' AND closed_at IS NULL THEN 1 END) as activeTasks, COUNT(CASE WHEN closed_at IS NOT NULL THEN 1 END) as closedTasks, COUNT(CASE WHEN status = 'deleted' THEN 1 END) as deletedTasks FROM tasks `, [], (err, row) => { if (err) reject(err); else resolve(row || { totalTasks: 0, activeTasks: 0, closedTasks: 0, deletedTasks: 0 }); }); }); // Статистика по назначениям const assignmentsStats = await new Promise((resolve, reject) => { db.get(` SELECT COUNT(*) as totalAssignments, COUNT(CASE WHEN status = 'assigned' THEN 1 END) as assignedCount, COUNT(CASE WHEN status = 'in_progress' THEN 1 END) as inProgressCount, COUNT(CASE WHEN status = 'completed' THEN 1 END) as completedCount, COUNT(CASE WHEN status = 'overdue' THEN 1 END) as overdueCount, COUNT(CASE WHEN status = 'rework' THEN 1 END) as reworkCount FROM task_assignments `, [], (err, row) => { if (err) reject(err); else resolve(row || { totalAssignments: 0, assignedCount: 0, inProgressCount: 0, completedCount: 0, overdueCount: 0, reworkCount: 0 }); }); }); // Статистика по пользователям const usersStats = await new Promise((resolve, reject) => { db.get(` SELECT COUNT(*) as totalUsers, COUNT(CASE WHEN role = 'admin' THEN 1 END) as adminUsers, COUNT(CASE WHEN role = 'teacher' THEN 1 END) as teacherUsers, COUNT(CASE WHEN auth_type = 'ldap' THEN 1 END) as ldapUsers, COUNT(CASE WHEN auth_type = 'local' THEN 1 END) as localUsers FROM users `, [], (err, row) => { if (err) reject(err); else resolve(row || { totalUsers: 0, adminUsers: 0, teacherUsers: 0, ldapUsers: 0, localUsers: 0 }); }); }); // Статистика по файлам const filesStats = await new Promise((resolve, reject) => { db.get(` SELECT COUNT(*) as totalFiles, SUM(file_size) as totalFilesSize FROM task_files `, [], (err, row) => { if (err) reject(err); else resolve(row || { totalFiles: 0, totalFilesSize: 0 }); }); }); res.json({ ...tasksStats, ...assignmentsStats, ...usersStats, ...filesStats, timestamp: new Date().toISOString() }); } catch (error) { console.error('Ошибка получения статистики:', error); res.status(500).json({ error: 'Ошибка получения статистики' }); } }); // Получение всех пользователей router.get('/admin/users', requireAdmin, async (req, res) => { try { const { getDb } = require('./database'); const db = getDb(); const query = ` SELECT id, login, name, email, role, auth_type, groups, description, created_at, last_login, updated_at FROM users ORDER BY name `; db.all(query, [], (err, users) => { if (err) { console.error('Ошибка получения пользователей:', err); return res.status(500).json({ error: 'Ошибка сервера' }); } res.json(users); }); } catch (error) { console.error('Ошибка:', error); res.status(500).json({ error: 'Ошибка сервера' }); } }); // Получение конкретного пользователя router.get('/admin/users/:id', requireAdmin, async (req, res) => { try { const { getDb } = require('./database'); const db = getDb(); const userId = req.params.id; const query = ` SELECT id, login, name, email, role, auth_type, groups, description, created_at, last_login, updated_at FROM users WHERE id = ? `; db.get(query, [userId], (err, user) => { if (err) { console.error('Ошибка получения пользователя:', err); return res.status(500).json({ error: 'Ошибка сервера' }); } if (!user) { return res.status(404).json({ error: 'Пользователь не найден' }); } res.json(user); }); } catch (error) { console.error('Ошибка:', error); res.status(500).json({ error: 'Ошибка сервера' }); } }); // Обновление пользователя router.put('/admin/users/:id', requireAdmin, async (req, res) => { try { const { getDb } = require('./database'); const db = getDb(); const userId = req.params.id; const { login, name, email, role, auth_type, groups, description } = req.body; if (!login || !name || !email) { return res.status(400).json({ error: 'Заполните обязательные поля' }); } // Проверяем, что пользователь не пытается изменить свою роль на не-админа if (req.session.user.id == userId && role !== 'admin') { return res.status(400).json({ error: 'Нельзя снять права администратора у себя' }); } const query = ` UPDATE users SET login = ?, name = ?, email = ?, role = ?, auth_type = ?, groups = ?, description = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ? `; db.run(query, [login, name, email, role, auth_type, groups || '[]', description || '', userId], function(err) { if (err) { console.error('Ошибка обновления пользователя:', err); return res.status(500).json({ error: 'Ошибка сервера' }); } if (this.changes === 0) { return res.status(404).json({ error: 'Пользователь не найден' }); } res.json({ success: true, message: 'Пользователь обновлен' }); }); } catch (error) { console.error('Ошибка:', error); res.status(500).json({ error: 'Ошибка сервера' }); } }); // Удаление пользователя router.delete('/admin/users/:id', requireAdmin, async (req, res) => { try { const { getDb } = require('./database'); const db = getDb(); const userId = req.params.id; // Нельзя удалить самого себя if (req.session.user.id == userId) { return res.status(400).json({ error: 'Нельзя удалить самого себя' }); } const query = `DELETE FROM users WHERE id = ?`; db.run(query, [userId], function(err) { if (err) { console.error('Ошибка удаления пользователя:', err); return res.status(500).json({ error: 'Ошибка сервера' }); } if (this.changes === 0) { return res.status(404).json({ error: 'Пользователь не найден' }); } res.json({ success: true, message: 'Пользователь удален' }); }); } catch (error) { console.error('Ошибка:', error); res.status(500).json({ error: 'Ошибка сервера' }); } }); // Получение всех задач (для админа) router.get('/admin/tasks', requireAdmin, async (req, res) => { try { const { getDb } = require('./database'); const db = getDb(); const { search = '', status = 'all', creator = '', assignee = '', limit = 100, offset = 0 } = req.query; let query = ` SELECT DISTINCT t.*, u.name as creator_name, u.login as creator_login, ot.title as original_task_title, ou.name as original_creator_name, GROUP_CONCAT(DISTINCT ta.user_id) as assigned_user_ids, GROUP_CONCAT(DISTINCT u2.name) as assigned_user_names FROM tasks t LEFT JOIN users u ON t.created_by = u.id LEFT JOIN tasks ot ON t.original_task_id = ot.id LEFT JOIN users ou ON ot.created_by = ou.id LEFT JOIN task_assignments ta ON t.id = ta.task_id LEFT JOIN users u2 ON ta.user_id = u2.id WHERE 1=1 `; const params = []; if (status !== 'all') { if (status === 'deleted') { query += " AND t.status = 'deleted'"; } else if (status === 'closed') { query += " AND t.closed_at IS NOT NULL"; } else if (status === 'active') { query += " AND t.status = 'active' AND t.closed_at IS NULL"; } } if (creator) { query += ` AND t.created_by = ?`; params.push(creator); } if (assignee) { query += ` AND ta.user_id = ?`; params.push(assignee); } if (search) { query += ` AND (t.title LIKE ? OR t.description LIKE ?)`; const searchPattern = `%${search}%`; params.push(searchPattern, searchPattern); } query += " GROUP BY t.id ORDER BY t.created_at DESC LIMIT ? OFFSET ?"; params.push(parseInt(limit), parseInt(offset)); db.all(query, params, (err, tasks) => { if (err) { console.error('Ошибка получения задач:', err); return res.status(500).json({ error: 'Ошибка сервера' }); } res.json(tasks); }); } catch (error) { console.error('Ошибка:', error); res.status(500).json({ error: 'Ошибка сервера' }); } }); // Получение всех логов активности router.get('/admin/activity-logs', requireAdmin, async (req, res) => { try { const { getDb } = require('./database'); const db = getDb(); const { taskId = '', userId = '', action = '', startDate = '', endDate = '', limit = 100, offset = 0 } = req.query; let query = ` SELECT al.*, u.name as user_name, t.title as task_title FROM activity_logs al LEFT JOIN users u ON al.user_id = u.id LEFT JOIN tasks t ON al.task_id = t.id WHERE 1=1 `; const params = []; if (taskId) { query += ` AND al.task_id = ?`; params.push(taskId); } if (userId) { query += ` AND al.user_id = ?`; params.push(userId); } if (action) { query += ` AND al.action = ?`; params.push(action); } if (startDate) { query += ` AND al.created_at >= ?`; params.push(startDate); } if (endDate) { query += ` AND al.created_at <= ?`; params.push(endDate); } query += " ORDER BY al.created_at DESC LIMIT ? OFFSET ?"; params.push(parseInt(limit), parseInt(offset)); db.all(query, params, (err, logs) => { if (err) { console.error('Ошибка получения логов:', err); return res.status(500).json({ error: 'Ошибка сервера' }); } res.json(logs); }); } catch (error) { console.error('Ошибка:', error); res.status(500).json({ error: 'Ошибка сервера' }); } }); // Получение логов уведомлений router.get('/admin/notification-logs', requireAdmin, async (req, res) => { try { const postgresLogger = require('./postgres'); const { taskId, status, startDate, endDate, limit = 100, offset = 0 } = req.query; if (!postgresLogger.pool || !postgresLogger.initialized) { return res.json({ logs: [], total: 0, limit, offset }); } let query = ` SELECT * FROM sms_logs WHERE 1=1 ${taskId ? 'AND task_id = $1' : ''} ${status ? `AND status = $${taskId ? 2 : 1}` : ''} ${startDate ? `AND created_at >= $${taskId ? (status ? 3 : 2) : (status ? 2 : 1)}` : ''} ${endDate ? `AND created_at <= $${taskId ? (status ? (startDate ? 4 : 3) : (startDate ? 3 : 2)) : (status ? (startDate ? 3 : 2) : (startDate ? 2 : 1))}` : ''} ORDER BY created_at DESC LIMIT $${taskId ? (status ? (startDate ? (endDate ? 5 : 4) : (endDate ? 4 : 3)) : (startDate ? (endDate ? 4 : 3) : (endDate ? 3 : 2))) : (status ? (startDate ? (endDate ? 4 : 3) : (endDate ? 3 : 2)) : (startDate ? (endDate ? 3 : 2) : (endDate ? 2 : 1)))} OFFSET $${taskId ? (status ? (startDate ? (endDate ? 6 : 5) : (endDate ? 5 : 4)) : (startDate ? (endDate ? 5 : 4) : (endDate ? 4 : 3))) : (status ? (startDate ? (endDate ? 5 : 4) : (endDate ? 4 : 3)) : (startDate ? (endDate ? 4 : 3) : (endDate ? 3 : 2)))} `; const params = []; if (taskId) params.push(taskId); if (status) params.push(status); if (startDate) params.push(startDate); if (endDate) params.push(endDate); params.push(parseInt(limit), parseInt(offset)); const client = await postgresLogger.pool.connect(); try { const result = await client.query(query, params); const logs = result.rows; // Получаем общее количество const countQuery = ` SELECT COUNT(*) as total FROM sms_logs WHERE 1=1 ${taskId ? 'AND task_id = $1' : ''} ${status ? `AND status = $${taskId ? 2 : 1}` : ''} ${startDate ? `AND created_at >= $${taskId ? (status ? 3 : 2) : (status ? 2 : 1)}` : ''} ${endDate ? `AND created_at <= $${taskId ? (status ? (startDate ? 4 : 3) : (startDate ? 3 : 2)) : (status ? (startDate ? 3 : 2) : (startDate ? 2 : 1))}` : ''} `; const countResult = await client.query(countQuery, params.slice(0, -2)); const total = countResult.rows[0]?.total || 0; res.json({ logs, total: parseInt(total), limit: parseInt(limit), offset: parseInt(offset) }); } finally { client.release(); } } catch (error) { console.error('Ошибка получения логов уведомлений:', error); res.status(500).json({ error: 'Ошибка получения логов' }); } }); // Получение детальной статистики router.get('/admin/detailed-stats', requireAdmin, async (req, res) => { try { const { getDb } = require('./database'); const db = getDb(); const { period = 'week' } = req.query; let dateFilter = ''; let dateFormat = ''; switch (period) { case 'day': dateFilter = "created_at >= DATE('now', '-1 day')"; dateFormat = "strftime('%H:00', created_at)"; break; case 'week': dateFilter = "created_at >= DATE('now', '-7 days')"; dateFormat = "strftime('%Y-%m-%d', created_at)"; break; case 'month': dateFilter = "created_at >= DATE('now', '-30 days')"; dateFormat = "strftime('%Y-%m-%d', created_at)"; break; case 'year': dateFilter = "created_at >= DATE('now', '-365 days')"; dateFormat = "strftime('%Y-%m', created_at)"; break; default: dateFilter = "created_at >= DATE('now', '-7 days')"; dateFormat = "strftime('%Y-%m-%d', created_at)"; } // Статистика по задачам по дням const tasksByDay = await new Promise((resolve, reject) => { db.all(` SELECT ${dateFormat} as date, COUNT(*) as total, COUNT(CASE WHEN status = 'active' AND closed_at IS NULL THEN 1 END) as active, COUNT(CASE WHEN closed_at IS NOT NULL THEN 1 END) as closed, COUNT(CASE WHEN status = 'deleted' THEN 1 END) as deleted FROM tasks WHERE ${dateFilter} GROUP BY ${dateFormat} ORDER BY ${dateFormat} `, [], (err, rows) => { if (err) reject(err); else resolve(rows || []); }); }); // Статистика по назначениям по дням const assignmentsByDay = await new Promise((resolve, reject) => { db.all(` SELECT ${dateFormat} as date, COUNT(*) as total, COUNT(CASE WHEN status = 'assigned' THEN 1 END) as assigned, COUNT(CASE WHEN status = 'in_progress' THEN 1 END) as in_progress, COUNT(CASE WHEN status = 'completed' THEN 1 END) as completed, COUNT(CASE WHEN status = 'overdue' THEN 1 END) as overdue, COUNT(CASE WHEN status = 'rework' THEN 1 END) as rework FROM task_assignments WHERE ${dateFilter} GROUP BY ${dateFormat} ORDER BY ${dateFormat} `, [], (err, rows) => { if (err) reject(err); else resolve(rows || []); }); }); // Статистика по пользователям (активность) const userActivity = await new Promise((resolve, reject) => { db.all(` SELECT u.name as user_name, u.login as user_login, u.role, u.auth_type, COUNT(DISTINCT t.id) as tasks_created, COUNT(DISTINCT ta.id) as tasks_assigned, COUNT(DISTINCT CASE WHEN ta.status = 'completed' THEN ta.id END) as tasks_completed, MAX(u.last_login) as last_login FROM users u LEFT JOIN tasks t ON u.id = t.created_by LEFT JOIN task_assignments ta ON u.id = ta.user_id GROUP BY u.id ORDER BY tasks_created DESC `, [], (err, rows) => { if (err) reject(err); else resolve(rows || []); }); }); // Статистика по файлам const filesStats = await new Promise((resolve, reject) => { db.all(` SELECT ${dateFormat} as date, COUNT(*) as file_count, SUM(file_size) as total_size FROM task_files WHERE ${dateFilter} GROUP BY ${dateFormat} ORDER BY ${dateFormat} `, [], (err, rows) => { if (err) reject(err); else resolve(rows || []); }); }); res.json({ period, tasksByDay, assignmentsByDay, userActivity, filesStats, timestamp: new Date().toISOString() }); } catch (error) { console.error('Ошибка получения детальной статистики:', error); res.status(500).json({ error: 'Ошибка получения статистики' }); } }); // Создание нового пользователя (только для админов) router.post('/admin/users', requireAdmin, async (req, res) => { try { const { getDb } = require('./database'); const db = getDb(); const { login, password, name, email, role = 'teacher', auth_type = 'local' } = req.body; if (!login || !password || !name || !email) { return res.status(400).json({ error: 'Заполните все обязательные поля' }); } // Проверяем, существует ли пользователь с таким логином db.get("SELECT id FROM users WHERE login = ?", [login], async (err, existingUser) => { if (err) { console.error('Ошибка проверки пользователя:', err); return res.status(500).json({ error: 'Ошибка сервера' }); } if (existingUser) { return res.status(400).json({ error: 'Пользователь с таким логином уже существует' }); } // Проверяем, существует ли пользователь с таким email db.get("SELECT id FROM users WHERE email = ?", [email], async (err, existingEmail) => { if (err) { console.error('Ошибка проверки email:', err); return res.status(500).json({ error: 'Ошибка сервера' }); } if (existingEmail) { return res.status(400).json({ error: 'Пользователь с таким email уже существует' }); } // Хешируем пароль const bcrypt = require('bcryptjs'); const hashedPassword = await bcrypt.hash(password, 10); // Создаем пользователя const query = ` INSERT INTO users (login, password, name, email, role, auth_type, created_at) VALUES (?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP) `; db.run(query, [login, hashedPassword, name, email, role, auth_type], function(err) { if (err) { console.error('Ошибка создания пользователя:', err); return res.status(500).json({ error: 'Ошибка сервера' }); } res.json({ success: true, message: 'Пользователь создан', userId: this.lastID }); }); }); }); } catch (error) { console.error('Ошибка:', error); res.status(500).json({ error: 'Ошибка сервера' }); } }); // Сброс пароля пользователя router.post('/admin/users/:id/reset-password', requireAdmin, async (req, res) => { try { const { getDb } = require('./database'); const db = getDb(); const userId = req.params.id; const { newPassword } = req.body; if (!newPassword || newPassword.length < 6) { return res.status(400).json({ error: 'Пароль должен содержать минимум 6 символов' }); } // Хешируем пароль const bcrypt = require('bcryptjs'); const hashedPassword = await bcrypt.hash(newPassword, 10); const query = `UPDATE users SET password = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?`; db.run(query, [hashedPassword, userId], function(err) { if (err) { console.error('Ошибка сброса пароля:', err); return res.status(500).json({ error: 'Ошибка сервера' }); } if (this.changes === 0) { return res.status(404).json({ error: 'Пользователь не найден' }); } res.json({ success: true, message: 'Пароль сброшен' }); }); } catch (error) { console.error('Ошибка:', error); res.status(500).json({ error: 'Ошибка сервера' }); } }); // API для админ Канбан-доски router.get('/admin/kanban-tasks', requireAdmin, async (req, res) => { try { const { getDb } = require('./database'); const db = getDb(); const days = parseInt(req.query.days) || 7; const now = new Date(); const futureDate = new Date(now.getTime() + days * 24 * 60 * 60 * 1000); const futureISO = futureDate.toISOString(); const query = ` SELECT DISTINCT t.*, u.name as creator_name, u.login as creator_login, GROUP_CONCAT(DISTINCT ta.user_id) as assigned_user_ids, GROUP_CONCAT(DISTINCT u2.name) as assigned_user_names FROM tasks t LEFT JOIN users u ON t.created_by = u.id LEFT JOIN task_assignments ta ON t.id = ta.task_id LEFT JOIN users u2 ON ta.user_id = u2.id WHERE t.status = 'active' AND t.closed_at IS NULL AND (t.due_date IS NULL OR t.due_date <= ?) GROUP BY t.id ORDER BY t.due_date ASC, t.created_at DESC `; db.all(query, [futureISO], async (err, tasks) => { if (err) { console.error('Ошибка получения задач для Канбана:', err); return res.status(500).json({ error: 'Ошибка сервера' }); } // Добавляем статус для Канбана const tasksWithKanban = await Promise.all(tasks.map(async (task) => { // Получаем назначения для задачи const assignments = await new Promise((resolve) => { db.all(` SELECT ta.*, u.name as user_name, u.login as user_login FROM task_assignments ta LEFT JOIN users u ON ta.user_id = u.id WHERE ta.task_id = ? `, [task.id], (err, rows) => { resolve(rows || []); }); }); // Определяем статус для Канбана let kanbanStatus = 'unassigned'; if (assignments.length === 0) { kanbanStatus = 'unassigned'; } else { const hasAssigned = assignments.some(a => a.status === 'assigned'); const hasInProgress = assignments.some(a => a.status === 'in_progress'); const hasOverdue = assignments.some(a => a.status === 'overdue'); const hasRework = assignments.some(a => a.status === 'rework'); const allCompleted = assignments.every(a => a.status === 'completed'); if (allCompleted) { kanbanStatus = 'completed'; } else if (hasRework) { kanbanStatus = 'rework'; } else if (hasOverdue) { kanbanStatus = 'overdue'; } else if (hasInProgress) { kanbanStatus = 'in_progress'; } else if (hasAssigned) { kanbanStatus = 'assigned'; } } return { ...task, kanbanStatus, assignments }; })); res.json({ tasks: tasksWithKanban }); }); } catch (error) { console.error('Ошибка:', error); res.status(500).json({ error: 'Ошибка сервера' }); } }); // Экспорт данных router.get('/admin/export', requireAdmin, async (req, res) => { try { const { getDb } = require('./database'); const db = getDb(); const { format = 'json', dataType = 'all' } = req.query; const exportData = {}; if (dataType === 'all' || dataType === 'users') { const users = await new Promise((resolve, reject) => { db.all(` SELECT id, login, name, email, role, auth_type, groups, description, created_at, last_login, updated_at FROM users ORDER BY id `, [], (err, rows) => { if (err) reject(err); else resolve(rows || []); }); }); exportData.users = users; } if (dataType === 'all' || dataType === 'tasks') { const tasks = await new Promise((resolve, reject) => { db.all(` SELECT t.*, u.name as creator_name, u.login as creator_login FROM tasks t LEFT JOIN users u ON t.created_by = u.id ORDER BY t.id `, [], (err, rows) => { if (err) reject(err); else resolve(rows || []); }); }); exportData.tasks = tasks; } if (dataType === 'all' || dataType === 'assignments') { const assignments = await new Promise((resolve, reject) => { db.all(` SELECT ta.*, u.name as user_name, u.login as user_login, t.title as task_title FROM task_assignments ta LEFT JOIN users u ON ta.user_id = u.id LEFT JOIN tasks t ON ta.task_id = t.id ORDER BY ta.id `, [], (err, rows) => { if (err) reject(err); else resolve(rows || []); }); }); exportData.assignments = assignments; } if (dataType === 'all' || dataType === 'logs') { const logs = await new Promise((resolve, reject) => { db.all(` SELECT al.*, u.name as user_name, t.title as task_title FROM activity_logs al LEFT JOIN users u ON al.user_id = u.id LEFT JOIN tasks t ON al.task_id = t.id ORDER BY al.id `, [], (err, rows) => { if (err) reject(err); else resolve(rows || []); }); }); exportData.activityLogs = logs; } exportData.exportedAt = new Date().toISOString(); exportData.system = 'MiniCRM'; exportData.version = '1.0.0'; if (format === 'csv') { // Простая реализация CSV экспорта (можно расширить) const csvData = Object.entries(exportData) .map(([key, value]) => { if (Array.isArray(value)) { if (value.length === 0) return `${key}: Нет данных\n`; const headers = Object.keys(value[0]).join(','); const rows = value.map(item => Object.values(item).map(val => typeof val === 'string' ? `"${val.replace(/"/g, '""')}"` : val ).join(',') ).join('\n'); return `${key}:\n${headers}\n${rows}\n\n`; } return `${key}: ${JSON.stringify(value)}\n`; }) .join(''); res.setHeader('Content-Type', 'text/csv'); res.setHeader('Content-Disposition', `attachment; filename="minicrm_export_${new Date().toISOString().split('T')[0]}.csv"`); res.send(csvData); } else { res.json(exportData); } } catch (error) { console.error('Ошибка экспорта данных:', error); res.status(500).json({ error: 'Ошибка экспорта данных' }); } }); // Получение профилей пользователей с настройками уведомлений router.get('/admin/user-profiles', requireAdmin, async (req, res) => { try { const { getDb } = require('./database'); const db = getDb(); const query = ` SELECT u.id, u.login, u.name, u.email as user_email, u.role, u.auth_type, u.groups, u.created_at, u.last_login, us.email_notifications, us.notification_email, us.telegram_notifications, us.telegram_chat_id, us.vk_notifications, us.vk_user_id, us.updated_at as settings_updated_at FROM users u LEFT JOIN user_settings us ON u.id = us.user_id ORDER BY u.name `; db.all(query, [], (err, profiles) => { if (err) { console.error('Ошибка получения профилей пользователей:', err); return res.status(500).json({ error: 'Ошибка сервера' }); } res.json(profiles); }); } catch (error) { console.error('Ошибка:', error); res.status(500).json({ error: 'Ошибка сервера' }); } }); // Получение конкретного профиля пользователя router.get('/admin/user-profiles/:id', requireAdmin, async (req, res) => { try { const { getDb } = require('./database'); const db = getDb(); const userId = req.params.id; const query = ` SELECT u.id, u.login, u.name, u.email as user_email, u.role, u.auth_type, u.groups, u.created_at, u.last_login, us.email_notifications, us.notification_email, us.telegram_notifications, us.telegram_chat_id, us.vk_notifications, us.vk_user_id, us.created_at as settings_created_at, us.updated_at as settings_updated_at FROM users u LEFT JOIN user_settings us ON u.id = us.user_id WHERE u.id = ? `; db.get(query, [userId], (err, profile) => { if (err) { console.error('Ошибка получения профиля пользователя:', err); return res.status(500).json({ error: 'Ошибка сервера' }); } if (!profile) { return res.status(404).json({ error: 'Пользователь не найден' }); } res.json(profile); }); } catch (error) { console.error('Ошибка:', error); res.status(500).json({ error: 'Ошибка сервера' }); } }); // Обновление настроек уведомлений пользователя (для админа) router.put('/admin/user-profiles/:id/notification-settings', requireAdmin, async (req, res) => { try { const { getDb } = require('./database'); const db = getDb(); const userId = req.params.id; const { email_notifications, notification_email, telegram_notifications, telegram_chat_id, vk_notifications, vk_user_id } = req.body; // Валидация email if (email_notifications && notification_email) { const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; if (!emailRegex.test(notification_email)) { return res.status(400).json({ error: 'Неверный формат email' }); } } // Проверяем существование записи db.get("SELECT id FROM user_settings WHERE user_id = ?", [userId], (err, existing) => { if (err) { console.error('Ошибка проверки настроек:', err); return res.status(500).json({ error: 'Ошибка сервера' }); } if (existing) { // Обновляем существующие настройки db.run( `UPDATE user_settings SET email_notifications = ?, notification_email = ?, telegram_notifications = ?, telegram_chat_id = ?, vk_notifications = ?, vk_user_id = ?, updated_at = CURRENT_TIMESTAMP WHERE user_id = ?`, [ email_notifications ? 1 : 0, notification_email || '', telegram_notifications ? 1 : 0, telegram_chat_id || '', vk_notifications ? 1 : 0, vk_user_id || '', userId ], function(updateErr) { if (updateErr) { console.error('Ошибка обновления настроек:', updateErr); return res.status(500).json({ error: 'Ошибка сервера' }); } // Логируем действие const { logActivity } = require('./database'); if (logActivity) { logActivity(0, req.session.user.id, 'USER_SETTINGS_UPDATED', `Админ обновил настройки уведомлений пользователя ${userId}`); } console.log(`✅ Админ обновил настройки пользователя ${userId}`); res.json({ success: true, message: 'Настройки уведомлений обновлены' }); } ); } else { // Создаем новые настройки db.run( `INSERT INTO user_settings (user_id, email_notifications, notification_email, telegram_notifications, telegram_chat_id, vk_notifications, vk_user_id) VALUES (?, ?, ?, ?, ?, ?, ?)`, [ userId, email_notifications ? 1 : 0, notification_email || '', telegram_notifications ? 1 : 0, telegram_chat_id || '', vk_notifications ? 1 : 0, vk_user_id || '' ], function(insertErr) { if (insertErr) { console.error('Ошибка создания настроек:', insertErr); return res.status(500).json({ error: 'Ошибка сервера' }); } // Логируем действие const { logActivity } = require('./database'); if (logActivity) { logActivity(null, req.session.user.id, 'USER_SETTINGS_CREATED', `Админ создал настройки уведомлений пользователя ${userId}`); } console.log(`✅ Админ создал настройки пользователя ${userId}`); res.json({ success: true, message: 'Настройки уведомлений созданы' }); } ); } }); } catch (error) { console.error('Ошибка:', error); res.status(500).json({ error: 'Ошибка сервера' }); } }); // Массовое обновление email уведомлений для нескольких пользователей router.post('/admin/bulk-email-settings', requireAdmin, async (req, res) => { try { const { getDb } = require('./database'); const db = getDb(); const { users, email_notifications, notification_email } = req.body; if (!Array.isArray(users) || users.length === 0) { return res.status(400).json({ error: 'Выберите пользователей' }); } if (email_notifications && !notification_email) { return res.status(400).json({ error: 'Укажите email для уведомлений' }); } // Валидация email if (email_notifications && notification_email) { const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; if (!emailRegex.test(notification_email)) { return res.status(400).json({ error: 'Неверный формат email' }); } } const results = { success: 0, failed: 0, errors: [] }; // Обновляем каждого пользователя в транзакции db.serialize(() => { db.run("BEGIN TRANSACTION"); users.forEach(userId => { // Проверяем существование записи db.get("SELECT id FROM user_settings WHERE user_id = ?", [userId], (err, existing) => { if (err) { results.failed++; results.errors.push({ userId, error: err.message }); return; } if (existing) { // Обновляем существующие настройки db.run( `UPDATE user_settings SET email_notifications = ?, notification_email = ?, updated_at = CURRENT_TIMESTAMP WHERE user_id = ?`, [ email_notifications ? 1 : 0, email_notifications ? notification_email : '', userId ], function(updateErr) { if (updateErr) { results.failed++; results.errors.push({ userId, error: updateErr.message }); } else { results.success++; } } ); } else { // Создаем новые настройки с значениями по умолчанию db.run( `INSERT INTO user_settings (user_id, email_notifications, notification_email, telegram_notifications, telegram_chat_id, vk_notifications, vk_user_id) VALUES (?, ?, ?, 0, '', 0, '')`, [ userId, email_notifications ? 1 : 0, email_notifications ? notification_email : '' ], function(insertErr) { if (insertErr) { results.failed++; results.errors.push({ userId, error: insertErr.message }); } else { results.success++; } } ); } }); }); setTimeout(() => { db.run("COMMIT", (commitErr) => { if (commitErr) { console.error('Ошибка коммита транзакции:', commitErr); return res.status(500).json({ error: 'Ошибка транзакции' }); } // Логируем действие const { logActivity } = require('./database'); if (logActivity) { logActivity(null, req.session.user.id, 'BULK_SETTINGS_UPDATED', `Админ массово обновил настройки для ${users.length} пользователей`); } console.log(`✅ Массовое обновление: успешно ${results.success}, ошибок ${results.failed}`); res.json({ success: true, message: 'Настройки обновлены', results }); }); }, 1000); }); } catch (error) { console.error('Ошибка:', error); res.status(500).json({ error: 'Ошибка сервера' }); } }); // Отправка тестового уведомления пользователю router.post('/admin/user-profiles/:id/test-notification', requireAdmin, async (req, res) => { try { const { getDb } = require('./database'); const db = getDb(); const userId = req.params.id; const notificationType = req.body.notification_type || 'test'; // Получаем информацию о пользователе db.get(` SELECT u.id, u.name, u.email, us.email_notifications, us.notification_email FROM users u LEFT JOIN user_settings us ON u.id = us.user_id WHERE u.id = ? `, [userId], async (err, user) => { if (err) { console.error('Ошибка получения пользователя:', err); return res.status(500).json({ error: 'Ошибка сервера' }); } if (!user) { return res.status(404).json({ error: 'Пользователь не найден' }); } // Проверяем, включены ли email уведомления if (!user.email_notifications) { return res.status(400).json({ error: 'Email уведомления отключены у пользователя' }); } const emailTo = user.notification_email || user.email; if (!emailTo) { return res.status(400).json({ error: 'У пользователя не указан email' }); } // Отправляем тестовое уведомление const emailNotifications = require('./email-notifications'); const emailSent = await emailNotifications.sendEmailNotification( emailTo, 'Тестовое уведомление от School CRM', `

✅ Тестовое уведомление

Здравствуйте, ${user.name}!

Это тестовое уведомление от системы School CRM.

Если вы получили это письмо, значит настройки уведомлений работают корректно.


Информация о тесте:

` ); if (emailSent) { // Логируем действие const { logActivity } = require('./database'); if (logActivity) { logActivity(null, req.session.user.id, 'TEST_NOTIFICATION_SENT', `Админ отправил тестовое уведомление пользователю ${user.name} (${emailTo})`); } res.json({ success: true, message: 'Тестовое уведомление отправлено', email: emailTo }); } else { res.status(500).json({ error: 'Не удалось отправить уведомление' }); } }); } catch (error) { console.error('Ошибка:', error); res.status(500).json({ error: 'Ошибка сервера' }); } }); module.exports = router;