/
erioxis
/
web-nodejs
Обзор
Документация
Войти
/
erioxis
/
web-nodejs
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
public/lab11/task2/index.html
494 строки
20 KB
erioxis
lab11fix
15 дек 2025, 23:21
15 дек 2025, 23:21
70dc7ba
Код
Авторство
О чём код?
<!DOCTYPE html> <html lang="ru"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>LR11 - Задание 2: Управление пользователями</title> <style> body { font-family: 'Tahoma', 'Arial', sans-serif; margin: 0; padding: 0; background: #008080 url('../../back.png') center/cover no-repeat fixed; color: #000; height: 100vh; overflow: hidden; } .desktop { padding: 20px; height: calc(100vh - 40px); position: relative; } .window { background-color: #ece9d8; border: 2px solid; border-top-color: #fff; border-left-color: #fff; border-right-color: #808080; border-bottom-color: #808080; padding: 3px; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 800px; height: 85vh; box-shadow: 3px 3px 10px rgba(0, 0, 0, 0.3); overflow: hidden; } .title-bar { background: linear-gradient(to right, #000080, #1084d0); color: white; padding: 3px 6px; font-weight: bold; font-size: 13px; display: flex; justify-content: space-between; align-items: center; } .window-content { padding: 20px; background: #ece9d8; height: calc(100% - 60px); overflow-y: auto; } h1 { color: #000080; text-align: center; margin: 0 0 20px 0; font-size: 16px; font-weight: bold; text-transform: uppercase; } .section { margin-bottom: 20px; padding: 15px; background: #fff; border: 1px solid #808080; } .form-group { margin-bottom: 12px; } .form-group label { display: block; margin-bottom: 3px; font-weight: bold; font-size: 11px; color: #000080; } .form-group input, .form-group select { width: 100%; padding: 5px 8px; border: 1px solid #808080; background: white; font-family: 'Tahoma'; font-size: 11px; } .button, .btn { background: linear-gradient(to bottom, #d4d0c8, #ece9d8); border: 2px solid; border-top-color: #fff; border-left-color: #fff; border-right-color: #808080; border-bottom-color: #808080; padding: 5px 10px; cursor: pointer; font-family: 'Tahoma'; font-size: 11px; color: #000; min-width: 80px; text-align: center; margin-right: 5px; margin-bottom: 5px; } .button:hover, .btn:hover { background: linear-gradient(to bottom, #316ac5, #245edb); color: white; border: 2px dotted #316ac5; } .error { color: #c00000; background: #ffe0e0; border: 1px solid #c00000; padding: 8px; margin: 10px 0; font-size: 11px; font-weight: bold; } .success { color: #006000; background: #e0ffe0; border: 1px solid #006000; padding: 8px; margin: 10px 0; font-size: 11px; font-weight: bold; } table { width: 100%; border-collapse: collapse; font-size: 11px; margin-top: 15px; } th { background: linear-gradient(to bottom, #000080, #1084d0); color: white; padding: 6px 8px; text-align: left; border: 1px solid #808080; font-weight: bold; } td { padding: 5px 8px; border: 1px solid #808080; background: white; vertical-align: top; } tr:nth-child(even) td { background: #f0f0f0; } .taskbar { position: fixed; bottom: 0; left: 0; right: 0; background: linear-gradient(to bottom, #245edb, #1d52c1); height: 30px; display: flex; align-items: center; padding: 0 5px; border-top: 1px solid #fff; } .start-button { background: linear-gradient(to bottom, #6ca035, #3d751b); color: white; padding: 4px 10px; border: 1px solid; border-top-color: #fff; border-left-color: #fff; border-right-color: #2d4f0f; border-bottom-color: #2d4f0f; font-weight: bold; margin-right: 10px; font-size: 11px; } footer { display: none; } h2.title { color: #000080; font-size: 14px; margin-bottom: 15px; border-bottom: 1px solid #808080; padding-bottom: 5px; } h3 { color: #000080; font-size: 12px; margin: 15px 0 10px 0; font-weight: bold; } h4 { color: #000080; font-size: 11px; margin: 10px 0 5px 0; font-weight: bold; } p { font-size: 11px; margin: 10px 0; } #add-user-form { margin-top: 20px; padding: 15px; background: #f0f0f0; border: 1px solid #808080; } </style> </head> <body> <div class="desktop"> <div class="window"> <div class="title-bar"> <span>LR11 - Задание 2: Управление пользователями</span> <span style="font-family: 'Arial'; font-size: 16px;">_ □ X</span> </div> <div class="window-content"> <div style="margin-bottom: 20px;"> <button class="button" onclick="location.href='../'">← Назад к заданиям</button> </div> <h1>УПРАВЛЕНИЕ ПОЛЬЗОВАТЕЛЯМИ</h1> <div class="section"> <h2 class="title">Административная панель управления пользователями</h2> <p style="font-size: 11px; color: #800000; font-weight: bold;">Только администратор может просматривать и управлять пользователями.</p> <div id="user-management-content"> <div id="user-management-data" style="font-size: 11px;"></div> <button class="button" onclick="loadUsers()">Загрузить список пользователей</button> <div id="add-user-form" style="display: none; margin-top: 20px;"> <h3>Добавить нового пользователя</h3> <div class="form-group"> <label for="new-user-login">Логин:</label> <input type="text" id="new-user-login" placeholder="Введите логин"> </div> <div class="form-group"> <label for="new-user-role">Роль:</label> <select id="new-user-role"> <option value="user">user (обычный пользователь)</option> <option value="admin">admin (администратор)</option> </select> </div> <button class="button" onclick="createUser()">Создать пользователя</button> <p style="font-size: 10px; color: #808080; margin-top: 10px;"> Примечание: Пароль будет сгенерирован автоматически и показан в отдельном окне. </p> </div> </div> </div> </div> </div> </div> <div class="taskbar"> <div class="start-button">Пуск</div> </div> <script> // ВАЖНО: Весь ваш JavaScript код остается полностью без изменений let authToken = localStorage.getItem('jwt_token'); let currentUser = null; // Проверка аутентификации при загрузке if (authToken) { fetch('/api/lab11/profile', { headers: { 'Authorization': `Bearer ${authToken}` } }) .then(response => { if (response.ok) { return response.json(); } throw new Error('Токен недействителен'); }) .then(data => { currentUser = data.user; if (currentUser.role === 'admin') { document.getElementById('add-user-form').style.display = 'block'; } }) .catch(err => { console.error('Ошибка проверки токена:', err); localStorage.removeItem('jwt_token'); authToken = null; alert('Сессия истекла или токен недействителен. Пожалуйста, войдите снова.'); window.location.href = '../'; // Перенаправляем на главную }); } else { alert('Сначала войдите в систему как администратор.'); window.location.href = '../'; // Перенаправляем на главную } function setError(elementId, message) { document.getElementById(elementId).innerHTML = `<div class="error">${message}</div>`; } function setSuccess(elementId, message) { document.getElementById(elementId).innerHTML = `<div class="success">${message}</div>`; } async function apiCall(url, method = 'GET', body = null, requiresAuth = true, requiresAdmin = true) { const headers = { 'Content-Type': 'application/json' }; // Установим по умолчанию if (authToken) { headers['Authorization'] = `Bearer ${authToken}`; } if (requiresAuth && !authToken) { alert('Сначала войдите в систему.'); return null; } const response = await fetch(url, { method, headers, body: body ? JSON.stringify(body) : undefined // Отправляем тело только если оно есть }); if (response.status === 401) { alert('Сессия истекла. Пожалуйста, войдите снова.'); localStorage.removeItem('jwt_token'); window.location.href = '../'; return null; } if (requiresAdmin && response.status === 403) { alert('Доступ запрещен. Требуется роль администратора.'); return null; } return response; } async function loadUsers() { if (!authToken) { document.getElementById('user-management-data').textContent = 'Сначала войдите в систему.'; return; } const response = await apiCall('/api/lab11/users', 'GET', null, true, true); if (response && response.ok) { const users = await response.json(); let html = `<h4>Список пользователей (${users.length})</h4>`; if (users.length === 0) { html += '<p>Нет данных.</p>'; } else { html += '<table><tr><th>ID</th><th>Логин</th><th>Роль</th><th>Дата создания</th><th>Действия</th></tr>'; users.forEach(user => { if (user.id !== currentUser.id) { // Не показываем кнопки для себя html += `<tr> <td>${user.id}</td> <td>${user.login}</td> <td>${user.role}</td> <td>${user.created_at}</td> <td> <button class="button" onclick="updateUserRole(${user.id})">Сделать ${user.role === 'admin' ? 'пользователем' : 'админом'}</button> <button class="button" onclick="deleteUser(${user.id})">Удалить</button> </td> </tr>`; } else { html += `<tr> <td>${user.id}</td> <td>${user.login}</td> <td>${user.role}</td> <td>${user.created_at}</td> <td>(Вы)</td> </tr>`; } }); html += '</table>'; } document.getElementById('user-management-data').innerHTML = html; } else { document.getElementById('user-management-data').textContent = 'Не удалось загрузить пользователей.'; } } async function updateUserRole(userId) { const newRole = prompt('Введите новую роль (user/admin):', 'user'); if (!newRole || (newRole !== 'user' && newRole !== 'admin')) { alert('Неверная роль. Используйте "user" или "admin".'); return; } const response = await apiCall(`/api/lab11/users/${userId}/role`, 'PUT', { role: newRole }, true, true); if (response && response.ok) { alert('Роль обновлена.'); loadUsers(); // Обновим список } else { const errorData = await response.json(); alert(errorData.error || 'Ошибка обновления роли'); } } // --- ИСПРАВЛЕННАЯ ФУНКЦИЯ УДАЛЕНИЯ --- async function deleteUser(userId) { if (!confirm('Вы уверены, что хотите удалить этого пользователя?')) { return; } // Подготовим заголовки: только Authorization const headers = {}; if (authToken) { headers['Authorization'] = `Bearer ${authToken}`; } // Выполняем DELETE запрос без тела и без Content-Type const response = await fetch(`/api/lab11/users/${userId}`, { method: 'DELETE', headers: headers, // Используем только Authorization }); if (response.ok) { // Успешное удаление (204 No Content) alert('Пользователь удален.'); loadUsers(); // Обновим список } else { // Обработка ошибок if (response.status === 400) { // Ошибка 400: Попытка удалить самого себя const errorData = await response.json(); alert(errorData.error || 'Ошибка удаления: Попытка удалить себя.'); } else if (response.status === 403) { // Ошибка 403: Доступ запрещен (не админ) alert('Ошибка удаления: Доступ запрещен.'); } else if (response.status === 404) { // Ошибка 404: Пользователь не найден alert('Ошибка удаления: Пользователь не найден.'); } else { // Другая ошибка (например, 500) alert('Ошибка удаления пользователя'); } } } async function createUser() { const login = document.getElementById('new-user-login').value; const role = document.getElementById('new-user-role').value; if (!login) { alert('Логин обязателен.'); return; } const passwordResponse = await apiCall('/api/lab11/generate-password', 'GET', null, true, true); if (!passwordResponse || !passwordResponse.ok) { alert('Не удалось сгенерировать пароль.'); return; } const passwordData = await passwordResponse.json(); const generatedPassword = passwordData.password; alert(`Сгенерированный пароль для ${login}: ${generatedPassword}\nОтправьте его пользователю.`); const response = await apiCall('/api/lab11/register', 'POST', { login, password: generatedPassword }, true, true); if (response && response.ok) { const data = await response.json(); const roleUpdateResponse = await apiCall(`/api/lab11/users/${data.id}/role`, 'PUT', { role: role }, true, true); if (roleUpdateResponse && roleUpdateResponse.ok) { alert(`Пользователь ${login} создан с ролью ${role}.`); document.getElementById('new-user-login').value = ''; loadUsers(); // Обновим список } else { const errorData = await roleUpdateResponse.json(); alert(`Пользователь ${login} создан, но установить роль не удалось: ${errorData.error}`); } } else { const errorData = await response.json(); alert(errorData.error || 'Ошибка создания пользователя'); } } </script> </body> </html>