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.
-
Если вы получили это письмо, значит настройки уведомлений работают корректно.
-
-
Информация о тесте:
-
- - Получатель: ${emailTo}
- - Отправитель: Администратор
- - Время: ${new Date().toLocaleString('ru-RU')}
-
-
-
-
-
-
- `
- );
-
- 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) => {