doc-del
This commit is contained in:
563
admin-doc.js
563
admin-doc.js
@@ -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;
|
|
||||||
1279
admin-server.js
1279
admin-server.js
File diff suppressed because it is too large
Load Diff
953
doc-endpoints.js
953
doc-endpoints.js
@@ -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;
|
|
||||||
@@ -13,6 +13,7 @@
|
|||||||
"express": "^4.21.2",
|
"express": "^4.21.2",
|
||||||
"express-session": "^1.18.2",
|
"express-session": "^1.18.2",
|
||||||
"form-data": "^4.0.0",
|
"form-data": "^4.0.0",
|
||||||
|
"mime-types": "^3.0.2",
|
||||||
"multer": "^2.0.2",
|
"multer": "^2.0.2",
|
||||||
"node-fetch": "~2.6.7",
|
"node-fetch": "~2.6.7",
|
||||||
"nodemailer": "^6.9.13",
|
"nodemailer": "^6.9.13",
|
||||||
@@ -22,4 +23,4 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"nodemon": "~3.0.1"
|
"nodemon": "~3.0.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,9 +14,6 @@ const { sendTaskNotifications, checkUpcomingDeadlines, getStatusText } = require
|
|||||||
const { setupUploadMiddleware } = require('./upload-middleware');
|
const { setupUploadMiddleware } = require('./upload-middleware');
|
||||||
const { setupTaskEndpoints } = require('./task-endpoints');
|
const { setupTaskEndpoints } = require('./task-endpoints');
|
||||||
|
|
||||||
// admin-doc
|
|
||||||
const adminDocRouter = require('./admin-doc');
|
|
||||||
const docRouter = require('./doc-endpoints');
|
|
||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
const PORT = process.env.PORT || 3000;
|
const PORT = process.env.PORT || 3000;
|
||||||
@@ -43,9 +40,6 @@ app.use(session({
|
|||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Подключаем роутеры для документов
|
|
||||||
app.use(adminDocRouter);
|
|
||||||
app.use(docRouter);
|
|
||||||
|
|
||||||
// Middleware для проверки готовности сервера
|
// Middleware для проверки готовности сервера
|
||||||
app.use((req, res, next) => {
|
app.use((req, res, next) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user