admin-profiles.html
This commit is contained in:
563
admin-doc.js
Normal file
563
admin-doc.js
Normal file
@@ -0,0 +1,563 @@
|
||||
// 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;
|
||||
Reference in New Issue
Block a user