diff --git a/admin-doc.js b/admin-doc.js deleted file mode 100644 index 336b46b..0000000 --- a/admin-doc.js +++ /dev/null @@ -1,563 +0,0 @@ -// admin-doc.js -const express = require('express'); -const router = express.Router(); -const multer = require('multer'); -const path = require('path'); -const fs = require('fs'); - -// Middleware для проверки прав администратора -const requireAdmin = (req, res, next) => { - if (!req.session.user || req.session.user.role !== 'admin') { - return res.status(403).json({ error: 'Недостаточно прав' }); - } - next(); -}; - -// Настройка загрузки файлов для шаблонов документов -const storage = multer.diskStorage({ - destination: (req, file, cb) => { - const uploadDir = path.join(__dirname, 'data', 'uploads', 'templates'); - if (!fs.existsSync(uploadDir)) { - fs.mkdirSync(uploadDir, { recursive: true }); - } - cb(null, uploadDir); - }, - filename: (req, file, cb) => { - const uniqueName = `${Date.now()}-${Math.round(Math.random() * 1E9)}${path.extname(file.originalname)}`; - cb(null, uniqueName); - } -}); - -const upload = multer({ - storage: storage, - limits: { fileSize: 10 * 1024 * 1024 }, // 10MB - fileFilter: (req, file, cb) => { - const allowedTypes = ['.doc', '.docx', '.pdf', '.txt', '.rtf']; - const ext = path.extname(file.originalname).toLowerCase(); - if (allowedTypes.includes(ext)) { - cb(null, true); - } else { - cb(new Error('Недопустимый тип файла. Разрешены: .doc, .docx, .pdf, .txt, .rtf')); - } - } -}); - -// Получение всех типов документов -router.get('/admin/doc/types', requireAdmin, async (req, res) => { - try { - const { getDb } = require('./database'); - const db = getDb(); - - const query = ` - SELECT dt.*, - COUNT(DISTINCT a.id) as approval_stages_count, - COUNT(DISTINCT d.id) as documents_count - FROM document_types dt - LEFT JOIN approval_stages a ON dt.id = a.document_type_id - LEFT JOIN documents d ON dt.id = d.document_type_id - GROUP BY dt.id - ORDER BY dt.name - `; - - db.all(query, [], (err, types) => { - if (err) { - console.error('Ошибка получения типов документов:', err); - return res.status(500).json({ error: 'Ошибка сервера' }); - } - res.json(types); - }); - } catch (error) { - console.error('Ошибка:', error); - res.status(500).json({ error: 'Ошибка сервера' }); - } -}); - -// Получение конкретного типа документа -router.get('/admin/doc/types/:id', requireAdmin, async (req, res) => { - try { - const { getDb } = require('./database'); - const db = getDb(); - const typeId = req.params.id; - - const query = ` - SELECT dt.* - FROM document_types dt - WHERE dt.id = ? - `; - - db.get(query, [typeId], (err, docType) => { - if (err) { - console.error('Ошибка получения типа документа:', err); - return res.status(500).json({ error: 'Ошибка сервера' }); - } - - if (!docType) { - return res.status(404).json({ error: 'Тип документа не найден' }); - } - - res.json(docType); - }); - } catch (error) { - console.error('Ошибка:', error); - res.status(500).json({ error: 'Ошибка сервера' }); - } -}); - -// Создание нового типа документа -router.post('/admin/doc/types', requireAdmin, upload.single('template'), async (req, res) => { - try { - const { getDb } = require('./database'); - const db = getDb(); - const { name, description } = req.body; - - if (!name) { - return res.status(400).json({ error: 'Название типа документа обязательно' }); - } - - // Проверяем, существует ли тип с таким названием - db.get("SELECT id FROM document_types WHERE name = ?", [name], (err, existing) => { - if (err) { - console.error('Ошибка проверки типа документа:', err); - return res.status(500).json({ error: 'Ошибка сервера' }); - } - - if (existing) { - return res.status(400).json({ error: 'Тип документа с таким названием уже существует' }); - } - - const templatePath = req.file ? req.file.path : null; - - const query = ` - INSERT INTO document_types (name, description, template_path) - VALUES (?, ?, ?) - `; - - db.run(query, [name, description || '', templatePath], function(err) { - if (err) { - console.error('Ошибка создания типа документа:', err); - return res.status(500).json({ error: 'Ошибка сервера' }); - } - - res.json({ - success: true, - message: 'Тип документа создан', - typeId: this.lastID - }); - }); - }); - } catch (error) { - console.error('Ошибка:', error); - res.status(500).json({ error: 'Ошибка сервера' }); - } -}); - -// Обновление типа документа -router.put('/admin/doc/types/:id', requireAdmin, upload.single('template'), async (req, res) => { - try { - const { getDb } = require('./database'); - const db = getDb(); - const typeId = req.params.id; - const { name, description } = req.body; - - if (!name) { - return res.status(400).json({ error: 'Название типа документа обязательно' }); - } - - // Проверяем, существует ли другой тип с таким названием - db.get("SELECT id FROM document_types WHERE name = ? AND id != ?", [name, typeId], (err, existing) => { - if (err) { - console.error('Ошибка проверки типа документа:', err); - return res.status(500).json({ error: 'Ошибка сервера' }); - } - - if (existing) { - return res.status(400).json({ error: 'Тип документа с таким названием уже существует' }); - } - - // Получаем текущий тип для удаления старого файла - db.get("SELECT template_path FROM document_types WHERE id = ?", [typeId], (err, currentType) => { - if (err) { - console.error('Ошибка получения типа документа:', err); - return res.status(500).json({ error: 'Ошибка сервера' }); - } - - let templatePath = currentType?.template_path; - - // Если загружен новый файл - if (req.file) { - // Удаляем старый файл, если он существует - if (currentType?.template_path && fs.existsSync(currentType.template_path)) { - fs.unlinkSync(currentType.template_path); - } - templatePath = req.file.path; - } - - const query = ` - UPDATE document_types - SET name = ?, description = ?, template_path = ?, updated_at = CURRENT_TIMESTAMP - WHERE id = ? - `; - - db.run(query, [name, description || '', templatePath, typeId], 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/doc/types/:id', requireAdmin, async (req, res) => { - try { - const { getDb } = require('./database'); - const db = getDb(); - const typeId = req.params.id; - - // Проверяем, есть ли документы этого типа - db.get("SELECT COUNT(*) as count FROM documents WHERE document_type_id = ?", [typeId], (err, result) => { - if (err) { - console.error('Ошибка проверки документов:', err); - return res.status(500).json({ error: 'Ошибка сервера' }); - } - - if (result.count > 0) { - return res.status(400).json({ - error: 'Нельзя удалить тип документа, так как есть документы этого типа' - }); - } - - // Получаем путь к файлу шаблона - db.get("SELECT template_path FROM document_types WHERE id = ?", [typeId], (err, docType) => { - if (err) { - console.error('Ошибка получения типа документа:', err); - return res.status(500).json({ error: 'Ошибка сервера' }); - } - - // Удаляем файл шаблона, если он существует - if (docType?.template_path && fs.existsSync(docType.template_path)) { - fs.unlinkSync(docType.template_path); - } - - // Сначала удаляем этапы согласования - db.run("DELETE FROM approval_stages WHERE document_type_id = ?", [typeId], (err) => { - if (err) { - console.error('Ошибка удаления этапов согласования:', err); - return res.status(500).json({ error: 'Ошибка сервера' }); - } - - // Затем удаляем тип документа - db.run("DELETE FROM document_types WHERE id = ?", [typeId], 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/doc/types/:typeId/stages', requireAdmin, async (req, res) => { - try { - const { getDb } = require('./database'); - const db = getDb(); - const typeId = req.params.typeId; - - const query = ` - SELECT a.*, u.name as approver_name - FROM approval_stages a - LEFT JOIN users u ON a.approver_user_id = u.id - WHERE a.document_type_id = ? - ORDER BY a.stage_number - `; - - db.all(query, [typeId], (err, stages) => { - if (err) { - console.error('Ошибка получения этапов согласования:', err); - return res.status(500).json({ error: 'Ошибка сервера' }); - } - - res.json(stages || []); - }); - } catch (error) { - console.error('Ошибка:', error); - res.status(500).json({ error: 'Ошибка сервера' }); - } -}); - -// Добавление этапа согласования -router.post('/admin/doc/types/:typeId/stages', requireAdmin, async (req, res) => { - try { - const { getDb } = require('./database'); - const db = getDb(); - const typeId = req.params.typeId; - const { - stage_number, - stage_name, - approver_role, - approver_user_id, - is_required = true, - can_edit = false, - can_comment = true, - deadline_days - } = req.body; - - if (!stage_number || !stage_name) { - return res.status(400).json({ error: 'Номер и название этапа обязательны' }); - } - - if (!approver_role && !approver_user_id) { - return res.status(400).json({ error: 'Укажите роль или конкретного пользователя для согласования' }); - } - - // Проверяем, существует ли этап с таким номером - db.get("SELECT id FROM approval_stages WHERE document_type_id = ? AND stage_number = ?", - [typeId, stage_number], (err, existing) => { - if (err) { - console.error('Ошибка проверки этапа:', err); - return res.status(500).json({ error: 'Ошибка сервера' }); - } - - if (existing) { - return res.status(400).json({ error: 'Этап с таким номером уже существует' }); - } - - const query = ` - INSERT INTO approval_stages - (document_type_id, stage_number, stage_name, approver_role, - approver_user_id, is_required, can_edit, can_comment, deadline_days) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) - `; - - db.run(query, [ - typeId, stage_number, stage_name, approver_role || null, - approver_user_id || null, is_required ? 1 : 0, - can_edit ? 1 : 0, can_comment ? 1 : 0, deadline_days - ], function(err) { - if (err) { - console.error('Ошибка создания этапа согласования:', err); - return res.status(500).json({ error: 'Ошибка сервера' }); - } - - res.json({ - success: true, - message: 'Этап согласования добавлен', - stageId: this.lastID - }); - }); - }); - } catch (error) { - console.error('Ошибка:', error); - res.status(500).json({ error: 'Ошибка сервера' }); - } -}); - -// Обновление этапа согласования -router.put('/admin/doc/types/:typeId/stages/:stageId', requireAdmin, async (req, res) => { - try { - const { getDb } = require('./database'); - const db = getDb(); - const typeId = req.params.typeId; - const stageId = req.params.stageId; - const { - stage_number, - stage_name, - approver_role, - approver_user_id, - is_required, - can_edit, - can_comment, - deadline_days - } = req.body; - - if (!stage_number || !stage_name) { - return res.status(400).json({ error: 'Номер и название этапа обязательны' }); - } - - // Проверяем, существует ли другой этап с таким номером - db.get("SELECT id FROM approval_stages WHERE document_type_id = ? AND stage_number = ? AND id != ?", - [typeId, stage_number, stageId], (err, existing) => { - if (err) { - console.error('Ошибка проверки этапа:', err); - return res.status(500).json({ error: 'Ошибка сервера' }); - } - - if (existing) { - return res.status(400).json({ error: 'Этап с таким номером уже существует' }); - } - - const query = ` - UPDATE approval_stages - SET stage_number = ?, stage_name = ?, approver_role = ?, - approver_user_id = ?, is_required = ?, can_edit = ?, - can_comment = ?, deadline_days = ?, updated_at = CURRENT_TIMESTAMP - WHERE id = ? AND document_type_id = ? - `; - - db.run(query, [ - stage_number, stage_name, approver_role || null, - approver_user_id || null, is_required ? 1 : 0, - can_edit ? 1 : 0, can_comment ? 1 : 0, deadline_days, - stageId, typeId - ], 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/doc/types/:typeId/stages/:stageId', requireAdmin, async (req, res) => { - try { - const { getDb } = require('./database'); - const db = getDb(); - const typeId = req.params.typeId; - const stageId = req.params.stageId; - - db.run("DELETE FROM approval_stages WHERE id = ? AND document_type_id = ?", - [stageId, typeId], 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/doc/approvers', requireAdmin, async (req, res) => { - try { - const { getDb } = require('./database'); - const db = getDb(); - - const query = ` - SELECT id, name, login, email, role - FROM users - WHERE role IN ('admin', 'teacher') - 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/doc/stats', requireAdmin, async (req, res) => { - try { - const { getDb } = require('./database'); - const db = getDb(); - - const stats = await new Promise((resolve, reject) => { - db.get(` - SELECT - COUNT(*) as total_documents, - COUNT(CASE WHEN status = 'draft' THEN 1 END) as draft_documents, - COUNT(CASE WHEN status = 'in_review' THEN 1 END) as in_review_documents, - COUNT(CASE WHEN status = 'approved' THEN 1 END) as approved_documents, - COUNT(CASE WHEN status = 'rejected' THEN 1 END) as rejected_documents, - COUNT(DISTINCT created_by) as unique_authors, - COUNT(DISTINCT document_type_id) as types_used - FROM documents - `, [], (err, row) => { - if (err) reject(err); - else resolve(row || {}); - }); - }); - - const recentDocuments = await new Promise((resolve, reject) => { - db.all(` - SELECT d.id, d.title, d.status, d.created_at, - u.name as author_name, dt.name as type_name - FROM documents d - LEFT JOIN users u ON d.created_by = u.id - LEFT JOIN document_types dt ON d.document_type_id = dt.id - ORDER BY d.created_at DESC - LIMIT 10 - `, [], (err, rows) => { - if (err) reject(err); - else resolve(rows || []); - }); - }); - - const typeStats = await new Promise((resolve, reject) => { - db.all(` - SELECT dt.name, - COUNT(d.id) as document_count, - COUNT(CASE WHEN d.status = 'approved' THEN 1 END) as approved_count, - COUNT(CASE WHEN d.status = 'rejected' THEN 1 END) as rejected_count - FROM document_types dt - LEFT JOIN documents d ON dt.id = d.document_type_id - GROUP BY dt.id - ORDER BY document_count DESC - `, [], (err, rows) => { - if (err) reject(err); - else resolve(rows || []); - }); - }); - - res.json({ - stats, - recentDocuments, - typeStats, - timestamp: new Date().toISOString() - }); - } catch (error) { - console.error('Ошибка получения статистики:', error); - res.status(500).json({ error: 'Ошибка получения статистики' }); - } -}); - -module.exports = router; \ No newline at end of file diff --git a/admin-server.js b/admin-server.js deleted file mode 100644 index a4eee1a..0000000 --- a/admin-server.js +++ /dev/null @@ -1,1279 +0,0 @@ -// 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; \ No newline at end of file diff --git a/doc-endpoints.js b/doc-endpoints.js deleted file mode 100644 index 1832c31..0000000 --- a/doc-endpoints.js +++ /dev/null @@ -1,953 +0,0 @@ -// doc-endpoints.js -const express = require('express'); -const router = express.Router(); -const multer = require('multer'); -const path = require('path'); -const fs = require('fs'); - -// Middleware для проверки авторизации -const requireAuth = (req, res, next) => { - if (!req.session.user) { - return res.status(401).json({ error: 'Требуется аутентификация' }); - } - next(); -}; - -// Настройка загрузки файлов для документов -const storage = multer.diskStorage({ - destination: (req, file, cb) => { - const uploadDir = path.join(__dirname, 'data', 'uploads', 'documents'); - if (!fs.existsSync(uploadDir)) { - fs.mkdirSync(uploadDir, { recursive: true }); - } - cb(null, uploadDir); - }, - filename: (req, file, cb) => { - const uniqueName = `${Date.now()}-${Math.round(Math.random() * 1E9)}${path.extname(file.originalname)}`; - cb(null, uniqueName); - } -}); - -const upload = multer({ - storage: storage, - limits: { fileSize: 50 * 1024 * 1024 }, // 50MB - fileFilter: (req, file, cb) => { - const allowedTypes = ['.doc', '.docx', '.pdf', '.txt', '.rtf', '.xls', '.xlsx', '.ppt', '.pptx', '.jpg', '.jpeg', '.png']; - const ext = path.extname(file.originalname).toLowerCase(); - if (allowedTypes.includes(ext)) { - cb(null, true); - } else { - cb(new Error('Недопустимый тип файла')); - } - } -}); - -// Вспомогательная функция для логирования действий с документами -function logDocumentActivity(db, documentId, userId, action, changes = '') { - db.run( - "INSERT INTO document_history (document_id, user_id, action, changes) VALUES (?, ?, ?, ?)", - [documentId, userId, action, changes] - ); -} - -// Получение доступных типов документов для создания -router.get('/api/doc/types', requireAuth, async (req, res) => { - try { - const { getDb } = require('./database'); - const db = getDb(); - - const query = ` - SELECT dt.*, - COUNT(DISTINCT a.id) as stages_count - FROM document_types dt - LEFT JOIN approval_stages a ON dt.id = a.document_type_id - GROUP BY dt.id - ORDER BY dt.name - `; - - db.all(query, [], (err, types) => { - if (err) { - console.error('Ошибка получения типов документов:', err); - return res.status(500).json({ error: 'Ошибка сервера' }); - } - res.json(types); - }); - } catch (error) { - console.error('Ошибка:', error); - res.status(500).json({ error: 'Ошибка сервера' }); - } -}); - -// Создание нового документа -router.post('/api/documents', requireAuth, upload.single('file'), async (req, res) => { - try { - const { getDb } = require('./database'); - const db = getDb(); - const { title, description, document_type_id } = req.body; - const userId = req.session.user.id; - - if (!title || !document_type_id) { - return res.status(400).json({ error: 'Название и тип документа обязательны' }); - } - - if (!req.file) { - return res.status(400).json({ error: 'Файл документа обязателен' }); - } - - // Проверяем существование типа документа - db.get("SELECT id FROM document_types WHERE id = ?", [document_type_id], (err, docType) => { - if (err || !docType) { - return res.status(400).json({ error: 'Тип документа не найден' }); - } - - db.serialize(() => { - // Создаем документ - db.run( - `INSERT INTO documents - (title, description, document_type_id, status, created_by, - file_path, file_name, file_size) - VALUES (?, ?, ?, 'draft', ?, ?, ?, ?)`, - [ - title, - description || '', - document_type_id, - userId, - req.file.path, - req.file.originalname, - req.file.size - ], - function(err) { - if (err) { - console.error('Ошибка создания документа:', err); - return res.status(500).json({ error: 'Ошибка сервера' }); - } - - const documentId = this.lastID; - - // Логируем создание - logDocumentActivity(db, documentId, userId, 'DOCUMENT_CREATED', `Создан документ: ${title}`); - - // Получаем этапы согласования для этого типа документа - db.all( - "SELECT * FROM approval_stages WHERE document_type_id = ? ORDER BY stage_number", - [document_type_id], - (err, stages) => { - if (err) { - console.error('Ошибка получения этапов согласования:', err); - return res.status(500).json({ error: 'Ошибка сервера' }); - } - - // Создаем записи для согласования - if (stages && stages.length > 0) { - stages.forEach(stage => { - // Определяем пользователя для согласования - let approverUserId = stage.approver_user_id; - - // Если указана роль, находим пользователя с этой ролью - if (stage.approver_role && !approverUserId) { - db.get( - "SELECT id FROM users WHERE role = ? LIMIT 1", - [stage.approver_role], - (err, user) => { - if (user) { - approverUserId = user.id; - } - createApprovalRecord(); - } - ); - } else { - createApprovalRecord(); - } - - function createApprovalRecord() { - if (!approverUserId) { - console.log('⚠️ Не указан согласующий для этапа:', stage.stage_name); - return; - } - - // Устанавливаем дедлайн, если указано - let deadline = null; - if (stage.deadline_days) { - deadline = new Date(); - deadline.setDate(deadline.getDate() + stage.deadline_days); - } - - db.run( - `INSERT INTO document_approvals - (document_id, stage_id, approver_user_id, deadline) - VALUES (?, ?, ?, ?)`, - [documentId, stage.id, approverUserId, deadline] - ); - } - }); - } - - res.json({ - success: true, - message: 'Документ создан', - documentId: documentId - }); - } - ); - } - ); - }); - }); - } catch (error) { - console.error('Ошибка:', error); - res.status(500).json({ error: 'Ошибка сервера' }); - } -}); - -// Получение списка документов пользователя -router.get('/api/documents', requireAuth, async (req, res) => { - try { - const { getDb } = require('./database'); - const db = getDb(); - const userId = req.session.user.id; - const { status, type, search } = req.query; - - let query = ` - SELECT DISTINCT d.*, - u.name as author_name, - dt.name as type_name, - dt.id as type_id, - (SELECT COUNT(*) FROM document_approvals da - WHERE da.document_id = d.id AND da.approver_user_id = ? AND da.status = 'pending') as pending_for_me - FROM documents d - LEFT JOIN users u ON d.created_by = u.id - LEFT JOIN document_types dt ON d.document_type_id = dt.id - LEFT JOIN document_approvals da ON d.id = da.document_id - WHERE 1=1 - `; - - const params = [userId]; - - // Фильтр по статусу - if (status && status !== 'all') { - query += ` AND d.status = ?`; - params.push(status); - } - - // Фильтр по типу - if (type && type !== 'all') { - query += ` AND d.document_type_id = ?`; - params.push(type); - } - - // Поиск - if (search) { - query += ` AND (d.title LIKE ? OR d.description LIKE ?)`; - const searchPattern = `%${search}%`; - params.push(searchPattern, searchPattern); - } - - // Показываем документы, которые создал пользователь или которые ему нужно согласовать - query += ` AND (d.created_by = ? OR da.approver_user_id = ?)`; - params.push(userId, userId); - - query += ` GROUP BY d.id ORDER BY d.created_at DESC`; - - db.all(query, params, async (err, documents) => { - if (err) { - console.error('Ошибка получения документов:', err); - return res.status(500).json({ error: 'Ошибка сервера' }); - } - - // Добавляем информацию о согласовании - const documentsWithApprovals = await Promise.all(documents.map(async (doc) => { - const approvals = await new Promise((resolve) => { - db.all(` - SELECT da.*, a.stage_number, a.stage_name, - u.name as approver_name, u.login as approver_login - FROM document_approvals da - LEFT JOIN approval_stages a ON da.stage_id = a.id - LEFT JOIN users u ON da.approver_user_id = u.id - WHERE da.document_id = ? - ORDER BY a.stage_number - `, [doc.id], (err, rows) => { - resolve(rows || []); - }); - }); - - return { - ...doc, - approvals - }; - })); - - res.json(documentsWithApprovals); - }); - } catch (error) { - console.error('Ошибка:', error); - res.status(500).json({ error: 'Ошибка сервера' }); - } -}); - -// Получение информации о конкретном документе -router.get('/api/documents/:id', requireAuth, async (req, res) => { - try { - const { getDb } = require('./database'); - const db = getDb(); - const documentId = req.params.id; - const userId = req.session.user.id; - - // Проверяем доступ к документу - db.get(` - SELECT 1 FROM documents d - LEFT JOIN document_approvals da ON d.id = da.document_id - WHERE d.id = ? AND (d.created_by = ? OR da.approver_user_id = ?) - LIMIT 1 - `, [documentId, userId, userId], (err, hasAccess) => { - if (err || !hasAccess) { - return res.status(403).json({ error: 'Доступ к документу запрещен' }); - } - - // Получаем информацию о документе - db.get(` - SELECT d.*, - u.name as author_name, u.login as author_login, - dt.name as type_name, dt.description as type_description, - au.name as approver_name, ru.name as rejector_name - FROM documents d - LEFT JOIN users u ON d.created_by = u.id - LEFT JOIN document_types dt ON d.document_type_id = dt.id - LEFT JOIN users au ON d.approved_by = au.id - LEFT JOIN users ru ON d.rejected_by = ru.id - WHERE d.id = ? - `, [documentId], async (err, document) => { - if (err || !document) { - return res.status(404).json({ error: 'Документ не найден' }); - } - - // Получаем этапы согласования - const approvals = await new Promise((resolve) => { - db.all(` - SELECT da.*, a.stage_number, a.stage_name, a.can_edit, a.can_comment, - u.name as approver_name, u.login as approver_login, - CASE - WHEN da.approver_user_id = ? THEN 'current' - WHEN da.status = 'pending' AND a.stage_number < ( - SELECT MIN(a2.stage_number) - FROM document_approvals da2 - LEFT JOIN approval_stages a2 ON da2.stage_id = a2.id - WHERE da2.document_id = ? AND da2.status = 'pending' - ) THEN 'completed' - ELSE 'future' - END as user_relation - FROM document_approvals da - LEFT JOIN approval_stages a ON da.stage_id = a.id - LEFT JOIN users u ON da.approver_user_id = u.id - WHERE da.document_id = ? - ORDER BY a.stage_number - `, [userId, documentId, documentId], (err, rows) => { - resolve(rows || []); - }); - }); - - // Получаем комментарии - const comments = await new Promise((resolve) => { - db.all(` - SELECT dc.*, u.name as user_name, u.login as user_login - FROM document_comments dc - LEFT JOIN users u ON dc.user_id = u.id - WHERE dc.document_id = ? - ORDER BY dc.created_at DESC - `, [documentId], (err, rows) => { - resolve(rows || []); - }); - }); - - // Получаем историю - const history = await new Promise((resolve) => { - db.all(` - SELECT dh.*, u.name as user_name - FROM document_history dh - LEFT JOIN users u ON dh.user_id = u.id - WHERE dh.document_id = ? - ORDER BY dh.created_at DESC - LIMIT 20 - `, [documentId], (err, rows) => { - resolve(rows || []); - }); - }); - - // Определяем текущий этап для пользователя - let currentStage = null; - if (document.status === 'in_review') { - currentStage = approvals.find(a => - a.approver_user_id === userId && a.status === 'pending' - ); - } - - // Проверяем права на редактирование - let canEdit = false; - let canApprove = false; - let canComment = true; - - if (document.created_by === userId && document.status === 'draft') { - canEdit = true; - } - - if (currentStage) { - const stageInfo = approvals.find(a => a.id === currentStage.id); - canEdit = stageInfo?.can_edit || false; - canApprove = true; - canComment = stageInfo?.can_comment || true; - } - - res.json({ - document, - approvals, - comments, - history, - permissions: { - canEdit, - canApprove, - canComment, - canDownload: true, - canDelete: document.created_by === userId && document.status === 'draft' - }, - currentStage - }); - }); - }); - } catch (error) { - console.error('Ошибка:', error); - res.status(500).json({ error: 'Ошибка сервера' }); - } -}); - -// Скачивание документа -router.get('/api/documents/:id/download', requireAuth, async (req, res) => { - try { - const { getDb } = require('./database'); - const db = getDb(); - const documentId = req.params.id; - const userId = req.session.user.id; - - // Проверяем доступ к документу - db.get(` - SELECT 1 FROM documents d - LEFT JOIN document_approvals da ON d.id = da.document_id - WHERE d.id = ? AND (d.created_by = ? OR da.approver_user_id = ?) - LIMIT 1 - `, [documentId, userId, userId], (err, hasAccess) => { - if (err || !hasAccess) { - return res.status(403).json({ error: 'Доступ к документу запрещен' }); - } - - // Получаем информацию о файле - db.get("SELECT file_path, file_name FROM documents WHERE id = ?", [documentId], (err, doc) => { - if (err || !doc) { - return res.status(404).json({ error: 'Документ не найден' }); - } - - if (!fs.existsSync(doc.file_path)) { - return res.status(404).json({ error: 'Файл не найден' }); - } - - // Устанавливаем заголовки для скачивания - const encodedFileName = encodeURIComponent(doc.file_name); - res.setHeader('Content-Disposition', `attachment; filename*=UTF-8''${encodedFileName}`); - res.setHeader('Content-Type', 'application/octet-stream'); - - // Отправляем файл - res.sendFile(doc.file_path); - }); - }); - } catch (error) { - console.error('Ошибка:', error); - res.status(500).json({ error: 'Ошибка сервера' }); - } -}); - -// Отправка документа на согласование -router.post('/api/documents/:id/submit', requireAuth, async (req, res) => { - try { - const { getDb } = require('./database'); - const db = getDb(); - const documentId = req.params.id; - const userId = req.session.user.id; - - // Проверяем, что документ принадлежит пользователю и имеет статус черновика - db.get("SELECT status, created_by FROM documents WHERE id = ?", [documentId], (err, document) => { - if (err || !document) { - return res.status(404).json({ error: 'Документ не найден' }); - } - - if (document.created_by !== userId) { - return res.status(403).json({ error: 'Недостаточно прав' }); - } - - if (document.status !== 'draft') { - return res.status(400).json({ error: 'Документ уже отправлен на согласование' }); - } - - db.serialize(() => { - // Обновляем статус документа - db.run( - "UPDATE documents SET status = 'in_review', updated_at = CURRENT_TIMESTAMP WHERE id = ?", - [documentId], - function(err) { - if (err) { - console.error('Ошибка обновления статуса:', err); - return res.status(500).json({ error: 'Ошибка сервера' }); - } - - // Логируем действие - logDocumentActivity(db, documentId, userId, 'DOCUMENT_SUBMITTED', 'Документ отправлен на согласование'); - - // Отправляем уведомления согласующим первого этапа - db.all(` - SELECT da.approver_user_id, u.email, u.name - FROM document_approvals da - LEFT JOIN users u ON da.approver_user_id = u.id - WHERE da.document_id = ? AND da.status = 'pending' - ORDER BY ( - SELECT stage_number FROM approval_stages WHERE id = da.stage_id - ) LIMIT 1 - `, [documentId], (err, approvers) => { - if (!err && approvers && approvers.length > 0) { - // Здесь можно добавить отправку email уведомлений - console.log(`📄 Документ ${documentId} отправлен на согласование`); - approvers.forEach(approver => { - console.log(` 👤 Уведомить: ${approver.name} (${approver.email})`); - }); - } - }); - - res.json({ success: true, message: 'Документ отправлен на согласование' }); - } - ); - }); - }); - } catch (error) { - console.error('Ошибка:', error); - res.status(500).json({ error: 'Ошибка сервера' }); - } -}); - -// Согласование документа -router.post('/api/documents/:id/approve', requireAuth, async (req, res) => { - try { - const { getDb } = require('./database'); - const db = getDb(); - const documentId = req.params.id; - const userId = req.session.user.id; - const { comments } = req.body; - - // Проверяем, что пользователь является текущим согласующим - db.get(` - SELECT da.*, a.stage_number - FROM document_approvals da - LEFT JOIN approval_stages a ON da.stage_id = a.id - WHERE da.document_id = ? AND da.approver_user_id = ? AND da.status = 'pending' - ORDER BY a.stage_number - LIMIT 1 - `, [documentId, userId], (err, approval) => { - if (err || !approval) { - return res.status(403).json({ error: 'Вы не являетесь текущим согласующим' }); - } - - db.serialize(() => { - // Обновляем запись согласования - db.run( - `UPDATE document_approvals - SET status = 'approved', comments = ?, approved_at = CURRENT_TIMESTAMP, - updated_at = CURRENT_TIMESTAMP - WHERE id = ?`, - [comments || '', approval.id], - function(err) { - if (err) { - console.error('Ошибка обновления согласования:', err); - return res.status(500).json({ error: 'Ошибка сервера' }); - } - - // Логируем действие - logDocumentActivity(db, documentId, userId, 'STAGE_APPROVED', - `Этап ${approval.stage_number} согласован`); - - // Добавляем комментарий, если он есть - if (comments) { - db.run( - `INSERT INTO document_comments (document_id, user_id, comment, is_internal) - VALUES (?, ?, ?, 0)`, - [documentId, userId, comments] - ); - } - - // Проверяем, остались ли этапы для согласования - db.get(` - SELECT COUNT(*) as pending_count - FROM document_approvals - WHERE document_id = ? AND status = 'pending' - `, [documentId], (err, result) => { - if (err) { - console.error('Ошибка проверки оставшихся этапов:', err); - return res.status(500).json({ error: 'Ошибка сервера' }); - } - - if (result.pending_count === 0) { - // Все этапы согласованы - db.run( - `UPDATE documents - SET status = 'approved', approved_at = CURRENT_TIMESTAMP, - approved_by = ?, updated_at = CURRENT_TIMESTAMP - WHERE id = ?`, - [userId, documentId], - function(err) { - if (err) { - console.error('Ошибка обновления статуса документа:', err); - return res.status(500).json({ error: 'Ошибка сервера' }); - } - - logDocumentActivity(db, documentId, userId, 'DOCUMENT_APPROVED', - 'Документ полностью согласован'); - - res.json({ - success: true, - message: 'Документ полностью согласован', - documentStatus: 'approved' - }); - } - ); - } else { - // Переходим к следующему этапу - db.get(` - SELECT da.approver_user_id, u.email, u.name - FROM document_approvals da - LEFT JOIN users u ON da.approver_user_id = u.id - WHERE da.document_id = ? AND da.status = 'pending' - ORDER BY ( - SELECT stage_number FROM approval_stages WHERE id = da.stage_id - ) LIMIT 1 - `, [documentId], (err, nextApprover) => { - if (!err && nextApprover) { - // Здесь можно добавить отправку уведомления следующему согласующему - console.log(`📄 Переход к следующему согласующему: ${nextApprover.name}`); - } - - res.json({ - success: true, - message: 'Этап согласован', - documentStatus: 'in_review' - }); - }); - } - }); - } - ); - }); - }); - } catch (error) { - console.error('Ошибка:', error); - res.status(500).json({ error: 'Ошибка сервера' }); - } -}); - -// Отклонение документа -router.post('/api/documents/:id/reject', requireAuth, async (req, res) => { - try { - const { getDb } = require('./database'); - const db = getDb(); - const documentId = req.params.id; - const userId = req.session.user.id; - const { reason } = req.body; - - if (!reason) { - return res.status(400).json({ error: 'Укажите причину отклонения' }); - } - - // Проверяем, что пользователь является текущим согласующим или создателем - db.get(` - SELECT d.created_by, da.*, a.stage_number - FROM documents d - LEFT JOIN document_approvals da ON d.id = da.document_id AND da.approver_user_id = ? AND da.status = 'pending' - LEFT JOIN approval_stages a ON da.stage_id = a.id - WHERE d.id = ? - `, [userId, documentId], (err, result) => { - if (err) { - console.error('Ошибка проверки прав:', err); - return res.status(500).json({ error: 'Ошибка сервера' }); - } - - const canReject = result?.created_by === userId || result?.id; // Создатель или текущий согласующий - - if (!canReject) { - return res.status(403).json({ error: 'Недостаточно прав для отклонения документа' }); - } - - db.serialize(() => { - // Если отклоняет согласующий, отмечаем его этап - if (result.id) { - db.run( - `UPDATE document_approvals - SET status = 'rejected', comments = ?, rejected_at = CURRENT_TIMESTAMP, - updated_at = CURRENT_TIMESTAMP - WHERE id = ?`, - [reason, result.id] - ); - - logDocumentActivity(db, documentId, userId, 'STAGE_REJECTED', - `Этап ${result.stage_number} отклонен: ${reason}`); - } - - // Обновляем статус документа - db.run( - `UPDATE documents - SET status = 'rejected', rejected_at = CURRENT_TIMESTAMP, - rejected_by = ?, rejection_reason = ?, updated_at = CURRENT_TIMESTAMP - WHERE id = ?`, - [userId, reason, documentId], - function(err) { - if (err) { - console.error('Ошибка обновления статуса документа:', err); - return res.status(500).json({ error: 'Ошибка сервера' }); - } - - // Добавляем комментарий - db.run( - `INSERT INTO document_comments (document_id, user_id, comment, is_internal) - VALUES (?, ?, ?, 0)`, - [documentId, userId, `Документ отклонен: ${reason}`] - ); - - logDocumentActivity(db, documentId, userId, 'DOCUMENT_REJECTED', - `Документ отклонен: ${reason}`); - - res.json({ success: true, message: 'Документ отклонен' }); - } - ); - }); - }); - } catch (error) { - console.error('Ошибка:', error); - res.status(500).json({ error: 'Ошибка сервера' }); - } -}); - -// Добавление комментария к документу -router.post('/api/documents/:id/comments', requireAuth, async (req, res) => { - try { - const { getDb } = require('./database'); - const db = getDb(); - const documentId = req.params.id; - const userId = req.session.user.id; - const { comment, is_internal = false } = req.body; - - if (!comment) { - return res.status(400).json({ error: 'Комментарий обязателен' }); - } - - // Проверяем доступ к документу - db.get(` - SELECT 1 FROM documents d - LEFT JOIN document_approvals da ON d.id = da.document_id - WHERE d.id = ? AND (d.created_by = ? OR da.approver_user_id = ?) - LIMIT 1 - `, [documentId, userId, userId], (err, hasAccess) => { - if (err || !hasAccess) { - return res.status(403).json({ error: 'Доступ к документу запрещен' }); - } - - db.run( - `INSERT INTO document_comments (document_id, user_id, comment, is_internal) - VALUES (?, ?, ?, ?)`, - [documentId, userId, comment, is_internal ? 1 : 0], - function(err) { - if (err) { - console.error('Ошибка добавления комментария:', err); - return res.status(500).json({ error: 'Ошибка сервера' }); - } - - logDocumentActivity(db, documentId, userId, 'COMMENT_ADDED', 'Добавлен комментарий'); - - res.json({ success: true, message: 'Комментарий добавлен' }); - } - ); - }); - } catch (error) { - console.error('Ошибка:', error); - res.status(500).json({ error: 'Ошибка сервера' }); - } -}); - -// Обновление документа (новая версия) -router.put('/api/documents/:id', requireAuth, upload.single('file'), async (req, res) => { - try { - const { getDb } = require('./database'); - const db = getDb(); - const documentId = req.params.id; - const userId = req.session.user.id; - const { title, description } = req.body; - - // Проверяем права на редактирование - db.get(` - SELECT d.*, da.id as approval_id, a.can_edit - FROM documents d - LEFT JOIN document_approvals da ON d.id = da.document_id AND da.approver_user_id = ? AND da.status = 'pending' - LEFT JOIN approval_stages a ON da.stage_id = a.id - WHERE d.id = ? - `, [userId, documentId], (err, document) => { - if (err || !document) { - return res.status(404).json({ error: 'Документ не найден' }); - } - - const canEdit = (document.created_by === userId && document.status === 'draft') || - (document.approval_id && document.can_edit); - - if (!canEdit) { - return res.status(403).json({ error: 'Недостаточно прав для редактирования' }); - } - - db.serialize(() => { - // Создаем новую версию документа - const newVersion = document.version + 1; - - const updateFields = [ - 'updated_at = CURRENT_TIMESTAMP', - 'version = ?' - ]; - const params = [newVersion]; - - if (title) { - updateFields.push('title = ?'); - params.push(title); - } - - if (description) { - updateFields.push('description = ?'); - params.push(description); - } - - if (req.file) { - // Удаляем старый файл, если он существует - if (document.file_path && fs.existsSync(document.file_path)) { - fs.unlinkSync(document.file_path); - } - - updateFields.push('file_path = ?', 'file_name = ?', 'file_size = ?'); - params.push(req.file.path, req.file.originalname, req.file.size); - } - - params.push(documentId); - - db.run( - `UPDATE documents SET ${updateFields.join(', ')} WHERE id = ?`, - params, - function(err) { - if (err) { - console.error('Ошибка обновления документа:', err); - return res.status(500).json({ error: 'Ошибка сервера' }); - } - - // Логируем действие - const changes = []; - if (title && title !== document.title) changes.push(`Название: "${document.title}" -> "${title}"`); - if (description && description !== document.description) changes.push('Описание изменено'); - if (req.file) changes.push('Файл обновлен'); - - logDocumentActivity(db, documentId, userId, 'DOCUMENT_UPDATED', - `Версия ${newVersion}: ${changes.join(', ')}`); - - res.json({ success: true, message: 'Документ обновлен', version: newVersion }); - } - ); - }); - }); - } catch (error) { - console.error('Ошибка:', error); - res.status(500).json({ error: 'Ошибка сервера' }); - } -}); - -// Удаление документа -router.delete('/api/documents/:id', requireAuth, async (req, res) => { - try { - const { getDb } = require('./database'); - const db = getDb(); - const documentId = req.params.id; - const userId = req.session.user.id; - - // Проверяем, что документ принадлежит пользователю и имеет статус черновика - db.get("SELECT status, created_by, file_path FROM documents WHERE id = ?", [documentId], (err, document) => { - if (err || !document) { - return res.status(404).json({ error: 'Документ не найден' }); - } - - if (document.created_by !== userId) { - return res.status(403).json({ error: 'Недостаточно прав' }); - } - - if (document.status !== 'draft') { - return res.status(400).json({ error: 'Можно удалять только черновики' }); - } - - db.serialize(() => { - // Удаляем связанные записи - db.run("DELETE FROM document_approvals WHERE document_id = ?", [documentId]); - db.run("DELETE FROM document_comments WHERE document_id = ?", [documentId]); - db.run("DELETE FROM document_history WHERE document_id = ?", [documentId]); - - // Удаляем файл - if (document.file_path && fs.existsSync(document.file_path)) { - fs.unlinkSync(document.file_path); - } - - // Удаляем документ - db.run("DELETE FROM documents WHERE id = ?", [documentId], function(err) { - if (err) { - console.error('Ошибка удаления документа:', err); - return res.status(500).json({ error: 'Ошибка сервера' }); - } - - res.json({ success: true, message: 'Документ удален' }); - }); - }); - }); - } catch (error) { - console.error('Ошибка:', error); - res.status(500).json({ error: 'Ошибка сервера' }); - } -}); - -// Получение документов, ожидающих согласования пользователем -router.get('/api/documents/pending', requireAuth, async (req, res) => { - try { - const { getDb } = require('./database'); - const db = getDb(); - const userId = req.session.user.id; - - const query = ` - SELECT d.*, - u.name as author_name, - dt.name as type_name, - a.stage_number, a.stage_name, - da.deadline, - julianday(da.deadline) - julianday('now') as days_until_deadline - FROM document_approvals da - LEFT JOIN documents d ON da.document_id = d.id - LEFT JOIN users u ON d.created_by = u.id - LEFT JOIN document_types dt ON d.document_type_id = dt.id - LEFT JOIN approval_stages a ON da.stage_id = a.id - WHERE da.approver_user_id = ? - AND da.status = 'pending' - AND d.status = 'in_review' - ORDER BY da.deadline ASC, d.created_at DESC - `; - - db.all(query, [userId], (err, documents) => { - if (err) { - console.error('Ошибка получения документов:', err); - return res.status(500).json({ error: 'Ошибка сервера' }); - } - res.json(documents); - }); - } catch (error) { - console.error('Ошибка:', error); - res.status(500).json({ error: 'Ошибка сервера' }); - } -}); - -module.exports = router; \ No newline at end of file diff --git a/package.json b/package.json index 29101a4..4d74cec 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "express": "^4.21.2", "express-session": "^1.18.2", "form-data": "^4.0.0", + "mime-types": "^3.0.2", "multer": "^2.0.2", "node-fetch": "~2.6.7", "nodemailer": "^6.9.13", @@ -22,4 +23,4 @@ "devDependencies": { "nodemon": "~3.0.1" } -} \ No newline at end of file +} diff --git a/server.js b/server.js index 658d62f..22db637 100644 --- a/server.js +++ b/server.js @@ -14,9 +14,6 @@ const { sendTaskNotifications, checkUpcomingDeadlines, getStatusText } = require const { setupUploadMiddleware } = require('./upload-middleware'); const { setupTaskEndpoints } = require('./task-endpoints'); -// admin-doc -const adminDocRouter = require('./admin-doc'); -const docRouter = require('./doc-endpoints'); const app = express(); const PORT = process.env.PORT || 3000; @@ -43,9 +40,6 @@ app.use(session({ } })); -// Подключаем роутеры для документов -app.use(adminDocRouter); -app.use(docRouter); // Middleware для проверки готовности сервера app.use((req, res, next) => {