diff --git a/api2-groups.js b/api2-groups.js new file mode 100644 index 0000000..045f614 --- /dev/null +++ b/api2-groups.js @@ -0,0 +1,971 @@ +// api2-groups.js +// API для управления внешними идентификаторами пользователей + +const express = require('express'); +const router = express.Router(); + +module.exports = function(app, db) { + + // Middleware для проверки аутентификации + const requireAuth = (req, res, next) => { + if (!req.session || !req.session.user) { + return res.status(401).json({ error: 'Требуется аутентификация' }); + } + next(); + }; + + // Middleware для проверки прав администратора + const requireAdmin = (req, res, next) => { + if (!req.session || !req.session.user || req.session.user.role !== 'admin') { + return res.status(403).json({ error: 'Недостаточно прав' }); + } + next(); + }; + + // 1. Создание таблиц при инициализации + function createIdTables() { + return new Promise((resolve, reject) => { + // Таблица с группами для идентификаторов + db.run(` + CREATE TABLE IF NOT EXISTS idgroups ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL UNIQUE, + description TEXT, + service_type TEXT NOT NULL, -- 'sberbank', 'yandex', 'ldap', 'other' + is_active BOOLEAN DEFAULT true, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + updated_at DATETIME DEFAULT CURRENT_TIMESTAMP + ) + `, (err) => { + if (err) { + console.error('❌ Ошибка создания таблицы idgroups:', err.message); + reject(err); + return; + } + + // Таблица с идентификаторами пользователей + db.run(` + CREATE TABLE IF NOT EXISTS idusers ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + user_id INTEGER NOT NULL, + service_type TEXT NOT NULL, -- 'sberbank', 'yandex', 'ldap', 'other' + external_id TEXT NOT NULL, + login TEXT, -- Логин LDAP (если есть) + ldap_group TEXT, -- Группа LDAP (если есть) + group_id INTEGER, -- Ссылка на группу в idgroups + metadata TEXT, -- Дополнительные данные в JSON + is_active BOOLEAN DEFAULT true, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + updated_at DATETIME DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE, + FOREIGN KEY (group_id) REFERENCES idgroups (id) ON DELETE SET NULL, + UNIQUE(user_id, service_type, external_id) + ) + `, (err) => { + if (err) { + console.error('❌ Ошибка создания таблицы idusers:', err.message); + reject(err); + return; + } + + // Создаем индексы + db.run('CREATE INDEX IF NOT EXISTS idx_idusers_user_id ON idusers(user_id)', (err) => { + if (err) console.error('❌ Ошибка создания индекса idx_idusers_user_id:', err.message); + }); + + db.run('CREATE INDEX IF NOT EXISTS idx_idusers_service_type ON idusers(service_type)', (err) => { + if (err) console.error('❌ Ошибка создания индекса idx_idusers_service_type:', err.message); + }); + + db.run('CREATE INDEX IF NOT EXISTS idx_idusers_external_id ON idusers(external_id)', (err) => { + if (err) console.error('❌ Ошибка создания индекса idx_idusers_external_id:', err.message); + }); + + db.run('CREATE INDEX IF NOT EXISTS idx_idgroups_service_type ON idgroups(service_type)', (err) => { + if (err) console.error('❌ Ошибка создания индекса idx_idgroups_service_type:', err.message); + }); + + console.log('✅ Таблицы для внешних идентификаторов созданы'); + resolve(); + }); + }); + }); + } + + // 2. Добавление стандартных групп + function addDefaultGroups() { + const defaultGroups = [ + { + name: 'Сбербанк - Сотрудники', + description: 'Идентификаторы сотрудников в системе Сбербанка', + service_type: 'sberbank', + is_active: true + }, + { + name: 'Сбербанк - Контрагенты', + description: 'Идентификаторы контрагентов в системе Сбербанка', + service_type: 'sberbank', + is_active: true + }, + { + name: 'Яндекс - Работники', + description: 'Идентификаторы работников в системе Яндекса', + service_type: 'yandex', + is_active: true + }, + { + name: 'Яндекс - Партнеры', + description: 'Идентификаторы партнеров в системе Яндекса', + service_type: 'yandex', + is_active: true + }, + { + name: 'LDAP - Преподаватели', + description: 'Пользователи из LDAP с ролью преподавателя', + service_type: 'ldap', + is_active: true + }, + { + name: 'LDAP - Администрация', + description: 'Пользователи из LDAP с ролью администрации', + service_type: 'ldap', + is_active: true + }, + { + name: 'Прочие системы', + description: 'Идентификаторы из других систем', + service_type: 'other', + is_active: true + } + ]; + + defaultGroups.forEach(group => { + db.get("SELECT id FROM idgroups WHERE name = ?", [group.name], (err, existing) => { + if (err) { + console.error(`❌ Ошибка проверки группы ${group.name}:`, err.message); + return; + } + + if (!existing) { + db.run( + `INSERT INTO idgroups (name, description, service_type, is_active) + VALUES (?, ?, ?, ?)`, + [group.name, group.description, group.service_type, group.is_active ? 1 : 0], + (insertErr) => { + if (insertErr) { + console.error(`❌ Ошибка создания группы ${group.name}:`, insertErr.message); + } else { + console.log(`✅ Группа "${group.name}" создана по умолчанию`); + } + } + ); + } + }); + }); + } + + // 3. API эндпоинты + + // GET /api2/groups - Получить все группы (доступно всем аутентифицированным) + router.get('/api2/groups', requireAuth, (req, res) => { + const { service_type, is_active } = req.query; + + let query = 'SELECT * FROM idgroups WHERE 1=1'; + const params = []; + + if (service_type) { + query += ' AND service_type = ?'; + params.push(service_type); + } + + if (is_active !== undefined) { + query += ' AND is_active = ?'; + params.push(is_active === 'true' ? 1 : 0); + } + + query += ' ORDER BY service_type, name'; + + db.all(query, params, (err, groups) => { + if (err) { + console.error('❌ Ошибка получения групп:', err); + return res.status(500).json({ error: 'Ошибка получения групп' }); + } + res.json(groups || []); + }); + }); + + // GET /api2/groups/:id - Получить группу по ID (доступно всем аутентифицированным) + router.get('/api2/groups/:id', requireAuth, (req, res) => { + const { id } = req.params; + + db.get('SELECT * FROM idgroups WHERE id = ?', [id], (err, group) => { + if (err) { + console.error('❌ Ошибка получения группы:', err); + return res.status(500).json({ error: 'Ошибка получения группы' }); + } + + if (!group) { + return res.status(404).json({ error: 'Группа не найдена' }); + } + + res.json(group); + }); + }); + + // POST /api2/groups - Создать новую группу (только админ) + router.post('/api2/groups', requireAuth, requireAdmin, (req, res) => { + const { name, description, service_type, is_active } = req.body; + + if (!name || !service_type) { + return res.status(400).json({ + error: 'Обязательные поля: name, service_type' + }); + } + + const validServiceTypes = ['sberbank', 'yandex', 'ldap', 'other']; + if (!validServiceTypes.includes(service_type)) { + return res.status(400).json({ + error: `Недопустимый тип сервиса. Допустимые значения: ${validServiceTypes.join(', ')}` + }); + } + + db.run( + `INSERT INTO idgroups (name, description, service_type, is_active) + VALUES (?, ?, ?, ?)`, + [ + name, + description || '', + service_type, + is_active !== undefined ? (is_active ? 1 : 0) : 1 + ], + function(err) { + if (err) { + if (err.code === 'SQLITE_CONSTRAINT') { + return res.status(409).json({ error: 'Группа с таким именем уже существует' }); + } + console.error('❌ Ошибка создания группы:', err); + return res.status(500).json({ error: 'Ошибка создания группы' }); + } + + res.status(201).json({ + success: true, + id: this.lastID, + message: 'Группа успешно создана' + }); + } + ); + }); + + // PUT /api2/groups/:id - Обновить группу (только админ) + router.put('/api2/groups/:id', requireAuth, requireAdmin, (req, res) => { + const { id } = req.params; + const { name, description, service_type, is_active } = req.body; + + // Проверяем существование группы + db.get('SELECT id FROM idgroups WHERE id = ?', [id], (err, existing) => { + if (err) { + console.error('❌ Ошибка проверки группы:', err); + return res.status(500).json({ error: 'Ошибка проверки группы' }); + } + + if (!existing) { + return res.status(404).json({ error: 'Группа не найдена' }); + } + + // Собираем поля для обновления + const updates = []; + const params = []; + + if (name !== undefined) { + updates.push('name = ?'); + params.push(name); + } + + if (description !== undefined) { + updates.push('description = ?'); + params.push(description); + } + + if (service_type !== undefined) { + const validServiceTypes = ['sberbank', 'yandex', 'ldap', 'other']; + if (!validServiceTypes.includes(service_type)) { + return res.status(400).json({ + error: `Недопустимый тип сервиса. Допустимые значения: ${validServiceTypes.join(', ')}` + }); + } + updates.push('service_type = ?'); + params.push(service_type); + } + + if (is_active !== undefined) { + updates.push('is_active = ?'); + params.push(is_active ? 1 : 0); + } + + if (updates.length === 0) { + return res.status(400).json({ error: 'Нет данных для обновления' }); + } + + updates.push('updated_at = CURRENT_TIMESTAMP'); + params.push(id); + + const query = `UPDATE idgroups SET ${updates.join(', ')} WHERE id = ?`; + + db.run(query, params, function(err) { + if (err) { + if (err.code === 'SQLITE_CONSTRAINT') { + return res.status(409).json({ error: 'Группа с таким именем уже существует' }); + } + console.error('❌ Ошибка обновления группы:', err); + return res.status(500).json({ error: 'Ошибка обновления группы' }); + } + + res.json({ + success: true, + changes: this.changes, + message: 'Группа успешно обновлена' + }); + }); + }); + }); + + // DELETE /api2/groups/:id - Удалить группу (только админ) + router.delete('/api2/groups/:id', requireAuth, requireAdmin, (req, res) => { + const { id } = req.params; + + // Проверяем, используется ли группа + db.get('SELECT COUNT(*) as count FROM idusers WHERE group_id = ?', [id], (err, result) => { + if (err) { + console.error('❌ Ошибка проверки использования группы:', err); + return res.status(500).json({ error: 'Ошибка проверки группы' }); + } + + if (result.count > 0) { + return res.status(400).json({ + error: 'Невозможно удалить группу, так как она используется в идентификаторах пользователей', + used_count: result.count + }); + } + + db.run('DELETE FROM idgroups WHERE id = ?', [id], function(err) { + if (err) { + console.error('❌ Ошибка удаления группы:', err); + return res.status(500).json({ error: 'Ошибка удаления группы' }); + } + + res.json({ + success: true, + changes: this.changes, + message: 'Группа успешно удалена' + }); + }); + }); + }); + + // GET /api2/idusers - Получить все идентификаторы пользователей (доступно всем аутентифицированным) + router.get('/api2/idusers', requireAuth, (req, res) => { + const { user_id, service_type, external_id, group_id, is_active } = req.query; + + let query = ` + SELECT iu.*, + u.name as user_name, + u.login as user_login, + u.email as user_email, + g.name as group_name + FROM idusers iu + LEFT JOIN users u ON iu.user_id = u.id + LEFT JOIN idgroups g ON iu.group_id = g.id + WHERE 1=1 + `; + + const params = []; + + if (user_id) { + query += ' AND iu.user_id = ?'; + params.push(user_id); + } + + if (service_type) { + query += ' AND iu.service_type = ?'; + params.push(service_type); + } + + if (external_id) { + query += ' AND iu.external_id LIKE ?'; + params.push(`%${external_id}%`); + } + + if (group_id) { + query += ' AND iu.group_id = ?'; + params.push(group_id); + } + + if (is_active !== undefined) { + query += ' AND iu.is_active = ?'; + params.push(is_active === 'true' ? 1 : 0); + } + + query += ' ORDER BY iu.service_type, iu.external_id'; + + db.all(query, params, (err, idusers) => { + if (err) { + console.error('❌ Ошибка получения идентификаторов пользователей:', err); + return res.status(500).json({ error: 'Ошибка получения данных' }); + } + + // Парсим JSON metadata если есть + const result = (idusers || []).map(item => { + if (item.metadata) { + try { + item.metadata = JSON.parse(item.metadata); + } catch (e) { + item.metadata = {}; + } + } else { + item.metadata = {}; + } + return item; + }); + + res.json(result); + }); + }); + + // GET /api2/idusers/:id - Получить идентификатор по ID (доступно всем аутентифицированным) + router.get('/api2/idusers/:id', requireAuth, (req, res) => { + const { id } = req.params; + + db.get(` + SELECT iu.*, + u.name as user_name, + u.login as user_login, + u.email as user_email, + g.name as group_name + FROM idusers iu + LEFT JOIN users u ON iu.user_id = u.id + LEFT JOIN idgroups g ON iu.group_id = g.id + WHERE iu.id = ? + `, [id], (err, iduser) => { + if (err) { + console.error('❌ Ошибка получения идентификатора:', err); + return res.status(500).json({ error: 'Ошибка получения данных' }); + } + + if (!iduser) { + return res.status(404).json({ error: 'Идентификатор не найден' }); + } + + // Парсим JSON metadata если есть + if (iduser.metadata) { + try { + iduser.metadata = JSON.parse(iduser.metadata); + } catch (e) { + iduser.metadata = {}; + } + } else { + iduser.metadata = {}; + } + + res.json(iduser); + }); + }); + + // GET /api2/idusers/user/:userId - Получить идентификаторы конкретного пользователя (доступно всем аутентифицированным) + router.get('/api2/idusers/user/:userId', requireAuth, (req, res) => { + const { userId } = req.params; + const { service_type } = req.query; + + let query = ` + SELECT iu.*, g.name as group_name + FROM idusers iu + LEFT JOIN idgroups g ON iu.group_id = g.id + WHERE iu.user_id = ? + `; + + const params = [userId]; + + if (service_type) { + query += ' AND iu.service_type = ?'; + params.push(service_type); + } + + query += ' ORDER BY iu.service_type, iu.external_id'; + + db.all(query, params, (err, idusers) => { + if (err) { + console.error('❌ Ошибка получения идентификаторов пользователя:', err); + return res.status(500).json({ error: 'Ошибка получения данных' }); + } + + // Парсим JSON metadata если есть + const result = (idusers || []).map(item => { + if (item.metadata) { + try { + item.metadata = JSON.parse(item.metadata); + } catch (e) { + item.metadata = {}; + } + } else { + item.metadata = {}; + } + return item; + }); + + res.json(result); + }); + }); + + // POST /api2/idusers - Создать новый идентификатор пользователя (только админ) + router.post('/api2/idusers', requireAuth, requireAdmin, (req, res) => { + const { + user_id, + service_type, + external_id, + login, + ldap_group, + group_id, + metadata, + is_active + } = req.body; + + // Валидация обязательных полей + if (!user_id || !service_type || !external_id) { + return res.status(400).json({ + error: 'Обязательные поля: user_id, service_type, external_id' + }); + } + + const validServiceTypes = ['sberbank', 'yandex', 'ldap', 'other']; + if (!validServiceTypes.includes(service_type)) { + return res.status(400).json({ + error: `Недопустимый тип сервиса. Допустимые значения: ${validServiceTypes.join(', ')}` + }); + } + + // Проверяем существование пользователя + db.get('SELECT id FROM users WHERE id = ?', [user_id], (err, user) => { + if (err) { + console.error('❌ Ошибка проверки пользователя:', err); + return res.status(500).json({ error: 'Ошибка проверки пользователя' }); + } + + if (!user) { + return res.status(404).json({ error: 'Пользователь не найден' }); + } + + // Если указана группа, проверяем ее существование + if (group_id) { + db.get('SELECT id, service_type FROM idgroups WHERE id = ?', [group_id], (err, group) => { + if (err) { + console.error('❌ Ошибка проверки группы:', err); + return res.status(500).json({ error: 'Ошибка проверки группы' }); + } + + if (!group) { + return res.status(404).json({ error: 'Указанная группа не найдена' }); + } + + // Проверяем соответствие типа сервиса + if (group.service_type !== service_type) { + return res.status(400).json({ + error: `Тип сервиса группы (${group.service_type}) не соответствует типу сервиса идентификатора (${service_type})` + }); + } + + // Создаем идентификатор + createIdUser(); + }); + } else { + // Создаем идентификатор без группы + createIdUser(); + } + + function createIdUser() { + const metadataJson = metadata ? JSON.stringify(metadata) : null; + + db.run( + `INSERT INTO idusers + (user_id, service_type, external_id, login, ldap_group, group_id, metadata, is_active) + VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, + [ + user_id, + service_type, + external_id, + login || null, + ldap_group || null, + group_id || null, + metadataJson, + is_active !== undefined ? (is_active ? 1 : 0) : 1 + ], + function(err) { + if (err) { + if (err.code === 'SQLITE_CONSTRAINT') { + return res.status(409).json({ + error: 'Идентификатор с такими параметрами уже существует для этого пользователя' + }); + } + console.error('❌ Ошибка создания идентификатора:', err); + return res.status(500).json({ error: 'Ошибка создания идентификатора' }); + } + + res.status(201).json({ + success: true, + id: this.lastID, + message: 'Идентификатор успешно создан' + }); + } + ); + } + }); + }); + + // PUT /api2/idusers/:id - Обновить идентификатор пользователя (только админ) + router.put('/api2/idusers/:id', requireAuth, requireAdmin, (req, res) => { + const { id } = req.params; + const { + user_id, + service_type, + external_id, + login, + ldap_group, + group_id, + metadata, + is_active + } = req.body; + + // Проверяем существование идентификатора + db.get('SELECT * FROM idusers WHERE id = ?', [id], (err, existing) => { + if (err) { + console.error('❌ Ошибка проверки идентификатора:', err); + return res.status(500).json({ error: 'Ошибка проверки идентификатора' }); + } + + if (!existing) { + return res.status(404).json({ error: 'Идентификатор не найден' }); + } + + // Собираем поля для обновления + const updates = []; + const params = []; + + if (user_id !== undefined) { + updates.push('user_id = ?'); + params.push(user_id); + } + + if (service_type !== undefined) { + const validServiceTypes = ['sberbank', 'yandex', 'ldap', 'other']; + if (!validServiceTypes.includes(service_type)) { + return res.status(400).json({ + error: `Недопустимый тип сервиса. Допустимые значения: ${validServiceTypes.join(', ')}` + }); + } + updates.push('service_type = ?'); + params.push(service_type); + } + + if (external_id !== undefined) { + updates.push('external_id = ?'); + params.push(external_id); + } + + if (login !== undefined) { + updates.push('login = ?'); + params.push(login || null); + } + + if (ldap_group !== undefined) { + updates.push('ldap_group = ?'); + params.push(ldap_group || null); + } + + if (group_id !== undefined) { + updates.push('group_id = ?'); + params.push(group_id || null); + } + + if (metadata !== undefined) { + updates.push('metadata = ?'); + params.push(metadata ? JSON.stringify(metadata) : null); + } + + if (is_active !== undefined) { + updates.push('is_active = ?'); + params.push(is_active ? 1 : 0); + } + + if (updates.length === 0) { + return res.status(400).json({ error: 'Нет данных для обновления' }); + } + + updates.push('updated_at = CURRENT_TIMESTAMP'); + params.push(id); + + const query = `UPDATE idusers SET ${updates.join(', ')} WHERE id = ?`; + + db.run(query, params, function(err) { + if (err) { + if (err.code === 'SQLITE_CONSTRAINT') { + return res.status(409).json({ + error: 'Идентификатор с такими параметрами уже существует' + }); + } + console.error('❌ Ошибка обновления идентификатора:', err); + return res.status(500).json({ error: 'Ошибка обновления идентификатора' }); + } + + res.json({ + success: true, + changes: this.changes, + message: 'Идентификатор успешно обновлен' + }); + }); + }); + }); + + // DELETE /api2/idusers/:id - Удалить идентификатор пользователя (только админ) + router.delete('/api2/idusers/:id', requireAuth, requireAdmin, (req, res) => { + const { id } = req.params; + + db.run('DELETE FROM idusers WHERE id = ?', [id], function(err) { + if (err) { + console.error('❌ Ошибка удаления идентификатора:', err); + return res.status(500).json({ error: 'Ошибка удаления идентификатора' }); + } + + res.json({ + success: true, + changes: this.changes, + message: 'Идентификатор успешно удален' + }); + }); + }); + + // GET /api2/idusers/search - Поиск идентификаторов (доступно всем аутентифицированным) + router.get('/api2/idusers/search', requireAuth, (req, res) => { + const { q, service_type } = req.query; + + if (!q || q.length < 2) { + return res.status(400).json({ + error: 'Строка поиска должна содержать минимум 2 символа' + }); + } + + let query = ` + SELECT iu.*, + u.name as user_name, + u.login as user_login, + u.email as user_email, + g.name as group_name + FROM idusers iu + LEFT JOIN users u ON iu.user_id = u.id + LEFT JOIN idgroups g ON iu.group_id = g.id + WHERE (iu.external_id LIKE ? OR iu.login LIKE ? OR u.name LIKE ? OR u.login LIKE ?) + `; + + const params = [`%${q}%`, `%${q}%`, `%${q}%`, `%${q}%`]; + + if (service_type) { + query += ' AND iu.service_type = ?'; + params.push(service_type); + } + + query += ' ORDER BY iu.service_type, iu.external_id LIMIT 50'; + + db.all(query, params, (err, idusers) => { + if (err) { + console.error('❌ Ошибка поиска идентификаторов:', err); + return res.status(500).json({ error: 'Ошибка поиска' }); + } + + // Парсим JSON metadata если есть + const result = (idusers || []).map(item => { + if (item.metadata) { + try { + item.metadata = JSON.parse(item.metadata); + } catch (e) { + item.metadata = {}; + } + } else { + item.metadata = {}; + } + return item; + }); + + res.json(result); + }); + }); + + // GET /api2/idusers/stats - Статистика по идентификаторам (доступно всем аутентифицированным) + router.get('/api2/idusers/stats', requireAuth, (req, res) => { + // Получаем статистику по типам сервисов + const query = ` + SELECT + service_type, + COUNT(*) as total_count, + COUNT(CASE WHEN is_active = 1 THEN 1 END) as active_count, + COUNT(DISTINCT user_id) as unique_users + FROM idusers + GROUP BY service_type + ORDER BY total_count DESC + `; + + db.all(query, [], (err, stats) => { + if (err) { + console.error('❌ Ошибка получения статистики:', err); + return res.status(500).json({ error: 'Ошибка получения статистики' }); + } + + // Получаем общую статистику + db.get(` + SELECT + COUNT(*) as total_identifiers, + COUNT(DISTINCT user_id) as total_users, + COUNT(CASE WHEN is_active = 1 THEN 1 END) as total_active + FROM idusers + `, [], (err, totalStats) => { + if (err) { + console.error('❌ Ошибка получения общей статистики:', err); + return res.status(500).json({ error: 'Ошибка получения статистики' }); + } + + res.json({ + by_service_type: stats || [], + totals: totalStats || {}, + timestamp: new Date().toISOString() + }); + }); + }); + }); + + // 4. Инициализация при подключении модуля + const init = async () => { + try { + await createIdTables(); + addDefaultGroups(); + console.log('✅ API для внешних идентификаторов инициализирован'); + } catch (error) { + console.error('❌ Ошибка инициализации API для внешних идентификаторов:', error.message); + } + }; + + // Запускаем инициализацию + init(); + + // Подключаем роутер к приложению + app.use(router); + + // Экспортируем функции для использования в других модулях + return { + // Функция для получения внешнего идентификатора пользователя + getUserIdByExternalId: function(service_type, external_id, callback) { + db.get( + 'SELECT user_id FROM idusers WHERE service_type = ? AND external_id = ? AND is_active = 1', + [service_type, external_id], + (err, result) => { + if (err) { + console.error('❌ Ошибка получения идентификатора пользователя:', err); + return callback(err, null); + } + callback(null, result ? result.user_id : null); + } + ); + }, + + // Функция для получения внешних идентификаторов пользователя + getUserExternalIds: function(user_id, service_type, callback) { + let query = 'SELECT * FROM idusers WHERE user_id = ? AND is_active = 1'; + const params = [user_id]; + + if (service_type) { + query += ' AND service_type = ?'; + params.push(service_type); + } + + db.all(query, params, (err, results) => { + if (err) { + console.error('❌ Ошибка получения внешних идентификаторов:', err); + return callback(err, null); + } + + // Парсим JSON metadata если есть + const formattedResults = (results || []).map(item => { + if (item.metadata) { + try { + item.metadata = JSON.parse(item.metadata); + } catch (e) { + item.metadata = {}; + } + } else { + item.metadata = {}; + } + return item; + }); + + callback(null, formattedResults); + }); + }, + + // Функция для добавления или обновления идентификатора + upsertUserId: function(user_id, service_type, external_id, data, callback) { + const { login, ldap_group, group_id, metadata, is_active } = data || {}; + + db.get( + 'SELECT id FROM idusers WHERE user_id = ? AND service_type = ? AND external_id = ?', + [user_id, service_type, external_id], + (err, existing) => { + if (err) { + return callback(err); + } + + const metadataJson = metadata ? JSON.stringify(metadata) : null; + + if (existing) { + // Обновляем существующий + db.run( + `UPDATE idusers SET + login = COALESCE(?, login), + ldap_group = COALESCE(?, ldap_group), + group_id = COALESCE(?, group_id), + metadata = COALESCE(?, metadata), + is_active = COALESCE(?, is_active), + updated_at = CURRENT_TIMESTAMP + WHERE id = ?`, + [ + login || null, + ldap_group || null, + group_id || null, + metadataJson, + is_active !== undefined ? (is_active ? 1 : 0) : 1, + existing.id + ], + function(err) { + callback(err, { updated: true, id: existing.id }); + } + ); + } else { + // Создаем новый + db.run( + `INSERT INTO idusers + (user_id, service_type, external_id, login, ldap_group, group_id, metadata, is_active) + VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, + [ + user_id, + service_type, + external_id, + login || null, + ldap_group || null, + group_id || null, + metadataJson, + is_active !== undefined ? (is_active ? 1 : 0) : 1 + ], + function(err) { + callback(err, { created: true, id: this.lastID }); + } + ); + } + } + ); + } + }; +}; \ No newline at end of file diff --git a/public/doc-auth.js b/public/doc-auth.js deleted file mode 100644 index 55e2f67..0000000 --- a/public/doc-auth.js +++ /dev/null @@ -1,158 +0,0 @@ -// auth.js - Аутентификация и авторизация -let currentUser = null; - -async function checkAuth() { - try { - const response = await fetch('/api/user'); - if (response.ok) { - const data = await response.json(); - currentUser = data.user; - showMainInterface(); - } else { - showLoginInterface(); - } - } catch (error) { - showLoginInterface(); - } -} - -function showLoginInterface() { - document.getElementById('login-modal').style.display = 'block'; - document.querySelector('.container').style.display = 'none'; -} - -function showMainInterface() { - document.getElementById('login-modal').style.display = 'none'; - document.querySelector('.container').style.display = 'block'; - - let userInfo = `Вы вошли как: ${currentUser.name}`; - if (currentUser.auth_type === 'ldap') { - userInfo += ` (LDAP)`; - } - - // Показываем только группы, которые влияют на роль - if (currentUser.groups && currentUser.groups.length > 0) { - // Получаем все группы ролей из конфигурации - const roleGroups = []; - - // Администраторы - if (window.ALLOWED_GROUPS) { - roleGroups.push(...window.ALLOWED_GROUPS.split(',').map(g => g.trim())); - } - - // Секретари - if (window.SECRETARY_GROUPS) { - roleGroups.push(...window.SECRETARY_GROUPS.split(',').map(g => g.trim())); - } - - // Группа помощи - if (window.HELP_GROUPS) { - roleGroups.push(...window.HELP_GROUPS.split(',').map(g => g.trim())); - } - - // IT поддержка - if (window.ITHELP_GROUPS) { - roleGroups.push(...window.ITHELP_GROUPS.split(',').map(g => g.trim())); - } - - // Заявки - if (window.REQUEST_GROUPS) { - roleGroups.push(...window.REQUEST_GROUPS.split(',').map(g => g.trim())); - } - - // Задачи - if (window.TASKS_GROUPS) { - roleGroups.push(...window.TASKS_GROUPS.split(',').map(g => g.trim())); - } - - // Фильтруем группы пользователя, оставляя только те, что влияют на роль - const relevantGroups = currentUser.groups.filter(group => - roleGroups.includes(group) - ); - - // Также всегда показываем роль пользователя - if (currentUser.role) { - userInfo += ` | Роль: ${getRoleDisplayName(currentUser.role)}`; - } - - // Показываем группы только если есть релевантные - if (relevantGroups.length > 0) { - userInfo += ` | Группы ролей: ${relevantGroups.join(', ')}`; - } - } - - document.getElementById('current-user').textContent = userInfo; - - document.getElementById('tasks-controls').style.display = 'block'; - - const showDeletedLabel = document.querySelector('.show-deleted-label'); - if (showDeletedLabel) { - if (currentUser.role === 'admin') { - showDeletedLabel.style.display = 'flex'; - } else { - showDeletedLabel.style.display = 'none'; - } - } - - loadUsers(); - loadTasks(); - loadActivityLogs(); - showSection('tasks'); - - showingTasksWithoutDate = false; - const btn = document.getElementById('tasks-no-date-btn'); - if (btn) btn.classList.remove('active'); -} - -// Вспомогательная функция для отображения понятного имени роли -function getRoleDisplayName(role) { - const roleNames = { - 'admin': 'Администратор', - 'secretary': 'Секретарь', - 'help': 'Помощь', - 'ithelp': 'IT поддержка', - 'request': 'Заявки', - 'tasks': 'Адмиинистрация', - 'teacher': 'Учитель' - }; - return roleNames[role] || role; -} - -async function login(event) { - event.preventDefault(); - - const login = document.getElementById('login').value; - const password = document.getElementById('password').value; - - try { - const response = await fetch('/api/login', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ login, password }) - }); - - if (response.ok) { - const data = await response.json(); - currentUser = data.user; - showMainInterface(); - } else { - const error = await response.json(); - alert(error.error || 'Ошибка входа'); - } - } catch (error) { - console.error('Ошибка:', error); - alert('Ошибка подключения к серверу'); - } -} - -async function logout() { - try { - await fetch('/api/logout', { method: 'POST' }); - currentUser = null; - showLoginInterface(); - } catch (error) { - console.error('Ошибка выхода:', error); - } -} \ No newline at end of file diff --git a/public/doc-tasks.js b/public/doc-tasks.js deleted file mode 100644 index e3bd3fa..0000000 --- a/public/doc-tasks.js +++ /dev/null @@ -1,558 +0,0 @@ -// tasks.js - Основные операции с задачами -let tasks = []; -let expandedTasks = new Set(); -let showingTasksWithoutDate = false; - -async function loadTasks() { - try { - showingTasksWithoutDate = false; - const btn = document.getElementById('tasks-no-date-btn'); - if (btn) btn.classList.remove('active'); - - const search = document.getElementById('search-tasks')?.value || ''; - const statusFilter = document.getElementById('status-filter')?.value || 'active,in_progress,assigned,overdue,rework'; - const creatorFilter = document.getElementById('creator-filter')?.value || ''; - const assigneeFilter = document.getElementById('assignee-filter')?.value || ''; - const deadlineFilter = document.getElementById('deadline-filter')?.value || ''; - const showDeleted = document.getElementById('show-deleted')?.checked || false; - - let url = '/api/tasks?'; - if (search) url += `search=${encodeURIComponent(search)}&`; - if (statusFilter) url += `status=${encodeURIComponent(statusFilter)}&`; - if (creatorFilter) url += `creator=${encodeURIComponent(creatorFilter)}&`; - if (assigneeFilter) url += `assignee=${encodeURIComponent(assigneeFilter)}&`; - if (deadlineFilter) url += `deadline=${encodeURIComponent(deadlineFilter)}&`; - if (showDeleted) url += `showDeleted=true&`; - - const response = await fetch(url); - tasks = await response.json(); - - // Загружаем файлы для всех задач - await Promise.all(tasks.map(async (task) => { - try { - const filesResponse = await fetch(`/api/tasks/${task.id}/files`); - if (filesResponse.ok) { - task.files = await filesResponse.json(); - } else { - task.files = []; - } - } catch (error) { - console.error(`Ошибка загрузки файлов для задачи ${task.id}:`, error); - task.files = []; - } - })); - - renderTasks(); - - } catch (error) { - console.error('Ошибка загрузки задач:', error); - } -} - -function showTasksWithoutDate() { - showingTasksWithoutDate = true; - const btn = document.getElementById('tasks-no-date-btn'); - if (btn) btn.classList.add('active'); - loadTasksWithoutDate(); -} - -async function loadTasksWithoutDate() { - try { - const response = await fetch('/api/tasks'); - if (!response.ok) throw new Error('Ошибка загрузки задач'); - - const allTasks = await response.json(); - tasks = allTasks.filter(task => { - const hasTaskDueDate = !task.due_date; - const hasAssignmentDueDates = task.assignments && - task.assignments.every(assignment => !assignment.due_date); - return hasTaskDueDate && hasAssignmentDueDates; - }); - - // Загружаем файлы для всех задач - await Promise.all(tasks.map(async (task) => { - try { - const filesResponse = await fetch(`/api/tasks/${task.id}/files`); - if (filesResponse.ok) { - task.files = await filesResponse.json(); - } else { - task.files = []; - } - } catch (error) { - console.error(`Ошибка загрузки файлов для задачи ${task.id}:`, error); - task.files = []; - } - })); - - renderTasks(); - } catch (error) { - console.error('Ошибка загрузки задач без срока:', error); - } -} - -async function createTask(event) { - event.preventDefault(); - - if (!currentUser) { - alert('Требуется аутентификация'); - return; - } - - const formData = new FormData(); - formData.append('title', document.getElementById('title').value); - formData.append('description', document.getElementById('description').value); - - const dueDate = document.getElementById('due-date').value; - if (!dueDate) { - alert('Дата и время выполнения обязательны'); - return; - } - formData.append('dueDate', dueDate); - - // Используем selectedUsers вместо прямого доступа к DOM - if (selectedUsers.length === 0) { - alert('Выберите хотя бы одного исполнителя'); - return; - } - selectedUsers.forEach(userId => { - formData.append('assignedUsers', userId); - }); - - const files = document.getElementById('files').files; - for (let i = 0; i < files.length; i++) { - formData.append('files', files[i]); - } - - try { - const response = await fetch('/api/tasks', { - method: 'POST', - body: formData - }); - - if (response.ok) { - alert('Задача успешно создана!'); - document.getElementById('create-task-form').reset(); - document.getElementById('file-list').innerHTML = ''; - document.getElementById('user-search').value = ''; - selectedUsers = []; - renderUsersChecklist(); - loadTasks(); - loadActivityLogs(); - showSection('tasks'); - } else { - const error = await response.json(); - alert(error.error || 'Ошибка создания задачи'); - } - } catch (error) { - console.error('Ошибка:', error); - alert('Ошибка создания задачи'); - } -} - -async function openEditModal(taskId) { - try { - const response = await fetch(`/api/tasks/${taskId}`); - if (!response.ok) { - if (response.status === 404) { - alert('Задача не найдена или у вас нет прав доступа'); - } - throw new Error('Ошибка загрузки задачи'); - } - - const task = await response.json(); - - if (!canUserEditTask(task)) { - alert('У вас нет прав для редактирования этой задачи'); - return; - } - - document.getElementById('edit-task-id').value = task.id; - document.getElementById('edit-title').value = task.title; - document.getElementById('edit-description').value = task.description || ''; - - document.getElementById('edit-due-date').value = task.due_date ? formatDateTimeForInput(task.due_date) : ''; - - // Устанавливаем выбранных пользователей - editSelectedUsers = task.assignments ? task.assignments.map(a => a.user_id) : []; - renderEditUsersChecklist(users); - - // Показываем существующие файлы - currentEditTaskFiles = task.files || []; - updateEditFileList(); - - document.getElementById('edit-task-modal').style.display = 'block'; - } catch (error) { - console.error('Ошибка:', error); - alert('Ошибка загрузки задачи'); - } -} - -function closeEditModal() { - document.getElementById('edit-task-modal').style.display = 'none'; - document.getElementById('edit-file-list').innerHTML = ''; - document.getElementById('edit-user-search').value = ''; - editSelectedUsers = []; - currentEditTaskFiles = []; - filterEditUsers(); -} - -async function updateTask(event) { - event.preventDefault(); - - const taskId = document.getElementById('edit-task-id').value; - const title = document.getElementById('edit-title').value; - const description = document.getElementById('edit-description').value; - const dueDate = document.getElementById('edit-due-date').value; - - if (!dueDate) { - alert('Дата и время выполнения обязательны'); - return; - } - - // Используем editSelectedUsers - const assignedUserIds = editSelectedUsers; - - const formData = new FormData(); - formData.append('title', title); - formData.append('description', description); - formData.append('assignedUsers', JSON.stringify(assignedUserIds)); - formData.append('dueDate', dueDate); - - const files = document.getElementById('edit-files').files; - for (let i = 0; i < files.length; i++) { - formData.append('files', files[i]); - } - - try { - const response = await fetch(`/api/tasks/${taskId}`, { - method: 'PUT', - body: formData - }); - - if (response.ok) { - alert('Задача успешно обновлена!'); - closeEditModal(); - loadTasks(); - loadActivityLogs(); - } else { - const error = await response.json(); - alert(error.error || 'Ошибка обновления задачи'); - } - } catch (error) { - console.error('Ошибка:', error); - alert('Ошибка обновления задачи'); - } -} - -function openCopyModal(taskId) { - document.getElementById('copy-task-id').value = taskId; - - // Устанавливаем дату по умолчанию (через 7 дней) - const defaultDate = new Date(); - defaultDate.setDate(defaultDate.getDate() + 7); - document.getElementById('copy-due-date').value = defaultDate.toISOString().substring(0, 16); - - // Сбрасываем выбранных пользователей - copySelectedUsers = []; - renderCopyUsersChecklist(users); - - document.getElementById('copy-task-modal').style.display = 'block'; -} - -function closeCopyModal() { - document.getElementById('copy-task-modal').style.display = 'none'; - document.getElementById('copy-user-search').value = ''; - copySelectedUsers = []; - filterCopyUsers(); -} - -async function copyTask(event) { - event.preventDefault(); - - const taskId = document.getElementById('copy-task-id').value; - const dueDate = document.getElementById('copy-due-date').value; - - if (!dueDate) { - alert('Дата и время выполнения обязательны для копии задачи'); - return; - } - - // Используем copySelectedUsers - const assignedUserIds = copySelectedUsers; - - if (assignedUserIds.length === 0) { - alert('Выберите хотя бы одного исполнителя для копии задачи'); - return; - } - - try { - const response = await fetch(`/api/tasks/${taskId}/copy`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - assignedUsers: assignedUserIds, - dueDate: dueDate - }) - }); - - if (response.ok) { - alert('Копия задачи успешно создана!'); - closeCopyModal(); - loadTasks(); - loadActivityLogs(); - } else { - const error = await response.json(); - alert(error.error || 'Ошибка создания копии задачи'); - } - } catch (error) { - console.error('Ошибка:', error); - alert('Ошибка создания копии задачи'); - } -} - -async function closeTask(taskId) { - if (!confirm('Вы уверены, что хотите закрыть эту задачу? Исполнители больше не будут видеть её.')) { - return; - } - - try { - const response = await fetch(`/api/tasks/${taskId}/close`, { - method: 'POST' - }); - - if (response.ok) { - alert('Задача закрыта!'); - loadTasks(); - loadActivityLogs(); - } else { - const error = await response.json(); - alert(error.error || 'Ошибка закрытия задачи'); - } - } catch (error) { - console.error('Ошибка:', error); - alert('Ошибка закрытия задачи'); - } -} - -async function reopenTask(taskId) { - try { - const response = await fetch(`/api/tasks/${taskId}/reopen`, { - method: 'POST' - }); - - if (response.ok) { - alert('Задача открыта!'); - loadTasks(); - loadActivityLogs(); - } else { - const error = await response.json(); - alert(error.error || 'Ошибка открытия задачи'); - } - } catch (error) { - console.error('Ошибка:', error); - alert('Ошибка открытия задачи'); - } -} - -async function deleteTask(taskId) { - if (!confirm('Вы уверены, что хотите удалить эту задачу?')) { - return; - } - - try { - const response = await fetch(`/api/tasks/${taskId}`, { - method: 'DELETE' - }); - - if (response.ok) { - alert('Задача удалена!'); - loadTasks(); - loadActivityLogs(); - } else { - const error = await response.json(); - alert(error.error || 'Ошибка удаления задачи'); - } - } catch (error) { - console.error('Ошибка:', error); - alert('Ошибка удаления задачи'); - } -} - -async function restoreTask(taskId) { - try { - const response = await fetch(`/api/tasks/${taskId}/restore`, { - method: 'POST' - }); - - if (response.ok) { - alert('Задача восстановлена!'); - loadTasks(); - loadActivityLogs(); - } else { - const error = await response.json(); - alert(error.error || 'Ошибка восстановления задачи'); - } - } catch (error) { - console.error('Ошибка:', error); - alert('Ошибка восстановления задачи'); - } -} - -function openEditAssignmentModal(taskId, userId) { - const task = tasks.find(t => t.id === taskId); - if (!task) return; - - const assignment = task.assignments.find(a => a.user_id === userId); - if (!assignment) return; - - document.getElementById('edit-assignment-task-id').value = taskId; - document.getElementById('edit-assignment-user-id').value = userId; - document.getElementById('edit-assignment-due-date').value = assignment.due_date ? formatDateTimeForInput(assignment.due_date) : ''; - - document.getElementById('edit-assignment-modal').style.display = 'block'; -} - -function closeEditAssignmentModal() { - document.getElementById('edit-assignment-modal').style.display = 'none'; -} - -async function updateAssignment(event) { - event.preventDefault(); - - const taskId = document.getElementById('edit-assignment-task-id').value; - const userId = document.getElementById('edit-assignment-user-id').value; - const dueDate = document.getElementById('edit-assignment-due-date').value; - - if (!dueDate) { - alert('Дата и время выполнения обязательны'); - return; - } - - try { - const response = await fetch(`/api/tasks/${taskId}/assignment/${userId}`, { - method: 'PUT', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - dueDate: dueDate - }) - }); - - if (response.ok) { - alert('Сроки исполнителя обновлены!'); - closeEditAssignmentModal(); - loadTasks(); - loadActivityLogs(); - } else { - const error = await response.json(); - alert(error.error || 'Ошибка обновления сроков'); - } - } catch (error) { - console.error('Ошибка:', error); - alert('Ошибка обновления сроков'); - } -} - -function openReworkModal(taskId) { - document.getElementById('rework-task-id').value = taskId; - document.getElementById('rework-task-modal').style.display = 'block'; -} - -function closeReworkModal() { - document.getElementById('rework-task-modal').style.display = 'none'; - document.getElementById('rework-comment').value = ''; -} - -async function sendForRework(event) { - event.preventDefault(); - - const taskId = document.getElementById('rework-task-id').value; - const comment = document.getElementById('rework-comment').value; - - try { - const response = await fetch(`/api/tasks/${taskId}/rework`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ comment }) - }); - - if (response.ok) { - alert('Задача возвращена на доработку!'); - closeReworkModal(); - loadTasks(); - loadActivityLogs(); - } else { - const error = await response.json(); - alert(error.error || 'Ошибка возврата задачи на доработку'); - } - } catch (error) { - console.error('Ошибка:', error); - alert('Ошибка возврата задачи на доработку'); - } -} - -async function updateStatus(taskId, userId, status) { - try { - const response = await fetch(`/api/tasks/${taskId}/status`, { - method: 'PUT', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ userId, status }) - }); - - if (response.ok) { - loadTasks(); - loadActivityLogs(); - } else { - const error = await response.json(); - alert(error.error || 'Ошибка обновления статуса'); - } - } catch (error) { - console.error('Ошибка:', error); - alert('Ошибка обновления статуса'); - } -} - -function canUserEditTask(task) { - if (!currentUser) return false; - - // Администратор может всё - if (currentUser.role === 'admin') return true; - - // Создатель может редактировать свою задачу - if (parseInt(task.created_by) === currentUser.id) { - // Но если задача уже назначена другим пользователям, - // создатель может только просматривать - if (task.assignments && task.assignments.length > 0) { - // Проверяем, назначена ли задача другим пользователям (не только себе) - const assignedToOthers = task.assignments.some(assignment => - parseInt(assignment.user_id) !== currentUser.id - ); - - if (assignedToOthers) { - // Создатель может только просматривать и закрывать задачу - return false; - } - } - return true; - } - - // Исполнитель может менять только свой статус - if (task.assignments) { - const isExecutor = task.assignments.some(assignment => - parseInt(assignment.user_id) === currentUser.id - ); - if (isExecutor) { - // Исполнитель может менять только статус - return false; - } - } - - return false; -} \ No newline at end of file diff --git a/public/doc-users.js b/public/doc-users.js deleted file mode 100644 index 61186da..0000000 --- a/public/doc-users.js +++ /dev/null @@ -1,260 +0,0 @@ -// users.js - Управление пользователями -let users = []; -let allUsers = []; -let filteredUsers = []; -let selectedUsers = []; -let editSelectedUsers = []; -let copySelectedUsers = []; - -async function loadUsers() { - try { - const response = await fetch('/api/users'); - const allUsersData = await response.json(); - //users = await response.json(); - // Сохраняем всех пользователей - allUsers = allUsersData; - // Фильтруем пользователей в зависимости от прав текущего пользователя - users = filterAssignableUsers(allUsersData); - filteredUsers = [...users]; - renderUsersChecklist(); - renderEditUsersChecklist(); - renderCopyUsersChecklist(); - populateFilterDropdowns(); - } catch (error) { - console.error('Ошибка загрузки пользователей:', error); - } -} -function filterAssignableUsers(allUsers) { - if (!currentUser) return []; - - // Администратор видит всех пользователей - if (currentUser.role === 'admin') { - return allUsers.filter(user => user.id !== currentUser.id); - } - if (currentUser.role === 'secretary') { - return allUsers.filter(user => user.id !== currentUser.id); - } - if (currentUser.role === 'ithelp') { - return allUsers.filter(user => - (user.role === 'teacher' || user.role === 'tasks' || user.role === 'help' || user.role === 'request' || user.role === 'ithelp') && - user.id !== currentUser.id - ); - } - if (currentUser.role === 'request') { - return allUsers.filter(user => - (user.role === 'teacher' || user.role === 'tasks' || user.role === 'help' || user.role === 'request' || user.role === 'ithelp') && - user.id !== currentUser.id - ); - } - // tasks видит учителей и других tasks - if (currentUser.role === 'help') { - return allUsers.filter(user => - (user.role === 'teacher' || user.role === 'tasks' || user.role === 'help' || user.role === 'request' || user.role === 'ithelp') && - user.id !== currentUser.id - ); - } - // tasks видит учителей и других tasks - if (currentUser.role === 'tasks') { - return allUsers.filter(user => - (user.role === 'teacher' || user.role === 'tasks' || user.role === 'help' || user.role === 'request' || user.role === 'ithelp') && - user.id !== currentUser.id - ); - } - // Учитель видит только учителей - if (currentUser.role === 'teacher') { - return allUsers.filter(user => - (user.role === 'help' || user.role === 'request' || user.role === 'ithelp') && - user.id !== currentUser.id - ); - } - // Проверяем группы пользователя для определения прав - const userGroups = currentUser.groups || []; - - // Загружаем конфигурацию групп из настроек - // (предполагается, что эти переменные определены в глобальной области) - const allowedGroups = getGroupsForCurrentUser(); - - // Если у пользователя нет специальных групп, возвращаем пустой массив - if (allowedGroups.length === 0) { - return []; - } - - // Фильтруем пользователей по группам - return allUsers.filter(user => { - // Пользователь не может назначать задачи себе - if (user.id === currentUser.id) return false; - - // Если у пользователя есть группы, проверяем пересечение - if (user.groups && Array.isArray(user.groups)) { - return user.groups.some(group => allowedGroups.includes(group)); - } - - return false; - }); -} - -// Функция для получения групп, которым текущий пользователь может назначать задачи -function getGroupsForCurrentUser() { - const allowedGroups = []; - const userGroups = currentUser.groups || []; - - // Определяем, какие группы доступны для назначения - // На основе ролей и групп текущего пользователя - - // Пример: пользователи с ролью 'secretary' могут назначать задачи группам 'teachers' - if (currentUser.role === 'secretary') { - allowedGroups.push('teachers', 'staff'); - } - - // Пример: пользователи из группы 'department_head' могут назначать своей группе - if (userGroups.includes('department_head')) { - allowedGroups.push('department_head', 'teachers'); - } - - // Пример: для помощи (help) можно назначать всем - if (currentUser.role === 'help') { - // Можно указать конкретные группы или 'all' для всех - allowedGroups.push('teachers', 'staff', 'administration'); - } - - // Пример: для IT поддержки - if (currentUser.role === 'ithelp') { - allowedGroups.push('teachers', 'staff', 'administration', 'it_department'); - } - - // Пример: пользователи с ролью 'request' могут создавать заявки для всех - if (currentUser.role === 'request') { - allowedGroups.push('all'); // Специальное значение "все" - } - - // Пример: пользователи с ролью 'tasks' (задачи) могут назначать учителям - if (currentUser.role === 'tasks') { - allowedGroups.push('teachers'); - } - - // Если массив содержит 'all', возвращаем специальный маркер - if (allowedGroups.includes('all')) { - return ['all']; // Это будет обрабатываться в фильтрации - } - - return [...new Set(allowedGroups)]; // Убираем дубликаты -} -function populateFilterDropdowns() { - const creatorFilter = document.getElementById('creator-filter'); - const assigneeFilter = document.getElementById('assignee-filter'); - - creatorFilter.innerHTML = ''; - assigneeFilter.innerHTML = ''; - - users.forEach(user => { - const creatorOption = document.createElement('option'); - creatorOption.value = user.id; - creatorOption.textContent = `${user.name} (${user.login})`; - creatorFilter.appendChild(creatorOption.cloneNode(true)); - - const assigneeOption = creatorOption.cloneNode(true); - assigneeFilter.appendChild(assigneeOption); - }); -} - -function filterUsers() { - const search = document.getElementById('user-search').value.toLowerCase(); - filteredUsers = users.filter(user => - user.name.toLowerCase().includes(search) || - user.login.toLowerCase().includes(search) || - user.email.toLowerCase().includes(search) - ); - renderUsersChecklist(); -} - -function filterEditUsers() { - const search = document.getElementById('edit-user-search').value.toLowerCase(); - const filtered = users.filter(user => - user.name.toLowerCase().includes(search) || - user.login.toLowerCase().includes(search) || - user.email.toLowerCase().includes(search) - ); - renderEditUsersChecklist(filtered); -} - -function filterCopyUsers() { - const search = document.getElementById('copy-user-search').value.toLowerCase(); - const filtered = users.filter(user => - user.name.toLowerCase().includes(search) || - user.login.toLowerCase().includes(search) || - user.email.toLowerCase().includes(search) - ); - renderCopyUsersChecklist(filtered); -} - -function renderUsersChecklist() { - const container = document.getElementById('users-checklist'); - container.innerHTML = filteredUsers - .filter(user => user.id !== currentUser.id) - .map(user => ` -
Просмотр и фильтрация данных таблиц SQLite
-База данных: SQLite
-Таблицы: users, tasks, files, activity_logs, assignments
-Общий размер: ${(totalSize / 1024 / 1024).toFixed(2)} MB / 300 MB
`; - - fileList.innerHTML = html; -} - -// Функции для работы с документами -async function loadMyDocuments() { - try { - const response = await fetch('/api/documents/my'); - const documents = await response.json(); - renderMyDocuments(documents); - } catch (error) { - console.error('Ошибка загрузки документов:', error); - } -} - -async function loadSecretaryDocuments() { - try { - const response = await fetch('/api/documents/secretary'); - const documents = await response.json(); - renderSecretaryDocuments(documents); - } catch (error) { - console.error('Ошибка загрузки документов секретаря:', error); - } -} - -function renderMyDocuments(documents) { - console.log('📄 Рендеринг документов:', documents); - - const container = document.getElementById('my-docs-list'); - - if (!documents || !Array.isArray(documents)) { - console.error('❌ documents не является массивом:', documents); - container.innerHTML = ` -Ошибка загрузки документов
-У вас нет документов для согласования
- -${doc.description}
-Ошибка загрузки документов: ${error.message}
-Тип: ${doc.document_type_name || 'Не указан'}
-Номер: ${doc.document_number || 'Не указан'}
-Дата документа: ${doc.document_date ? formatDate(doc.document_date) : 'Не указана'}
-Количество страниц: ${doc.pages_count || 'Не указано'}
- ${doc.comment ? `Комментарий автора: ${doc.comment}
` : ''} -Причина отказа: ${doc.refusal_reason}
- ` : ''} -
${doc.comment}
-