Files
minicrm/admin-server.js
2026-01-27 01:02:59 +05:00

1279 lines
53 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.
// 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(0, 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(0, 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',
`
<!DOCTYPE html>
<html>
<head>
<style>
body { font-family: Arial, sans-serif; line-height: 1.6; }
.container { max-width: 600px; margin: 0 auto; padding: 20px; }
.header { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 20px; border-radius: 10px 10px 0 0; }
.content { padding: 20px; border: 1px solid #ddd; border-top: none; }
.button { display: inline-block; background: #667eea; color: white; padding: 10px 20px; text-decoration: none; border-radius: 5px; }
.footer { margin-top: 20px; font-size: 12px; color: #666; }
</style>
</head>
<body>
<div class="container">
<div class="header">
<h2>✅ Тестовое уведомление</h2>
</div>
<div class="content">
<p>Здравствуйте, ${user.name}!</p>
<p>Это тестовое уведомление от системы School CRM.</p>
<p>Если вы получили это письмо, значит настройки уведомлений работают корректно.</p>
<br>
<p><strong>Информация о тесте:</strong></p>
<ul>
<li>Получатель: ${emailTo}</li>
<li>Отправитель: Администратор</li>
<li>Время: ${new Date().toLocaleString('ru-RU')}</li>
</ul>
</div>
<div class="footer">
<p>Это тестовое сообщение от School CRM системы.</p>
<p>Вы можете изменить настройки уведомлений в личном кабинете.</p>
</div>
</div>
</body>
</html>
`
);
if (emailSent) {
// Логируем действие
const { logActivity } = require('./database');
if (logActivity) {
logActivity(0, 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;