Группы 2.1

This commit is contained in:
2026-02-07 22:28:36 +05:00
parent 10e89abaab
commit eec6961174
3 changed files with 2076 additions and 1350 deletions

View File

@@ -44,12 +44,12 @@ module.exports = function(app, db) {
} }
// Таблица с идентификаторами пользователей // Таблица с идентификаторами пользователей
db.run(` db.run(`
CREATE TABLE IF NOT EXISTS idusers ( CREATE TABLE IF NOT EXISTS idusers (
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL, user_id INTEGER NOT NULL,
service_type TEXT NOT NULL, -- 'sberbank', 'yandex', 'ldap', 'other' service_type TEXT NOT NULL, -- 'sberbank', 'yandex', 'ldap', 'other'
external_id TEXT NOT NULL, external_id TEXT, -- СДЕЛАЛИ НЕОБЯЗАТЕЛЬНЫМ
login TEXT, -- Логин LDAP (если есть) login TEXT, -- Логин LDAP (если есть)
ldap_group TEXT, -- Группа LDAP (если есть) ldap_group TEXT, -- Группа LDAP (если есть)
group_id INTEGER, -- Ссылка на группу в idgroups group_id INTEGER, -- Ссылка на группу в idgroups
@@ -59,9 +59,9 @@ module.exports = function(app, db) {
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE, FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE,
FOREIGN KEY (group_id) REFERENCES idgroups (id) ON DELETE SET NULL, FOREIGN KEY (group_id) REFERENCES idgroups (id) ON DELETE SET NULL,
UNIQUE(user_id, service_type, external_id) UNIQUE(user_id, service_type, external_id) -- Теперь external_id может быть NULL
) )
`, (err) => { `, (err) => {
if (err) { if (err) {
console.error('❌ Ошибка создания таблицы idusers:', err.message); console.error('❌ Ошибка создания таблицы idusers:', err.message);
reject(err); reject(err);
@@ -510,7 +510,7 @@ module.exports = function(app, db) {
}); });
// POST /api2/idusers - Создать новый идентификатор пользователя (только админ) // POST /api2/idusers - Создать новый идентификатор пользователя (только админ)
router.post('/api2/idusers', requireAuth, requireAdmin, (req, res) => { router.post('/api2/idusers', requireAuth, requireAdmin, (req, res) => {
const { const {
user_id, user_id,
service_type, service_type,
@@ -522,10 +522,10 @@ module.exports = function(app, db) {
is_active is_active
} = req.body; } = req.body;
// Валидация обязательных полей // Валидация обязательных полей - СДЕЛАЕМ external_id НЕОБЯЗАТЕЛЬНЫМ
if (!user_id || !service_type || !external_id) { if (!user_id || !service_type) {
return res.status(400).json({ return res.status(400).json({
error: 'Обязательные поля: user_id, service_type, external_id' error: 'Обязательные поля: user_id, service_type'
}); });
} }
@@ -559,14 +559,8 @@ module.exports = function(app, db) {
return res.status(404).json({ error: 'Указанная группа не найдена' }); return res.status(404).json({ error: 'Указанная группа не найдена' });
} }
// Проверяем соответствие типа сервиса // УБИРАЕМ проверку соответствия типа сервиса
if (group.service_type !== service_type) { // Теперь группы независимы от сервиса
return res.status(400).json({
error: `Тип сервиса группы (${group.service_type}) не соответствует типу сервиса идентификатора (${service_type})`
});
}
// Создаем идентификатор
createIdUser(); createIdUser();
}); });
} else { } else {
@@ -577,6 +571,9 @@ module.exports = function(app, db) {
function createIdUser() { function createIdUser() {
const metadataJson = metadata ? JSON.stringify(metadata) : null; const metadataJson = metadata ? JSON.stringify(metadata) : null;
// Генерируем уникальный external_id если он не предоставлен
const finalExternalId = external_id || `temp_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
db.run( db.run(
`INSERT INTO idusers `INSERT INTO idusers
(user_id, service_type, external_id, login, ldap_group, group_id, metadata, is_active) (user_id, service_type, external_id, login, ldap_group, group_id, metadata, is_active)
@@ -584,7 +581,7 @@ module.exports = function(app, db) {
[ [
user_id, user_id,
service_type, service_type,
external_id, finalExternalId,
login || null, login || null,
ldap_group || null, ldap_group || null,
group_id || null, group_id || null,
@@ -611,10 +608,10 @@ module.exports = function(app, db) {
); );
} }
}); });
}); });
// PUT /api2/idusers/:id - Обновить идентификатор пользователя (только админ) // PUT /api2/idusers/:id - Обновить идентификатор пользователя (только админ)
router.put('/api2/idusers/:id', requireAuth, requireAdmin, (req, res) => { router.put('/api2/idusers/:id', requireAuth, requireAdmin, (req, res) => {
const { id } = req.params; const { id } = req.params;
const { const {
user_id, user_id,
@@ -638,6 +635,31 @@ module.exports = function(app, db) {
return res.status(404).json({ error: 'Идентификатор не найден' }); return res.status(404).json({ error: 'Идентификатор не найден' });
} }
// Если указана группа и она меняется, проверяем существование новой группы
if (group_id !== undefined && group_id !== existing.group_id) {
if (group_id) {
db.get('SELECT id 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: 'Указанная группа не найдена' });
}
// УБИРАЕМ проверку соответствия типа сервиса
// Теперь группы независимы от сервиса
updateIdUser();
});
} else {
updateIdUser();
}
} else {
updateIdUser();
}
function updateIdUser() {
// Собираем поля для обновления // Собираем поля для обновления
const updates = []; const updates = [];
const params = []; const params = [];
@@ -660,7 +682,9 @@ module.exports = function(app, db) {
if (external_id !== undefined) { if (external_id !== undefined) {
updates.push('external_id = ?'); updates.push('external_id = ?');
params.push(external_id); // Генерируем временный external_id если он пустой
const finalExternalId = external_id || `temp_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
params.push(finalExternalId);
} }
if (login !== undefined) { if (login !== undefined) {
@@ -714,8 +738,9 @@ module.exports = function(app, db) {
message: 'Идентификатор успешно обновлен' message: 'Идентификатор успешно обновлен'
}); });
}); });
}
}); });
}); });
// DELETE /api2/idusers/:id - Удалить идентификатор пользователя (только админ) // DELETE /api2/idusers/:id - Удалить идентификатор пользователя (только админ)
router.delete('/api2/idusers/:id', requireAuth, requireAdmin, (req, res) => { router.delete('/api2/idusers/:id', requireAuth, requireAdmin, (req, res) => {

File diff suppressed because it is too large Load Diff

View File

@@ -1,26 +1,26 @@
// Глобальные переменные // Глобальные переменные
let currentUser = null; let currentUser = null;
let isAdmin = false; let isAdmin = false;
let currentTab = 'groups'; let currentTab = 'groups';
let groups = []; let groups = [];
let idusers = []; let idusers = [];
let allUsers = []; let allUsers = [];
let currentPage = { let currentPage = {
groups: 1, groups: 1,
idusers: 1 idusers: 1
}; };
const itemsPerPage = 20; const itemsPerPage = 20;
let deleteCallback = null; let deleteCallback = null;
let deleteParams = null; let deleteParams = null;
// Инициализация при загрузке страницы // Инициализация при загрузке страницы
document.addEventListener('DOMContentLoaded', function() { document.addEventListener('DOMContentLoaded', function() {
checkAuth(); checkAuth();
setupEventListeners(); setupEventListeners();
}); });
// Проверка аутентификации // Проверка аутентификации
async function checkAuth() { async function checkAuth() {
try { try {
const response = await fetch('/api/user'); const response = await fetch('/api/user');
if (!response.ok) { if (!response.ok) {
@@ -40,7 +40,7 @@
loadGroups(); loadGroups();
loadAllUsers(); loadAllUsers();
loadIdUsers(); loadIdUsers();
// loadStats(); //loadStats();
} else { } else {
window.location.href = '/'; window.location.href = '/';
} }
@@ -48,10 +48,10 @@
console.error('Ошибка проверки аутентификации:', error); console.error('Ошибка проверки аутентификации:', error);
window.location.href = '/'; window.location.href = '/';
} }
} }
// Настройка обработчиков событий // Настройка обработчиков событий
function setupEventListeners() { function setupEventListeners() {
// Переключение вкладок // Переключение вкладок
document.querySelectorAll('.tab').forEach(tab => { document.querySelectorAll('.tab').forEach(tab => {
tab.addEventListener('click', function() { tab.addEventListener('click', function() {
@@ -78,10 +78,15 @@
document.getElementById('iduserServiceTypeFilter').addEventListener('change', filterIdUsers); document.getElementById('iduserServiceTypeFilter').addEventListener('change', filterIdUsers);
document.getElementById('iduserGroupFilter').addEventListener('change', filterIdUsers); document.getElementById('iduserGroupFilter').addEventListener('change', filterIdUsers);
document.getElementById('iduserStatusFilter').addEventListener('change', filterIdUsers); document.getElementById('iduserStatusFilter').addEventListener('change', filterIdUsers);
}
// Дебаунс для поиска // Обновление опций групп при изменении типа сервиса в форме
function debounce(func, wait) { document.getElementById('iduserServiceType').addEventListener('change', function() {
updateGroupOptions();
});
}
// Дебаунс для поиска
function debounce(func, wait) {
let timeout; let timeout;
return function executedFunction(...args) { return function executedFunction(...args) {
const later = () => { const later = () => {
@@ -91,10 +96,10 @@
clearTimeout(timeout); clearTimeout(timeout);
timeout = setTimeout(later, wait); timeout = setTimeout(later, wait);
}; };
} }
// Переключение вкладок // Переключение вкладок
function switchTab(tabId) { function switchTab(tabId) {
currentTab = tabId; currentTab = tabId;
// Обновляем активные вкладки // Обновляем активные вкладки
@@ -112,10 +117,10 @@
content.classList.add('active'); content.classList.add('active');
} }
}); });
} }
// Загрузка всех пользователей // Загрузка всех пользователей
async function loadAllUsers() { async function loadAllUsers() {
try { try {
const response = await fetch('/api/users'); const response = await fetch('/api/users');
if (!response.ok) throw new Error('Ошибка загрузки пользователей'); if (!response.ok) throw new Error('Ошибка загрузки пользователей');
@@ -135,10 +140,10 @@
} catch (error) { } catch (error) {
console.error('Ошибка загрузки пользователей:', error); console.error('Ошибка загрузки пользователей:', error);
} }
} }
// Загрузка групп // Загрузка групп
async function loadGroups() { async function loadGroups() {
try { try {
const tableBody = document.getElementById('groupsTableBody'); const tableBody = document.getElementById('groupsTableBody');
tableBody.innerHTML = '<tr><td colspan="8" class="loading"><i class="fas fa-spinner fa-spin"></i> Загрузка групп...</td></tr>'; tableBody.innerHTML = '<tr><td colspan="8" class="loading"><i class="fas fa-spinner fa-spin"></i> Загрузка групп...</td></tr>';
@@ -170,10 +175,10 @@
document.getElementById('groupsTableBody').innerHTML = document.getElementById('groupsTableBody').innerHTML =
'<tr><td colspan="8" class="error">Ошибка загрузки групп: ' + error.message + '</td></tr>'; '<tr><td colspan="8" class="error">Ошибка загрузки групп: ' + error.message + '</td></tr>';
} }
} }
// Отображение групп // Отображение групп
function renderGroups(filteredGroups = null) { function renderGroups(filteredGroups = null) {
const groupsToRender = filteredGroups || groups; const groupsToRender = filteredGroups || groups;
const tableBody = document.getElementById('groupsTableBody'); const tableBody = document.getElementById('groupsTableBody');
@@ -237,10 +242,10 @@
// Пагинация // Пагинация
renderPagination('groups', totalPages); renderPagination('groups', totalPages);
} }
// Фильтрация групп // Фильтрация групп
function filterGroups() { function filterGroups() {
const searchText = document.getElementById('groupSearch').value.toLowerCase(); const searchText = document.getElementById('groupSearch').value.toLowerCase();
const serviceType = document.getElementById('groupServiceTypeFilter').value; const serviceType = document.getElementById('groupServiceTypeFilter').value;
const statusFilter = document.getElementById('groupStatusFilter').value; const statusFilter = document.getElementById('groupStatusFilter').value;
@@ -263,10 +268,10 @@
currentPage.groups = 1; currentPage.groups = 1;
renderGroups(filtered); renderGroups(filtered);
} }
// Загрузка идентификаторов пользователей // Загрузка идентификаторов пользователей
async function loadIdUsers() { async function loadIdUsers() {
try { try {
const tableBody = document.getElementById('idusersTableBody'); const tableBody = document.getElementById('idusersTableBody');
tableBody.innerHTML = '<tr><td colspan="11" class="loading"><i class="fas fa-spinner fa-spin"></i> Загрузка идентификаторов...</td></tr>'; tableBody.innerHTML = '<tr><td colspan="11" class="loading"><i class="fas fa-spinner fa-spin"></i> Загрузка идентификаторов...</td></tr>';
@@ -281,10 +286,10 @@
document.getElementById('idusersTableBody').innerHTML = document.getElementById('idusersTableBody').innerHTML =
'<tr><td colspan="11" class="error">Ошибка загрузки идентификаторов: ' + error.message + '</td></tr>'; '<tr><td colspan="11" class="error">Ошибка загрузки идентификаторов: ' + error.message + '</td></tr>';
} }
} }
// Отображение идентификаторов пользователей // Отображение идентификаторов пользователей
function renderIdUsers(filteredIdUsers = null) { function renderIdUsers(filteredIdUsers = null) {
const idusersToRender = filteredIdUsers || idusers; const idusersToRender = filteredIdUsers || idusers;
const tableBody = document.getElementById('idusersTableBody'); const tableBody = document.getElementById('idusersTableBody');
@@ -338,7 +343,7 @@
<small>${iduser.user_login || '-'}</small> <small>${iduser.user_login || '-'}</small>
</td> </td>
<td>${serviceTypeBadge}</td> <td>${serviceTypeBadge}</td>
<td><strong>${iduser.external_id}</strong></td> <td><strong>${iduser.external_id || '-'}</strong></td>
<td>${iduser.login || '-'}</td> <td>${iduser.login || '-'}</td>
<td>${iduser.ldap_group || '-'}</td> <td>${iduser.ldap_group || '-'}</td>
<td>${iduser.group_name || '-'}</td> <td>${iduser.group_name || '-'}</td>
@@ -362,10 +367,10 @@
// Пагинация // Пагинация
renderPagination('idusers', totalPages); renderPagination('idusers', totalPages);
} }
// Фильтрация идентификаторов пользователей // Фильтрация идентификаторов пользователей
function filterIdUsers() { function filterIdUsers() {
const searchText = document.getElementById('iduserSearch').value.toLowerCase(); const searchText = document.getElementById('iduserSearch').value.toLowerCase();
const serviceType = document.getElementById('iduserServiceTypeFilter').value; const serviceType = document.getElementById('iduserServiceTypeFilter').value;
const groupId = document.getElementById('iduserGroupFilter').value; const groupId = document.getElementById('iduserGroupFilter').value;
@@ -374,7 +379,7 @@
const filtered = idusers.filter(iduser => { const filtered = idusers.filter(iduser => {
// Поиск по тексту // Поиск по тексту
const matchesSearch = !searchText || const matchesSearch = !searchText ||
iduser.external_id.toLowerCase().includes(searchText) || (iduser.external_id && iduser.external_id.toLowerCase().includes(searchText)) ||
(iduser.login && iduser.login.toLowerCase().includes(searchText)) || (iduser.login && iduser.login.toLowerCase().includes(searchText)) ||
(iduser.user_name && iduser.user_name.toLowerCase().includes(searchText)) || (iduser.user_name && iduser.user_name.toLowerCase().includes(searchText)) ||
(iduser.user_login && iduser.user_login.toLowerCase().includes(searchText)); (iduser.user_login && iduser.user_login.toLowerCase().includes(searchText));
@@ -394,10 +399,10 @@
currentPage.idusers = 1; currentPage.idusers = 1;
renderIdUsers(filtered); renderIdUsers(filtered);
} }
// Загрузка статистики // Загрузка статистики
async function loadStats() { async function loadStats() {
try { try {
const statsGrid = document.getElementById('statsGrid'); const statsGrid = document.getElementById('statsGrid');
statsGrid.innerHTML = '<div class="loading"><i class="fas fa-spinner fa-spin"></i> Загрузка статистики...</div>'; statsGrid.innerHTML = '<div class="loading"><i class="fas fa-spinner fa-spin"></i> Загрузка статистики...</div>';
@@ -412,10 +417,10 @@
document.getElementById('statsGrid').innerHTML = document.getElementById('statsGrid').innerHTML =
'<div class="error">Ошибка загрузки статистики: ' + error.message + '</div>'; '<div class="error">Ошибка загрузки статистики: ' + error.message + '</div>';
} }
} }
// Отображение статистики // Отображение статистики
function renderStats(stats) { function renderStats(stats) {
const statsGrid = document.getElementById('statsGrid'); const statsGrid = document.getElementById('statsGrid');
const tableBody = document.getElementById('statsTableBody'); const tableBody = document.getElementById('statsTableBody');
@@ -470,10 +475,10 @@
} else { } else {
tableBody.innerHTML = '<tr><td colspan="5" class="no-data">Нет данных для отображения</td></tr>'; tableBody.innerHTML = '<tr><td colspan="5" class="no-data">Нет данных для отображения</td></tr>';
} }
} }
// Модальное окно для добавления группы // Модальное окно для добавления группы
function showAddGroupModal() { function showAddGroupModal() {
if (!isAdmin) { if (!isAdmin) {
alert('Недостаточно прав'); alert('Недостаточно прав');
return; return;
@@ -486,10 +491,10 @@
document.getElementById('groupMessage').innerHTML = ''; document.getElementById('groupMessage').innerHTML = '';
document.getElementById('groupModal').classList.add('active'); document.getElementById('groupModal').classList.add('active');
} }
// Модальное окно для редактирования группы // Модальное окно для редактирования группы
async function editGroup(id) { async function editGroup(id) {
if (!isAdmin) { if (!isAdmin) {
alert('Недостаточно прав'); alert('Недостаточно прав');
return; return;
@@ -514,15 +519,15 @@
console.error('Ошибка загрузки группы:', error); console.error('Ошибка загрузки группы:', error);
alert('Ошибка загрузки группы: ' + error.message); alert('Ошибка загрузки группы: ' + error.message);
} }
} }
// Закрытие модального окна группы // Закрытие модального окна группы
function closeGroupModal() { function closeGroupModal() {
document.getElementById('groupModal').classList.remove('active'); document.getElementById('groupModal').classList.remove('active');
} }
// Сохранение группы // Сохранение группы
async function saveGroup(event) { async function saveGroup(event) {
event.preventDefault(); event.preventDefault();
if (!isAdmin) { if (!isAdmin) {
@@ -582,10 +587,10 @@
</div> </div>
`; `;
} }
} }
// Подтверждение удаления группы // Подтверждение удаления группы
function confirmDeleteGroup(id) { function confirmDeleteGroup(id) {
if (!isAdmin) { if (!isAdmin) {
alert('Недостаточно прав'); alert('Недостаточно прав');
return; return;
@@ -601,10 +606,10 @@
deleteParams = { id }; deleteParams = { id };
document.getElementById('confirmModal').classList.add('active'); document.getElementById('confirmModal').classList.add('active');
} }
// Удаление группы // Удаление группы
async function deleteGroup(params) { async function deleteGroup(params) {
try { try {
const response = await fetch(`/api2/groups/${params.id}`, { const response = await fetch(`/api2/groups/${params.id}`, {
method: 'DELETE' method: 'DELETE'
@@ -631,10 +636,10 @@
console.error('Ошибка удаления группы:', error); console.error('Ошибка удаления группы:', error);
alert('Ошибка удаления группы: ' + error.message); alert('Ошибка удаления группы: ' + error.message);
} }
} }
// Модальное окно для добавления идентификатора // Модальное окно для добавления идентификатора
function showAddIdUserModal() { function showAddIdUserModal() {
if (!isAdmin) { if (!isAdmin) {
alert('Недостаточно прав'); alert('Недостаточно прав');
return; return;
@@ -643,17 +648,33 @@
document.getElementById('iduserModalTitle').textContent = 'Добавить идентификатор'; document.getElementById('iduserModalTitle').textContent = 'Добавить идентификатор';
document.getElementById('iduserId').value = ''; document.getElementById('iduserId').value = '';
document.getElementById('iduserForm').reset(); document.getElementById('iduserForm').reset();
// Устанавливаем тип сервиса по умолчанию как LDAP
document.getElementById('iduserServiceType').value = 'ldap';
document.getElementById('iduserIsActive').checked = true; document.getElementById('iduserIsActive').checked = true;
document.getElementById('iduserMessage').innerHTML = ''; document.getElementById('iduserMessage').innerHTML = '';
// Обновляем опции групп // Добавляем обработчик для автозаполнения LDAP полей
const userIdSelect = document.getElementById('iduserUserId');
// Удаляем существующие обработчики чтобы избежать дублирования
const newUserIdSelect = userIdSelect.cloneNode(true);
userIdSelect.parentNode.replaceChild(newUserIdSelect, userIdSelect);
// Добавляем новый обработчик
document.getElementById('iduserUserId').addEventListener('change', function() {
autoFillLdapFields(this.value);
});
// Обновляем опции групп (все группы, независимо от сервиса)
updateGroupOptions(); updateGroupOptions();
document.getElementById('iduserModal').classList.add('active'); document.getElementById('iduserModal').classList.add('active');
} }
// Модальное окно для редактирования идентификатора // Модальное окно для редактирования идентификатора
async function editIdUser(id) { async function editIdUser(id) {
if (!isAdmin) { if (!isAdmin) {
alert('Недостаточно прав'); alert('Недостаточно прав');
return; return;
@@ -668,7 +689,7 @@
document.getElementById('iduserModalTitle').textContent = 'Редактировать идентификатор'; document.getElementById('iduserModalTitle').textContent = 'Редактировать идентификатор';
document.getElementById('iduserId').value = iduser.id; document.getElementById('iduserId').value = iduser.id;
document.getElementById('iduserUserId').value = iduser.user_id; document.getElementById('iduserUserId').value = iduser.user_id;
document.getElementById('iduserExternalId').value = iduser.external_id; document.getElementById('iduserExternalId').value = iduser.external_id || '';
document.getElementById('iduserLogin').value = iduser.login || ''; document.getElementById('iduserLogin').value = iduser.login || '';
document.getElementById('iduserLdapGroup').value = iduser.ldap_group || ''; document.getElementById('iduserLdapGroup').value = iduser.ldap_group || '';
document.getElementById('iduserServiceType').value = iduser.service_type; document.getElementById('iduserServiceType').value = iduser.service_type;
@@ -681,9 +702,20 @@
document.getElementById('iduserMetadata').value = ''; document.getElementById('iduserMetadata').value = '';
} }
// Обновляем опции групп и выбираем текущую // Обновляем опции групп (все группы) и выбираем текущую
updateGroupOptions().then(() => { await updateGroupOptions();
document.getElementById('iduserGroupId').value = iduser.group_id || ''; document.getElementById('iduserGroupId').value = iduser.group_id || '';
// Добавляем обработчик для автозаполнения LDAP полей
const userIdSelect = document.getElementById('iduserUserId');
// Удаляем существующие обработчики чтобы избежать дублирования
const newUserIdSelect = userIdSelect.cloneNode(true);
userIdSelect.parentNode.replaceChild(newUserIdSelect, userIdSelect);
// Добавляем новый обработчик
document.getElementById('iduserUserId').addEventListener('change', function() {
autoFillLdapFields(this.value);
}); });
document.getElementById('iduserMessage').innerHTML = ''; document.getElementById('iduserMessage').innerHTML = '';
@@ -693,11 +725,50 @@
console.error('Ошибка загрузки идентификатора:', error); console.error('Ошибка загрузки идентификатора:', error);
alert('Ошибка загрузки идентификатора: ' + error.message); alert('Ошибка загрузки идентификатора: ' + error.message);
} }
}
// Автоматическое заполнение полей LDAP на основе выбранного пользователя
function autoFillLdapFields(userId) {
if (!userId) return;
// Находим пользователя в списке всех пользователей
const user = allUsers.find(u => u.id == userId);
if (!user) return;
// Проверяем, является ли пользователь LDAP пользователем
// Предполагаем, что LDAP пользователи имеют логин в определенном формате
// или у них есть специальный атрибут в метаданных
const isLdapUser = user.login && (
user.login.includes('@') || // LDAP логин часто содержит домен
user.service_type === 'ldap' ||
(user.metadata && user.metadata.dn) // Имеет Distinguished Name
);
if (isLdapUser) {
// Автоматически заполняем логин LDAP если поле пустое
if (!document.getElementById('iduserLogin').value.trim()) {
document.getElementById('iduserLogin').value = user.login;
} }
// Обновление опций групп в зависимости от типа сервиса // Автоматически заполняем группу LDAP из атрибутов пользователя
async function updateGroupOptions() { if (!document.getElementById('iduserLdapGroup').value.trim()) {
const serviceType = document.getElementById('iduserServiceType').value; // Можно извлекать из различных мест:
// 1. Из метаданных пользователя
// 2. Из атрибута department или ou
// 3. Из других полей
if (user.metadata && user.metadata.ou) {
document.getElementById('iduserLdapGroup').value = user.metadata.ou;
} else if (user.department) {
document.getElementById('iduserLdapGroup').value = user.department;
} else if (user.metadata && user.metadata.memberOf) {
document.getElementById('iduserLdapGroup').value = user.metadata.memberOf;
}
}
}
}
// Обновление опций групп - теперь показывает все группы независимо от типа сервиса
async function updateGroupOptions() {
const groupSelect = document.getElementById('iduserGroupId'); const groupSelect = document.getElementById('iduserGroupId');
// Загружаем группы, если еще не загружены // Загружаем группы, если еще не загружены
@@ -705,38 +776,36 @@
await loadGroups(); await loadGroups();
} }
// Фильтруем группы по типу сервиса // Берем все активные группы (без фильтрации по типу сервиса)
const filteredGroups = serviceType ? const activeGroups = groups.filter(g => g.is_active);
groups.filter(g => g.service_type === serviceType && g.is_active) :
groups.filter(g => g.is_active);
// Сохраняем текущее значение // Сохраняем текущее значение
const currentValue = groupSelect.value; const currentValue = groupSelect.value;
// Обновляем опции // Обновляем опции
groupSelect.innerHTML = '<option value="">Без группы</option>'; groupSelect.innerHTML = '<option value="">Без группы</option>';
filteredGroups.forEach(group => { activeGroups.forEach(group => {
const option = document.createElement('option'); const option = document.createElement('option');
option.value = group.id; option.value = group.id;
option.textContent = group.name; option.textContent = `${group.name} (${getServiceTypeName(group.service_type)})`;
groupSelect.appendChild(option); groupSelect.appendChild(option);
}); });
// Восстанавливаем значение, если оно есть в новых опциях // Восстанавливаем значение, если оно есть в новых опциях
if (currentValue && filteredGroups.some(g => g.id == currentValue)) { if (currentValue && activeGroups.some(g => g.id == currentValue)) {
groupSelect.value = currentValue; groupSelect.value = currentValue;
} }
return Promise.resolve(); return Promise.resolve();
} }
// Закрытие модального окна идентификатора // Закрытие модального окна идентификатора
function closeIdUserModal() { function closeIdUserModal() {
document.getElementById('iduserModal').classList.remove('active'); document.getElementById('iduserModal').classList.remove('active');
} }
// Сохранение идентификатора // Сохранение идентификатора
async function saveIdUser(event) { async function saveIdUser(event) {
event.preventDefault(); event.preventDefault();
if (!isAdmin) { if (!isAdmin) {
@@ -747,6 +816,9 @@
const iduserId = document.getElementById('iduserId').value; const iduserId = document.getElementById('iduserId').value;
const isEdit = !!iduserId; const isEdit = !!iduserId;
// Валидация - External ID теперь необязательный
const externalId = document.getElementById('iduserExternalId').value.trim();
// Парсим метаданные // Парсим метаданные
let metadata = null; let metadata = null;
const metadataText = document.getElementById('iduserMetadata').value.trim(); const metadataText = document.getElementById('iduserMetadata').value.trim();
@@ -767,14 +839,35 @@
const iduserData = { const iduserData = {
user_id: parseInt(document.getElementById('iduserUserId').value), user_id: parseInt(document.getElementById('iduserUserId').value),
service_type: document.getElementById('iduserServiceType').value, service_type: document.getElementById('iduserServiceType').value,
external_id: document.getElementById('iduserExternalId').value, external_id: externalId || null, // Разрешаем null для external_id
login: document.getElementById('iduserLogin').value || undefined, login: document.getElementById('iduserLogin').value.trim() || null,
ldap_group: document.getElementById('iduserLdapGroup').value || undefined, ldap_group: document.getElementById('iduserLdapGroup').value.trim() || null,
group_id: document.getElementById('iduserGroupId').value || undefined, group_id: document.getElementById('iduserGroupId').value || null,
metadata: metadata, metadata: metadata,
is_active: document.getElementById('iduserIsActive').checked is_active: document.getElementById('iduserIsActive').checked
}; };
// Проверяем обязательные поля
if (!iduserData.user_id) {
document.getElementById('iduserMessage').innerHTML = `
<div class="error">
<i class="fas fa-exclamation-circle"></i>
Пожалуйста, выберите пользователя
</div>
`;
return;
}
if (!iduserData.service_type) {
document.getElementById('iduserMessage').innerHTML = `
<div class="error">
<i class="fas fa-exclamation-circle"></i>
Пожалуйста, выберите тип сервиса
</div>
`;
return;
}
try { try {
const url = isEdit ? `/api2/idusers/${iduserId}` : '/api2/idusers'; const url = isEdit ? `/api2/idusers/${iduserId}` : '/api2/idusers';
const method = isEdit ? 'PUT' : 'POST'; const method = isEdit ? 'PUT' : 'POST';
@@ -817,10 +910,10 @@
</div> </div>
`; `;
} }
} }
// Подтверждение удаления идентификатора // Подтверждение удаления идентификатора
function confirmDeleteIdUser(id) { function confirmDeleteIdUser(id) {
if (!isAdmin) { if (!isAdmin) {
alert('Недостаточно прав'); alert('Недостаточно прав');
return; return;
@@ -830,16 +923,16 @@
if (!iduser) return; if (!iduser) return;
document.getElementById('confirmModalTitle').textContent = 'Удаление идентификатора'; document.getElementById('confirmModalTitle').textContent = 'Удаление идентификатора';
document.getElementById('confirmMessage').textContent = `Вы уверены, что хотите удалить идентификатор "${iduser.external_id}" пользователя "${iduser.user_name}"?`; document.getElementById('confirmMessage').textContent = `Вы уверены, что хотите удалить идентификатор "${iduser.external_id || 'без внешнего ID'}" пользователя "${iduser.user_name}"?`;
deleteCallback = deleteIdUser; deleteCallback = deleteIdUser;
deleteParams = { id }; deleteParams = { id };
document.getElementById('confirmModal').classList.add('active'); document.getElementById('confirmModal').classList.add('active');
} }
// Удаление идентификатора // Удаление идентификатора
async function deleteIdUser(params) { async function deleteIdUser(params) {
try { try {
const response = await fetch(`/api2/idusers/${params.id}`, { const response = await fetch(`/api2/idusers/${params.id}`, {
method: 'DELETE' method: 'DELETE'
@@ -866,10 +959,10 @@
console.error('Ошибка удаления идентификатора:', error); console.error('Ошибка удаления идентификатора:', error);
alert('Ошибка удаления идентификатора: ' + error.message); alert('Ошибка удаления идентификатора: ' + error.message);
} }
} }
// Пагинация // Пагинация
function renderPagination(type, totalPages) { function renderPagination(type, totalPages) {
const paginationDiv = document.getElementById(`${type}Pagination`); const paginationDiv = document.getElementById(`${type}Pagination`);
if (totalPages <= 1) { if (totalPages <= 1) {
@@ -906,10 +999,10 @@
</span>`; </span>`;
paginationDiv.innerHTML = html; paginationDiv.innerHTML = html;
} }
// Изменение страницы // Изменение страницы
function changePage(type, page) { function changePage(type, page) {
currentPage[type] = page; currentPage[type] = page;
if (type === 'groups') { if (type === 'groups') {
@@ -917,36 +1010,36 @@
} else if (type === 'idusers') { } else if (type === 'idusers') {
filterIdUsers(); filterIdUsers();
} }
} }
// Подтверждение удаления // Подтверждение удаления
function confirmDelete() { function confirmDelete() {
if (deleteCallback && deleteParams) { if (deleteCallback && deleteParams) {
deleteCallback(deleteParams); deleteCallback(deleteParams);
} }
closeConfirmModal(); closeConfirmModal();
} }
// Закрытие модального окна подтверждения // Закрытие модального окна подтверждения
function closeConfirmModal() { function closeConfirmModal() {
document.getElementById('confirmModal').classList.remove('active'); document.getElementById('confirmModal').classList.remove('active');
deleteCallback = null; deleteCallback = null;
deleteParams = null; deleteParams = null;
} }
// Просмотр метаданных // Просмотр метаданных
function showMetadata(metadata) { function showMetadata(metadata) {
document.getElementById('metadataContent').textContent = metadata; document.getElementById('metadataContent').textContent = metadata;
document.getElementById('metadataModal').classList.add('active'); document.getElementById('metadataModal').classList.add('active');
} }
// Закрытие модального окна метаданных // Закрытие модального окна метаданных
function closeMetadataModal() { function closeMetadataModal() {
document.getElementById('metadataModal').classList.remove('active'); document.getElementById('metadataModal').classList.remove('active');
} }
// Выход из системы // Выход из системы
async function logout() { async function logout() {
try { try {
await fetch('/api/logout', { await fetch('/api/logout', {
method: 'POST' method: 'POST'
@@ -956,10 +1049,10 @@
console.error('Ошибка выхода:', error); console.error('Ошибка выхода:', error);
window.location.href = '/'; window.location.href = '/';
} }
} }
// Вспомогательные функции // Вспомогательные функции
function formatDate(dateString, type = 'date') { function formatDate(dateString, type = 'date') {
if (!dateString) return '-'; if (!dateString) return '-';
const date = new Date(dateString); const date = new Date(dateString);
@@ -982,19 +1075,19 @@
} }
return date.toLocaleString('ru-RU'); return date.toLocaleString('ru-RU');
} }
function getServiceTypeName(type) { function getServiceTypeName(type) {
switch(type) { switch(type) {
case 'sberbank': return 'Сбербанк'; case 'sberbank': return 'Сбербанк';
case 'yandex': return 'Яндекс'; case 'yandex': return 'Яндекс';
case 'ldap': return 'LDAP'; case 'ldap': return 'LDAP';
default: return 'Прочие'; default: return 'Прочие';
} }
} }
function escapeHtml(text) { function escapeHtml(text) {
const div = document.createElement('div'); const div = document.createElement('div');
div.textContent = text; div.textContent = text;
return div.innerHTML; return div.innerHTML;
} }