document.addEventListener('DOMContentLoaded', () => { const tableBody = document.querySelector('#users-table tbody'); const itemsCount = document.getElementById('items-count'); const refreshBtn = document.getElementById('refresh-btn'); const filterInput = document.getElementById('table-filter'); // Кнопки управления const btnCreate = document.getElementById('btn-create'); const btnEdit = document.getElementById('btn-edit'); const btnDelete = document.getElementById('btn-delete'); // Элементы модального окна const userModal = document.getElementById('user-modal'); const userForm = document.getElementById('user-form'); const modalTitle = document.getElementById('modal-title'); const btnModalCancel = document.getElementById('btn-modal-cancel'); let selectedUsername = null; let selectedUserRow = null; // Загрузка данных async function loadUsers() { try { const response = await fetch('api/users.php'); if (!response.ok) throw new Error('Ошибка сервера'); const data = await response.json(); renderTable(data); resetSelection(); } catch (error) { alert('Не удалось обновить список пользователей'); } } function renderTable(users) { tableBody.innerHTML = ''; users.forEach(user => { const tr = document.createElement('tr'); tr.dataset.username = user.name; tr.dataset.desc = user.desc; const statusClass = user.status === 'Normal' ? 'status-normal' : 'status-deactivated'; tr.innerHTML = ` ${escapeHtml(user.name)} ${escapeHtml(user.email)} ${escapeHtml(user.desc)} ${escapeHtml(user.tfa)} ${escapeHtml(user.status)} `; // Логика выбора строки кликом tr.addEventListener('click', () => { if (selectedUserRow) selectedUserRow.classList.remove('selected-user'); if (selectedUsername === user.name) { resetSelection(); } else { selectedUsername = user.name; selectedUserRow = tr; tr.classList.add('selected-user'); btnEdit.disabled = false; // Запрещаем удалять root-пользователя напрямую из UI ради безопасности btnDelete.disabled = (user.name === 'root'); } }); tableBody.appendChild(tr); }); itemsCount.textContent = `${users.length} items`; } function resetSelection() { selectedUsername = null; selectedUserRow = null; btnEdit.disabled = true; btnDelete.disabled = true; } // Фильтрация таблицы filterInput.addEventListener('input', (e) => { const value = e.target.value.toLowerCase(); Array.from(tableBody.querySelectorAll('tr')).forEach(tr => { const match = tr.textContent.toLowerCase().includes(value); tr.style.display = match ? '' : 'none'; }); }); // Открытие модального окна создания btnCreate.addEventListener('click', () => { userForm.reset(); document.getElementById('form-action').value = 'create'; document.getElementById('username').disabled = false; document.getElementById('password-group').style.display = 'block'; modalTitle.textContent = 'Create User'; userModal.classList.add('open'); }); // Открытие модального окна редактирования btnEdit.addEventListener('click', () => { if (!selectedUsername) return; userForm.reset(); document.getElementById('form-action').value = 'update'; document.getElementById('old-username').value = selectedUsername; const usernameInput = document.getElementById('username'); usernameInput.value = selectedUsername; usernameInput.disabled = true; // Имя пользователя в Linux менять через useradd напрямую нельзя document.getElementById('description').value = selectedUserRow.dataset.desc || ''; document.getElementById('password-group').style.display = 'block'; // Пароль заполнять по желанию modalTitle.textContent = 'Edit User'; userModal.classList.add('open'); }); // Обработка кнопки удаления btnDelete.addEventListener('click', async () => { if (!selectedUsername) return; if (confirm(`Вы уверены, что хотите удалить пользователя ${selectedUsername} вместе с домашней директорией?`)) { await sendAction({ action: 'delete', username: selectedUsername }); } }); // Закрытие модального окна btnModalCancel.addEventListener('click', () => userModal.classList.remove('open')); // Отправка формы (Создание / Изменение) userForm.addEventListener('submit', async (e) => { e.preventDefault(); const action = document.getElementById('form-action').value; const payload = { action: action, username: document.getElementById('username').value, description: document.getElementById('description').value, password: document.getElementById('password').value, old_username: document.getElementById('old-username').value }; await sendAction(payload); userModal.classList.remove('open'); }); async function sendAction(data) { try { const response = await fetch('api/users.php', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); const result = await response.json(); if (result.success) { loadUsers(); } else { alert('Ошибка: ' + result.error); } } catch (error) { alert('Ошибка сети при отправке запроса'); } } function escapeHtml(text) { if (!text) return ''; return text.toString().replace(/&/g, "&").replace(//g, ">"); } refreshBtn.addEventListener('click', loadUsers); loadUsers(); // --- ЛОГИКА ВКЛАДКИ GROUP --- const tabUser = document.getElementById('tab-user'); const tabGroup = document.getElementById('tab-group'); const tableHeader = document.querySelector('#users-table thead tr'); const groupModal = document.getElementById('group-modal'); const groupForm = document.getElementById('group-form'); const btnGroupCancel = document.getElementById('btn-group-cancel'); let currentTab = 'user'; // Храним активную вкладку ('user' или 'group') let selectedGroupName = null; // Переключение на вкладку User tabUser.addEventListener('click', () => { tabGroup.classList.remove('active'); tabUser.classList.add('active'); currentTab = 'user'; tableHeader.innerHTML = ` Name ▴ Email Description 2FA Status Status `; resetSelection(); loadUsers(); // Вызываем старую функцию загрузки пользователей }); // Переключение на вкладку Group tabGroup.addEventListener('click', () => { tabUser.classList.remove('active'); tabGroup.classList.add('active'); currentTab = 'group'; tableHeader.innerHTML = ` Group Name ▴ GID Members (Users) Status `; resetSelection(); loadGroups(); }); // Загрузка групп с бэкенда async function loadGroups() { try { const response = await fetch('api/groups.php'); const groups = await response.json(); renderGroupsTable(groups); } catch (error) { alert('Ошибка загрузки групп'); } } function renderGroupsTable(groups) { tableBody.innerHTML = ''; groups.forEach(group => { const tr = document.createElement('tr'); tr.innerHTML = ` ${escapeHtml(group.name)} ${group.gid} ${escapeHtml(group.users)} ${group.status} `; tr.addEventListener('click', () => { if (selectedUserRow) selectedUserRow.classList.remove('selected-user'); if (selectedGroupName === group.name) { selectedGroupName = null; selectedUserRow = null; btnDelete.disabled = true; } else { selectedGroupName = group.name; selectedUserRow = tr; tr.classList.add('selected-user'); btnEdit.disabled = true; // Для групп редактирование отключим btnDelete.disabled = (group.name === 'root' || group.name === 'wheel'); } }); tableBody.appendChild(tr); }); itemsCount.textContent = `${groups.length} items`; } // Модифицируем общие кнопки под контекст активной вкладки btnCreate.addEventListener('click', (e) => { if (currentTab === 'group') { e.stopPropagation(); // Останавливаем открытие модалки пользователей groupForm.reset(); groupModal.classList.add('open'); } }); btnDelete.addEventListener('click', async () => { if (currentTab === 'group' && selectedGroupName) { if (confirm(`Удалить группу ${selectedGroupName}?`)) { await sendGroupAction({ action: 'delete', group_name: selectedGroupName }); } } }); btnGroupCancel.addEventListener('click', () => groupModal.classList.remove('open')); groupForm.addEventListener('submit', async (e) => { e.preventDefault(); await sendGroupAction({ action: 'create', group_name: document.getElementById('group-name').value }); groupModal.classList.remove('open'); }); async function sendGroupAction(data) { try { const response = await fetch('api/groups.php', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); const result = await response.json(); if (result.success) { loadGroups(); } else { alert('Ошибка: ' + result.error); } } catch (error) { alert('Ошибка сети при обработке группы'); } } // Модифицируем круглую кнопку обновления (↻) refreshBtn.addEventListener('click', () => { if (currentTab === 'group') loadGroups(); }); });