useradd
This commit is contained in:
@@ -10,6 +10,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"archiver": "^7.0.1",
|
"archiver": "^7.0.1",
|
||||||
"axios": "^1.13.5",
|
"axios": "^1.13.5",
|
||||||
|
"bcrypt": "^6.0.0",
|
||||||
"bcryptjs": "~2.4.3",
|
"bcryptjs": "~2.4.3",
|
||||||
"dotenv": "~16.3.1",
|
"dotenv": "~16.3.1",
|
||||||
"express": "^4.21.2",
|
"express": "^4.21.2",
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ function showAdminInterface() {
|
|||||||
function setupEventListeners() {
|
function setupEventListeners() {
|
||||||
const loginForm = document.getElementById('login-form');
|
const loginForm = document.getElementById('login-form');
|
||||||
const editUserForm = document.getElementById('edit-user-form');
|
const editUserForm = document.getElementById('edit-user-form');
|
||||||
|
const createUserForm = document.getElementById('create-user-form');
|
||||||
|
|
||||||
if (loginForm) {
|
if (loginForm) {
|
||||||
loginForm.addEventListener('submit', login);
|
loginForm.addEventListener('submit', login);
|
||||||
@@ -71,6 +72,10 @@ function setupEventListeners() {
|
|||||||
if (editUserForm) {
|
if (editUserForm) {
|
||||||
editUserForm.addEventListener('submit', updateUser);
|
editUserForm.addEventListener('submit', updateUser);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (createUserForm) {
|
||||||
|
createUserForm.addEventListener('submit', createUser);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function login(event) {
|
async function login(event) {
|
||||||
@@ -270,6 +275,101 @@ function renderUsersTable() {
|
|||||||
`).join('');
|
`).join('');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Функция для открытия модального окна создания пользователя
|
||||||
|
function openCreateUserModal() {
|
||||||
|
const modal = document.getElementById('create-user-modal');
|
||||||
|
if (modal) {
|
||||||
|
// Сбрасываем форму
|
||||||
|
const form = document.getElementById('create-user-form');
|
||||||
|
if (form) {
|
||||||
|
form.reset();
|
||||||
|
}
|
||||||
|
modal.style.display = 'block';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Функция для закрытия модального окна создания пользователя
|
||||||
|
function closeCreateUserModal() {
|
||||||
|
const modal = document.getElementById('create-user-modal');
|
||||||
|
if (modal) {
|
||||||
|
modal.style.display = 'none';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Функция для создания нового пользователя
|
||||||
|
async function createUser(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
const login = document.getElementById('create-login').value;
|
||||||
|
const password = document.getElementById('create-password').value;
|
||||||
|
const name = document.getElementById('create-name').value;
|
||||||
|
const email = document.getElementById('create-email').value;
|
||||||
|
const role = document.getElementById('create-role').value;
|
||||||
|
const auth_type = document.getElementById('create-auth-type').value;
|
||||||
|
const groups = document.getElementById('create-groups').value;
|
||||||
|
const description = document.getElementById('create-description').value;
|
||||||
|
|
||||||
|
// Валидация
|
||||||
|
if (!login || !password || !name || !email) {
|
||||||
|
alert('Заполните все обязательные поля');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (password.length < 6) {
|
||||||
|
alert('Пароль должен содержать не менее 6 символов');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const userData = {
|
||||||
|
login,
|
||||||
|
password,
|
||||||
|
name,
|
||||||
|
email,
|
||||||
|
role,
|
||||||
|
auth_type,
|
||||||
|
groups: groups || '[]',
|
||||||
|
description
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch('/admin/users', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify(userData)
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.ok) {
|
||||||
|
const result = await response.json();
|
||||||
|
alert(`Пользователь успешно создан! ID: ${result.id}`);
|
||||||
|
closeCreateUserModal();
|
||||||
|
loadUsers(); // Перезагружаем список пользователей
|
||||||
|
|
||||||
|
// Обновляем статистику если она видна
|
||||||
|
if (document.getElementById('admin-dashboard')?.classList.contains('active')) {
|
||||||
|
if (typeof loadDashboardStats === 'function') {
|
||||||
|
loadDashboardStats();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (document.getElementById('admin-stats-section')?.classList.contains('active')) {
|
||||||
|
if (typeof loadUsersStats === 'function') {
|
||||||
|
loadUsersStats();
|
||||||
|
}
|
||||||
|
if (typeof loadOverallStats === 'function') {
|
||||||
|
loadOverallStats();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const error = await response.json();
|
||||||
|
alert(error.error || 'Ошибка создания пользователя');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Ошибка:', error);
|
||||||
|
alert('Ошибка создания пользователя');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function openEditUserModal(userId) {
|
async function openEditUserModal(userId) {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`/admin/users/${userId}`);
|
const response = await fetch(`/admin/users/${userId}`);
|
||||||
@@ -447,5 +547,8 @@ window.searchUsers = searchUsers;
|
|||||||
window.loadUsers = loadUsers;
|
window.loadUsers = loadUsers;
|
||||||
window.openEditUserModal = openEditUserModal;
|
window.openEditUserModal = openEditUserModal;
|
||||||
window.closeEditUserModal = closeEditUserModal;
|
window.closeEditUserModal = closeEditUserModal;
|
||||||
|
window.openCreateUserModal = openCreateUserModal;
|
||||||
|
window.closeCreateUserModal = closeCreateUserModal;
|
||||||
|
window.createUser = createUser;
|
||||||
window.updateUser = updateUser;
|
window.updateUser = updateUser;
|
||||||
window.deleteUser = deleteUser;
|
window.deleteUser = deleteUser;
|
||||||
@@ -61,6 +61,7 @@
|
|||||||
<div class="search-container">
|
<div class="search-container">
|
||||||
<input type="text" id="user-search" placeholder="Поиск пользователей по логину, имени или email..." oninput="searchUsers()">
|
<input type="text" id="user-search" placeholder="Поиск пользователей по логину, имени или email..." oninput="searchUsers()">
|
||||||
<button onclick="loadUsers()">Сбросить</button>
|
<button onclick="loadUsers()">Сбросить</button>
|
||||||
|
<button class="create-user-btn" onclick="openCreateUserModal()">➕ Создать пользователя</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<table class="users-table">
|
<table class="users-table">
|
||||||
@@ -91,6 +92,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Модальное окно редактирования пользователя -->
|
||||||
<div id="edit-user-modal" class="modal">
|
<div id="edit-user-modal" class="modal">
|
||||||
<div class="modal-content modal-lg">
|
<div class="modal-content modal-lg">
|
||||||
<span class="close" onclick="closeEditUserModal()">×</span>
|
<span class="close" onclick="closeEditUserModal()">×</span>
|
||||||
@@ -147,6 +149,67 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Модальное окно создания пользователя -->
|
||||||
|
<div id="create-user-modal" class="modal">
|
||||||
|
<div class="modal-content modal-lg">
|
||||||
|
<span class="close" onclick="closeCreateUserModal()">×</span>
|
||||||
|
<h3>Создать нового пользователя</h3>
|
||||||
|
<form id="create-user-form">
|
||||||
|
<div class="form-row">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="create-login">Логин *</label>
|
||||||
|
<input type="text" id="create-login" name="login" required>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="create-password">Пароль *</label>
|
||||||
|
<input type="password" id="create-password" name="password" required minlength="6">
|
||||||
|
<small class="form-hint">Минимум 6 символов</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-row">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="create-name">Имя *</label>
|
||||||
|
<input type="text" id="create-name" name="name" required>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="create-email">Email *</label>
|
||||||
|
<input type="email" id="create-email" name="email" required>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-row">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="create-role">Роль</label>
|
||||||
|
<select id="create-role" name="role">
|
||||||
|
<option value="teacher">Учитель</option>
|
||||||
|
<option value="admin">Администратор</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="create-auth-type">Тип авторизации</label>
|
||||||
|
<select id="create-auth-type" name="auth_type">
|
||||||
|
<option value="local">Локальная</option>
|
||||||
|
<option value="ldap">LDAP</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="create-groups">Группы (JSON)</label>
|
||||||
|
<input type="text" id="create-groups" name="groups" placeholder='["group1", "group2"]'>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="create-description">Описание</label>
|
||||||
|
<textarea id="create-description" name="description" rows="3"></textarea>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button type="submit">Создать пользователя</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<script src="admin-script.js"></script>
|
<script src="admin-script.js"></script>
|
||||||
<script src="admin-dashboard.js"></script>
|
<script src="admin-dashboard.js"></script>
|
||||||
<script src="admin-stats.js"></script>
|
<script src="admin-stats.js"></script>
|
||||||
|
|||||||
138
public/style.css
138
public/style.css
@@ -4760,4 +4760,142 @@ button.btn-primary {
|
|||||||
transform: translateY(0);
|
transform: translateY(0);
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
/* Стили для кнопки создания пользователя */
|
||||||
|
.create-user-btn {
|
||||||
|
background-color: #27ae60;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
padding: 8px 16px;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.create-user-btn:hover {
|
||||||
|
background-color: #2ecc71;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Стили для хинта в форме */
|
||||||
|
.form-hint {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #7f8c8d;
|
||||||
|
display: block;
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Обновленные стили для поиска */
|
||||||
|
.search-container {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-container input {
|
||||||
|
flex: 1;
|
||||||
|
padding: 8px;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-container button {
|
||||||
|
padding: 8px 16px;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
background-color: #3498db;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-container button:hover {
|
||||||
|
opacity: 0.9;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Стили для кнопок действий */
|
||||||
|
.user-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-actions button {
|
||||||
|
padding: 5px 10px;
|
||||||
|
border: none;
|
||||||
|
border-radius: 3px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-actions .edit-btn {
|
||||||
|
background-color: #f39c12;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-actions .edit-btn:hover {
|
||||||
|
background-color: #e67e22;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-actions .delete-btn {
|
||||||
|
background-color: #e74c3c;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-actions .delete-btn:hover {
|
||||||
|
background-color: #c0392b;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-actions .delete-btn:disabled {
|
||||||
|
background-color: #bdc3c7;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Стили для бейджей */
|
||||||
|
.ldap-badge, .admin-badge {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 2px 6px;
|
||||||
|
border-radius: 3px;
|
||||||
|
font-size: 10px;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-left: 5px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ldap-badge {
|
||||||
|
background-color: #9b59b6;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-badge {
|
||||||
|
background-color: #e74c3c;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Стили для модального окна */
|
||||||
|
.modal-lg {
|
||||||
|
max-width: 600px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-row {
|
||||||
|
display: flex;
|
||||||
|
gap: 15px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-row .form-group {
|
||||||
|
flex: 1;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close {
|
||||||
|
float: right;
|
||||||
|
font-size: 28px;
|
||||||
|
font-weight: bold;
|
||||||
|
cursor: pointer;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close:hover {
|
||||||
|
color: #e74c3c;
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user