admin-profiles.html

This commit is contained in:
2026-01-26 22:55:51 +05:00
parent e547c89ce0
commit 1a0698a72b
4 changed files with 1643 additions and 0 deletions

563
admin-doc.js Normal file
View 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;