// Глобальные переменные let currentUser = null; let isAdmin = false; let currentTab = 'groups'; let groups = []; let idusers = []; let allUsers = []; let currentPage = { groups: 1, idusers: 1 }; const itemsPerPage = 20; let deleteCallback = null; let deleteParams = null; // Инициализация при загрузке страницы document.addEventListener('DOMContentLoaded', function() { checkAuth(); setupEventListeners(); }); // Проверка аутентификации async function checkAuth() { try { const response = await fetch('/api/user'); if (!response.ok) { window.location.href = '/'; return; } const data = await response.json(); if (data.user) { currentUser = data.user; isAdmin = currentUser.role === 'admin'; document.getElementById('userName').textContent = currentUser.name; document.getElementById('userRole').textContent = `Роль: ${currentUser.role}`; // Загружаем начальные данные loadGroups(); loadAllUsers(); loadIdUsers(); loadStats(); } else { window.location.href = '/'; } } catch (error) { console.error('Ошибка проверки аутентификации:', error); window.location.href = '/'; } } // Настройка обработчиков событий function setupEventListeners() { // Переключение вкладок document.querySelectorAll('.tab').forEach(tab => { tab.addEventListener('click', function() { const tabId = this.getAttribute('data-tab'); switchTab(tabId); }); }); // Поиск в таблице групп document.getElementById('groupSearch').addEventListener('input', debounce(function() { filterGroups(); }, 300)); // Фильтры групп document.getElementById('groupServiceTypeFilter').addEventListener('change', filterGroups); document.getElementById('groupStatusFilter').addEventListener('change', filterGroups); // Поиск в таблице идентификаторов document.getElementById('iduserSearch').addEventListener('input', debounce(function() { filterIdUsers(); }, 300)); // Фильтры идентификаторов document.getElementById('iduserServiceTypeFilter').addEventListener('change', filterIdUsers); document.getElementById('iduserGroupFilter').addEventListener('change', filterIdUsers); document.getElementById('iduserStatusFilter').addEventListener('change', filterIdUsers); } // Дебаунс для поиска function debounce(func, wait) { let timeout; return function executedFunction(...args) { const later = () => { clearTimeout(timeout); func(...args); }; clearTimeout(timeout); timeout = setTimeout(later, wait); }; } // Переключение вкладок function switchTab(tabId) { currentTab = tabId; // Обновляем активные вкладки document.querySelectorAll('.tab').forEach(tab => { tab.classList.remove('active'); if (tab.getAttribute('data-tab') === tabId) { tab.classList.add('active'); } }); // Показываем активный контент document.querySelectorAll('.tab-content').forEach(content => { content.classList.remove('active'); if (content.id === `tab-${tabId}`) { content.classList.add('active'); } }); } // Загрузка всех пользователей async function loadAllUsers() { try { const response = await fetch('/api/users'); if (!response.ok) throw new Error('Ошибка загрузки пользователей'); allUsers = await response.json(); // Заполняем select в форме const select = document.getElementById('iduserUserId'); select.innerHTML = ''; allUsers.forEach(user => { const option = document.createElement('option'); option.value = user.id; option.textContent = `${user.name} (${user.login})`; select.appendChild(option); }); } catch (error) { console.error('Ошибка загрузки пользователей:', error); } } // Загрузка групп async function loadGroups() { try { const tableBody = document.getElementById('groupsTableBody'); tableBody.innerHTML = ' Загрузка групп...'; const response = await fetch('/api2/groups'); if (!response.ok) throw new Error('Ошибка загрузки групп'); groups = await response.json(); // Заполняем фильтр групп для идентификаторов const groupFilter = document.getElementById('iduserGroupFilter'); const currentValue = groupFilter.value; groupFilter.innerHTML = ''; groups.forEach(group => { const option = document.createElement('option'); option.value = group.id; option.textContent = group.name; groupFilter.appendChild(option); }); if (currentValue) { groupFilter.value = currentValue; } renderGroups(); } catch (error) { console.error('Ошибка загрузки групп:', error); document.getElementById('groupsTableBody').innerHTML = 'Ошибка загрузки групп: ' + error.message + ''; } } // Отображение групп function renderGroups(filteredGroups = null) { const groupsToRender = filteredGroups || groups; const tableBody = document.getElementById('groupsTableBody'); if (groupsToRender.length === 0) { tableBody.innerHTML = 'Нет данных для отображения'; document.getElementById('groupsPagination').innerHTML = ''; return; } // Пагинация const totalPages = Math.ceil(groupsToRender.length / itemsPerPage); const startIndex = (currentPage.groups - 1) * itemsPerPage; const endIndex = startIndex + itemsPerPage; const pageGroups = groupsToRender.slice(startIndex, endIndex); // Очищаем таблицу tableBody.innerHTML = ''; // Заполняем таблицу pageGroups.forEach(group => { const row = document.createElement('tr'); // Бейдж для типа сервиса let serviceTypeBadge = ''; switch(group.service_type) { case 'sberbank': serviceTypeBadge = 'Сбербанк'; break; case 'yandex': serviceTypeBadge = 'Яндекс'; break; case 'ldap': serviceTypeBadge = 'LDAP'; break; default: serviceTypeBadge = 'Прочие'; } row.innerHTML = ` ${group.id} ${group.name} ${serviceTypeBadge} ${group.description || '-'} ${group.is_active ? 'Активна' : 'Неактивна'} ${formatDate(group.created_at)} ${formatDate(group.updated_at)} ${isAdmin ? ` ` : ''} `; tableBody.appendChild(row); }); // Пагинация renderPagination('groups', totalPages); } // Фильтрация групп function filterGroups() { const searchText = document.getElementById('groupSearch').value.toLowerCase(); const serviceType = document.getElementById('groupServiceTypeFilter').value; const statusFilter = document.getElementById('groupStatusFilter').value; const filtered = groups.filter(group => { // Поиск по тексту const matchesSearch = !searchText || group.name.toLowerCase().includes(searchText) || (group.description && group.description.toLowerCase().includes(searchText)); // Фильтр по типу сервиса const matchesServiceType = !serviceType || group.service_type === serviceType; // Фильтр по статусу const matchesStatus = !statusFilter || (statusFilter === 'true' ? group.is_active : !group.is_active); return matchesSearch && matchesServiceType && matchesStatus; }); currentPage.groups = 1; renderGroups(filtered); } // Загрузка идентификаторов пользователей async function loadIdUsers() { try { const tableBody = document.getElementById('idusersTableBody'); tableBody.innerHTML = ' Загрузка идентификаторов...'; const response = await fetch('/api2/idusers'); if (!response.ok) throw new Error('Ошибка загрузки идентификаторов'); idusers = await response.json(); renderIdUsers(); } catch (error) { console.error('Ошибка загрузки идентификаторов:', error); document.getElementById('idusersTableBody').innerHTML = 'Ошибка загрузки идентификаторов: ' + error.message + ''; } } // Отображение идентификаторов пользователей function renderIdUsers(filteredIdUsers = null) { const idusersToRender = filteredIdUsers || idusers; const tableBody = document.getElementById('idusersTableBody'); if (idusersToRender.length === 0) { tableBody.innerHTML = 'Нет данных для отображения'; document.getElementById('idusersPagination').innerHTML = ''; return; } // Пагинация const totalPages = Math.ceil(idusersToRender.length / itemsPerPage); const startIndex = (currentPage.idusers - 1) * itemsPerPage; const endIndex = startIndex + itemsPerPage; const pageIdUsers = idusersToRender.slice(startIndex, endIndex); // Очищаем таблицу tableBody.innerHTML = ''; // Заполняем таблицу pageIdUsers.forEach(iduser => { const row = document.createElement('tr'); // Бейдж для типа сервиса let serviceTypeBadge = ''; switch(iduser.service_type) { case 'sberbank': serviceTypeBadge = 'Сбербанк'; break; case 'yandex': serviceTypeBadge = 'Яндекс'; break; case 'ldap': serviceTypeBadge = 'LDAP'; break; default: serviceTypeBadge = 'Прочие'; } // Превью метаданных let metadataPreview = '-'; if (iduser.metadata && Object.keys(iduser.metadata).length > 0) { metadataPreview = `
${Object.keys(iduser.metadata).length} поле(й)
`; } row.innerHTML = ` ${iduser.id} ${iduser.user_name || 'Неизвестно'}
${iduser.user_login || '-'} ${serviceTypeBadge} ${iduser.external_id} ${iduser.login || '-'} ${iduser.ldap_group || '-'} ${iduser.group_name || '-'} ${metadataPreview} ${iduser.is_active ? 'Активен' : 'Неактивен'} ${formatDate(iduser.created_at)} ${isAdmin ? ` ` : ''} `; tableBody.appendChild(row); }); // Пагинация renderPagination('idusers', totalPages); } // Фильтрация идентификаторов пользователей function filterIdUsers() { const searchText = document.getElementById('iduserSearch').value.toLowerCase(); const serviceType = document.getElementById('iduserServiceTypeFilter').value; const groupId = document.getElementById('iduserGroupFilter').value; const statusFilter = document.getElementById('iduserStatusFilter').value; const filtered = idusers.filter(iduser => { // Поиск по тексту const matchesSearch = !searchText || iduser.external_id.toLowerCase().includes(searchText) || (iduser.login && iduser.login.toLowerCase().includes(searchText)) || (iduser.user_name && iduser.user_name.toLowerCase().includes(searchText)) || (iduser.user_login && iduser.user_login.toLowerCase().includes(searchText)); // Фильтр по типу сервиса const matchesServiceType = !serviceType || iduser.service_type === serviceType; // Фильтр по группе const matchesGroup = !groupId || iduser.group_id == groupId; // Фильтр по статусу const matchesStatus = !statusFilter || (statusFilter === 'true' ? iduser.is_active : !iduser.is_active); return matchesSearch && matchesServiceType && matchesGroup && matchesStatus; }); currentPage.idusers = 1; renderIdUsers(filtered); } // Загрузка статистики async function loadStats() { try { const statsGrid = document.getElementById('statsGrid'); statsGrid.innerHTML = '
Загрузка статистики...
'; const response = await fetch('/api2/idusers/stats'); if (!response.ok) throw new Error('Ошибка загрузки статистики'); const stats = await response.json(); renderStats(stats); } catch (error) { console.error('Ошибка загрузки статистики:', error); document.getElementById('statsGrid').innerHTML = '
Ошибка загрузки статистики: ' + error.message + '
'; } } // Отображение статистики function renderStats(stats) { const statsGrid = document.getElementById('statsGrid'); const tableBody = document.getElementById('statsTableBody'); // Статистические карточки statsGrid.innerHTML = `
Всего идентификаторов
${stats.totals.total_identifiers || 0}
Уникальных пользователей
${stats.totals.total_users || 0}
Активных идентификаторов
${stats.totals.total_active || 0}
Обновлено
${formatDate(stats.timestamp, 'time')}
`; // Таблица по типам сервисов tableBody.innerHTML = ''; if (stats.by_service_type && stats.by_service_type.length > 0) { const total = stats.totals.total_identifiers || 1; stats.by_service_type.forEach(stat => { const percentage = Math.round((stat.total_count / total) * 100); const row = document.createElement('tr'); row.innerHTML = ` ${getServiceTypeName(stat.service_type)} ${stat.total_count} ${stat.active_count} ${stat.unique_users}
${percentage}% `; tableBody.appendChild(row); }); } else { tableBody.innerHTML = 'Нет данных для отображения'; } } // Модальное окно для добавления группы function showAddGroupModal() { if (!isAdmin) { alert('Недостаточно прав'); return; } document.getElementById('groupModalTitle').textContent = 'Добавить группу'; document.getElementById('groupId').value = ''; document.getElementById('groupForm').reset(); document.getElementById('groupIsActive').checked = true; document.getElementById('groupMessage').innerHTML = ''; document.getElementById('groupModal').classList.add('active'); } // Модальное окно для редактирования группы async function editGroup(id) { if (!isAdmin) { alert('Недостаточно прав'); return; } try { const response = await fetch(`/api2/groups/${id}`); if (!response.ok) throw new Error('Ошибка загрузки группы'); const group = await response.json(); document.getElementById('groupModalTitle').textContent = 'Редактировать группу'; document.getElementById('groupId').value = group.id; document.getElementById('groupName').value = group.name; document.getElementById('groupDescription').value = group.description || ''; document.getElementById('groupServiceType').value = group.service_type; document.getElementById('groupIsActive').checked = group.is_active; document.getElementById('groupMessage').innerHTML = ''; document.getElementById('groupModal').classList.add('active'); } catch (error) { console.error('Ошибка загрузки группы:', error); alert('Ошибка загрузки группы: ' + error.message); } } // Закрытие модального окна группы function closeGroupModal() { document.getElementById('groupModal').classList.remove('active'); } // Сохранение группы async function saveGroup(event) { event.preventDefault(); if (!isAdmin) { alert('Недостаточно прав'); return; } const groupId = document.getElementById('groupId').value; const isEdit = !!groupId; const groupData = { name: document.getElementById('groupName').value, description: document.getElementById('groupDescription').value, service_type: document.getElementById('groupServiceType').value, is_active: document.getElementById('groupIsActive').checked }; try { const url = isEdit ? `/api2/groups/${groupId}` : '/api2/groups'; const method = isEdit ? 'PUT' : 'POST'; const response = await fetch(url, { method: method, headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(groupData) }); if (!response.ok) { const errorData = await response.json(); throw new Error(errorData.error || 'Ошибка сохранения'); } const result = await response.json(); // Показываем сообщение об успехе document.getElementById('groupMessage').innerHTML = `
Группа успешно ${isEdit ? 'обновлена' : 'создана'}!
`; // Обновляем список групп setTimeout(() => { closeGroupModal(); loadGroups(); }, 1500); } catch (error) { console.error('Ошибка сохранения группы:', error); document.getElementById('groupMessage').innerHTML = `
Ошибка: ${error.message}
`; } } // Подтверждение удаления группы function confirmDeleteGroup(id) { if (!isAdmin) { alert('Недостаточно прав'); return; } const group = groups.find(g => g.id == id); if (!group) return; document.getElementById('confirmModalTitle').textContent = 'Удаление группы'; document.getElementById('confirmMessage').textContent = `Вы уверены, что хотите удалить группу "${group.name}"?`; deleteCallback = deleteGroup; deleteParams = { id }; document.getElementById('confirmModal').classList.add('active'); } // Удаление группы async function deleteGroup(params) { try { const response = await fetch(`/api2/groups/${params.id}`, { method: 'DELETE' }); if (!response.ok) { const errorData = await response.json(); throw new Error(errorData.error || 'Ошибка удаления'); } const result = await response.json(); if (result.success) { // Показываем сообщение об успехе alert('Группа успешно удалена'); // Обновляем список групп loadGroups(); } else { throw new Error(result.message || 'Ошибка удаления'); } } catch (error) { console.error('Ошибка удаления группы:', error); alert('Ошибка удаления группы: ' + error.message); } } // Модальное окно для добавления идентификатора function showAddIdUserModal() { if (!isAdmin) { alert('Недостаточно прав'); return; } document.getElementById('iduserModalTitle').textContent = 'Добавить идентификатор'; document.getElementById('iduserId').value = ''; document.getElementById('iduserForm').reset(); document.getElementById('iduserIsActive').checked = true; document.getElementById('iduserMessage').innerHTML = ''; // Обновляем опции групп updateGroupOptions(); document.getElementById('iduserModal').classList.add('active'); } // Модальное окно для редактирования идентификатора async function editIdUser(id) { if (!isAdmin) { alert('Недостаточно прав'); return; } try { const response = await fetch(`/api2/idusers/${id}`); if (!response.ok) throw new Error('Ошибка загрузки идентификатора'); const iduser = await response.json(); document.getElementById('iduserModalTitle').textContent = 'Редактировать идентификатор'; document.getElementById('iduserId').value = iduser.id; document.getElementById('iduserUserId').value = iduser.user_id; document.getElementById('iduserExternalId').value = iduser.external_id; document.getElementById('iduserLogin').value = iduser.login || ''; document.getElementById('iduserLdapGroup').value = iduser.ldap_group || ''; document.getElementById('iduserServiceType').value = iduser.service_type; document.getElementById('iduserIsActive').checked = iduser.is_active; // Устанавливаем метаданные if (iduser.metadata && Object.keys(iduser.metadata).length > 0) { document.getElementById('iduserMetadata').value = JSON.stringify(iduser.metadata, null, 2); } else { document.getElementById('iduserMetadata').value = ''; } // Обновляем опции групп и выбираем текущую updateGroupOptions().then(() => { document.getElementById('iduserGroupId').value = iduser.group_id || ''; }); document.getElementById('iduserMessage').innerHTML = ''; document.getElementById('iduserModal').classList.add('active'); } catch (error) { console.error('Ошибка загрузки идентификатора:', error); alert('Ошибка загрузки идентификатора: ' + error.message); } } // Обновление опций групп в зависимости от типа сервиса async function updateGroupOptions() { const serviceType = document.getElementById('iduserServiceType').value; const groupSelect = document.getElementById('iduserGroupId'); // Загружаем группы, если еще не загружены if (groups.length === 0) { await loadGroups(); } // Фильтруем группы по типу сервиса const filteredGroups = serviceType ? groups.filter(g => g.service_type === serviceType && g.is_active) : groups.filter(g => g.is_active); // Сохраняем текущее значение const currentValue = groupSelect.value; // Обновляем опции groupSelect.innerHTML = ''; filteredGroups.forEach(group => { const option = document.createElement('option'); option.value = group.id; option.textContent = group.name; groupSelect.appendChild(option); }); // Восстанавливаем значение, если оно есть в новых опциях if (currentValue && filteredGroups.some(g => g.id == currentValue)) { groupSelect.value = currentValue; } return Promise.resolve(); } // Закрытие модального окна идентификатора function closeIdUserModal() { document.getElementById('iduserModal').classList.remove('active'); } // Сохранение идентификатора async function saveIdUser(event) { event.preventDefault(); if (!isAdmin) { alert('Недостаточно прав'); return; } const iduserId = document.getElementById('iduserId').value; const isEdit = !!iduserId; // Парсим метаданные let metadata = null; const metadataText = document.getElementById('iduserMetadata').value.trim(); if (metadataText) { try { metadata = JSON.parse(metadataText); } catch (error) { document.getElementById('iduserMessage').innerHTML = `
Ошибка в формате JSON: ${error.message}
`; return; } } const iduserData = { user_id: parseInt(document.getElementById('iduserUserId').value), service_type: document.getElementById('iduserServiceType').value, external_id: document.getElementById('iduserExternalId').value, login: document.getElementById('iduserLogin').value || undefined, ldap_group: document.getElementById('iduserLdapGroup').value || undefined, group_id: document.getElementById('iduserGroupId').value || undefined, metadata: metadata, is_active: document.getElementById('iduserIsActive').checked }; try { const url = isEdit ? `/api2/idusers/${iduserId}` : '/api2/idusers'; const method = isEdit ? 'PUT' : 'POST'; const response = await fetch(url, { method: method, headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(iduserData) }); if (!response.ok) { const errorData = await response.json(); throw new Error(errorData.error || 'Ошибка сохранения'); } const result = await response.json(); // Показываем сообщение об успехе document.getElementById('iduserMessage').innerHTML = `
Идентификатор успешно ${isEdit ? 'обновлен' : 'создан'}!
`; // Обновляем список идентификаторов setTimeout(() => { closeIdUserModal(); loadIdUsers(); }, 1500); } catch (error) { console.error('Ошибка сохранения идентификатора:', error); document.getElementById('iduserMessage').innerHTML = `
Ошибка: ${error.message}
`; } } // Подтверждение удаления идентификатора function confirmDeleteIdUser(id) { if (!isAdmin) { alert('Недостаточно прав'); return; } const iduser = idusers.find(i => i.id == id); if (!iduser) return; document.getElementById('confirmModalTitle').textContent = 'Удаление идентификатора'; document.getElementById('confirmMessage').textContent = `Вы уверены, что хотите удалить идентификатор "${iduser.external_id}" пользователя "${iduser.user_name}"?`; deleteCallback = deleteIdUser; deleteParams = { id }; document.getElementById('confirmModal').classList.add('active'); } // Удаление идентификатора async function deleteIdUser(params) { try { const response = await fetch(`/api2/idusers/${params.id}`, { method: 'DELETE' }); if (!response.ok) { const errorData = await response.json(); throw new Error(errorData.error || 'Ошибка удаления'); } const result = await response.json(); if (result.success) { // Показываем сообщение об успехе alert('Идентификатор успешно удален'); // Обновляем список идентификаторов loadIdUsers(); } else { throw new Error(result.message || 'Ошибка удаления'); } } catch (error) { console.error('Ошибка удаления идентификатора:', error); alert('Ошибка удаления идентификатора: ' + error.message); } } // Пагинация function renderPagination(type, totalPages) { const paginationDiv = document.getElementById(`${type}Pagination`); if (totalPages <= 1) { paginationDiv.innerHTML = ''; return; } let html = ''; // Кнопка "Назад" html += ``; // Номера страниц for (let i = 1; i <= totalPages; i++) { if (i === 1 || i === totalPages || (i >= currentPage[type] - 2 && i <= currentPage[type] + 2)) { html += ``; } else if (i === currentPage[type] - 3 || i === currentPage[type] + 3) { html += `...`; } } // Кнопка "Вперед" html += ``; // Информация о странице html += ` Страница ${currentPage[type]} из ${totalPages} `; paginationDiv.innerHTML = html; } // Изменение страницы function changePage(type, page) { currentPage[type] = page; if (type === 'groups') { filterGroups(); } else if (type === 'idusers') { filterIdUsers(); } } // Подтверждение удаления function confirmDelete() { if (deleteCallback && deleteParams) { deleteCallback(deleteParams); } closeConfirmModal(); } // Закрытие модального окна подтверждения function closeConfirmModal() { document.getElementById('confirmModal').classList.remove('active'); deleteCallback = null; deleteParams = null; } // Просмотр метаданных function showMetadata(metadata) { document.getElementById('metadataContent').textContent = metadata; document.getElementById('metadataModal').classList.add('active'); } // Закрытие модального окна метаданных function closeMetadataModal() { document.getElementById('metadataModal').classList.remove('active'); } // Выход из системы async function logout() { try { await fetch('/api/logout', { method: 'POST' }); window.location.href = '/'; } catch (error) { console.error('Ошибка выхода:', error); window.location.href = '/'; } } // Вспомогательные функции function formatDate(dateString, type = 'date') { if (!dateString) return '-'; const date = new Date(dateString); if (type === 'date') { return date.toLocaleDateString('ru-RU', { year: 'numeric', month: '2-digit', day: '2-digit' }) + ' ' + date.toLocaleTimeString('ru-RU', { hour: '2-digit', minute: '2-digit' }); } else if (type === 'time') { return date.toLocaleTimeString('ru-RU', { hour: '2-digit', minute: '2-digit', second: '2-digit' }); } return date.toLocaleString('ru-RU'); } function getServiceTypeName(type) { switch(type) { case 'sberbank': return 'Сбербанк'; case 'yandex': return 'Яндекс'; case 'ldap': return 'LDAP'; default: return 'Прочие'; } } function escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; }