/
Cooolprogrammers
/
web-nodejs-labs
Обзор
Документация
Войти
/
Cooolprogrammers
/
web-nodejs-labs
Код
Запросы
0
Задачи
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
public/lab11/task1/index.html
508 строк
18 KB
darkvoid6@mail.ru
ЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫ
18 дек 2025, 07:59
18 дек 2025, 07:59
f2c4f06
Код
Авторство
О чём код?
<!DOCTYPE html> <html lang="ru"> <head> <meta charset="UTF-8"> <title>Lab 11: JWT Security + Admin Edit</title> <style> body { font-family: 'JetBrains Mono', 'Courier New', monospace; padding: 25px; max-width: 900px; margin: 0 auto; background: #0a0e14; color: #10b981; background-image: radial-gradient(circle at 10% 20%, rgba(16, 185, 129, 0.05) 0%, transparent 20%), radial-gradient(circle at 90% 80%, rgba(16, 185, 129, 0.05) 0%, transparent 20%); } .box { border: 1px solid #374151; padding: 25px; margin-bottom: 25px; border-radius: 12px; background: #1f2937; box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2); backdrop-filter: blur(10px); transition: all 0.3s ease; } .box:hover { border-color: #10b981; box-shadow: 0 12px 40px rgba(16, 185, 129, 0.2); } .hidden { display: none; } input, select { padding: 12px; margin: 10px 0; display: block; width: 100%; max-width: 350px; box-sizing: border-box; background: #0a0e14; border: 1px solid #374151; border-radius: 8px; color: #a7f3d0; font-family: 'JetBrains Mono', monospace; transition: all 0.3s; } input:focus, select:focus { outline: none; border-color: #10b981; box-shadow: 0 0 0 3px rgba(16, 185, 129, 0.2); } button { padding: 12px 20px; cursor: pointer; border: 1px solid transparent; border-radius: 8px; margin-top: 10px; margin-right: 10px; font-family: 'JetBrains Mono', monospace; font-weight: 600; letter-spacing: 0.5px; transition: all 0.3s; } button:hover { transform: translateY(-2px); box-shadow: 0 6px 15px rgba(0, 0, 0, 0.3); } button.danger { background: transparent; color: #ef4444; border-color: #ef4444; } button.danger:hover { background: rgba(239, 68, 68, 0.1); } button.warning { background: transparent; color: #f59e0b; border-color: #f59e0b; } button.warning:hover { background: rgba(245, 158, 11, 0.1); } button.secondary { background: transparent; color: #6c757d; border-color: #6c757d; } button.secondary:hover { background: rgba(108, 117, 125, 0.1); } .error { color: #ef4444; font-weight: 600; } .success { color: #10b981; font-weight: 600; } table { width: 100%; border-collapse: collapse; margin-top: 20px; border: 1px solid #374151; border-radius: 8px; overflow: hidden; background: #1f2937; } th, td { border: 1px solid #374151; padding: 12px 15px; text-align: left; } th { background: rgba(16, 185, 129, 0.08); color: #34d399; font-weight: 700; text-transform: uppercase; letter-spacing: 1px; } td { color: #e5e7eb; } tr:hover { background: rgba(16, 185, 129, 0.05); } /* Стили для формы редактирования */ .edit-box { background: rgba(31, 41, 55, 0.8); border: 2px solid #10b981; animation: slideUp 0.3s ease; } h1, h3, h4 { color: #10b981; border-bottom: 1px solid #374151; padding-bottom: 15px; margin-bottom: 20px; } h1 { font-size: 2.2rem; text-align: center; margin-bottom: 30px; text-shadow: 0 0 10px rgba(16, 185, 129, 0.3); } #role-display { color: #34d399; background: rgba(16, 185, 129, 0.15); padding: 4px 12px; border-radius: 6px; border: 1px solid #10b981; } hr { border: none; height: 1px; background: #374151; margin: 25px 0; } label { display: block; margin: 15px 0 5px; color: #34d399; font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px; font-size: 0.9rem; } @keyframes slideUp { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: translateY(0); } } .success { background: transparent; color: #10b981; border-color: #10b981; } .success:hover { background: rgba(16, 185, 129, 0.1); } </style> </head> <body> <h1>🔐 Лабораторная работа №11 (Admin Full Edit)</h1> <!-- БЛОК АВТОРИЗАЦИИ --> <div id="auth-forms" class="box"> <h3>🔑 Вход / Регистрация</h3> <input type="text" id="login" placeholder="Логин"> <input type="password" id="password" placeholder="Пароль"> <button onclick="doLogin()">Войти</button> <button onclick="doRegister()">Зарегистрироваться</button> <p id="auth-msg"></p> </div> <!-- БЛОК ПОЛЬЗОВАТЕЛЯ --> <div id="user-panel" class="box hidden"> <h3>👋 Добро пожаловать, <span id="username-display"></span>!</h3> <p>Ваша роль: <b id="role-display"></b></p> <button class="danger" onclick="doLogout()">🚪 Выйти (Logout)</button> <hr> <!-- ЗАДАНИЕ 1: Смена пароля --> <h4>🔑 Смена пароля (Личный)</h4> <input type="password" id="oldPass" placeholder="Старый пароль"> <input type="password" id="newPass" placeholder="Новый пароль"> <button onclick="changePassword()">Сменить пароль</button> <p id="pass-msg"></p> </div> <!-- БЛОК СПИСКА ПОЛЬЗОВАТЕЛЕЙ (ЗАДАНИЕ 2) --> <div class="box"> <h3>👥 Список пользователей</h3> <button onclick="loadUsers()">🔄 Обновить список</button> <!-- Форма РЕДАКТИРОВАНИЯ (появляется при клике Edit) --> <div id="admin-edit-form" class="box edit-box hidden"> <h4>✏️ Редактирование пользователя ID: <span id="edit-id-display"></span></h4> <input type="hidden" id="edit-u-id"> <label>Логин:</label> <input type="text" id="edit-u-login"> <label>Роль:</label> <select id="edit-u-role"> <option value="user">👤 User</option> <option value="admin">👑 Admin</option> </select> <label>Новый пароль (оставьте пустым, если не менять):</label> <input type="text" id="edit-u-pass" placeholder="Введите для сброса пароля"> <div style="margin-top: 20px;"> <button class="success" onclick="saveUserEdit()">💾 Сохранить изменения</button> <button class="secondary" onclick="cancelEdit()">❌ Отмена</button> </div> </div> <table id="users-table"> <thead> <tr> <th>ID</th> <th>Login</th> <th>Role</th> <th>Actions (Admin)</th> </tr> </thead> <tbody></tbody> </table> <!-- Форма ДОБАВЛЕНИЯ для админа --> <div id="admin-create-form" class="hidden" style="margin-top: 25px; border-top: 2px dashed #374151; padding-top: 20px;"> <h4>➕ Добавить пользователя (Admin):</h4> <input type="text" id="new-u-login" placeholder="Login"> <input type="password" id="new-u-pass" placeholder="Password"> <select id="new-u-role"> <option value="user">👤 User</option> <option value="admin">👑 Admin</option> </select> <button onclick="createUserAdmin()">Создать</button> </div> </div> <script> const API = '/api/lab11'; let token = localStorage.getItem('token'); let currentUser = null; // Инициализация if (token) { parseToken(token); showPanel(true); } else { showPanel(false); } loadUsers(); function parseToken(t) { try { const payload = JSON.parse(atob(t.split('.')[1])); currentUser = payload; document.getElementById('username-display').innerText = payload.login; document.getElementById('role-display').innerText = payload.role; if(payload.role === 'admin') { document.getElementById('admin-create-form').classList.remove('hidden'); } } catch (e) { doLogout(); } } function showPanel(isAuth) { if (isAuth) { document.getElementById('auth-forms').classList.add('hidden'); document.getElementById('user-panel').classList.remove('hidden'); } else { document.getElementById('auth-forms').classList.remove('hidden'); document.getElementById('user-panel').classList.add('hidden'); document.getElementById('admin-create-form').classList.add('hidden'); document.getElementById('admin-edit-form').classList.add('hidden'); } } // --- ВХОД / РЕГИСТРАЦИЯ --- async function doLogin() { const login = document.getElementById('login').value; const password = document.getElementById('password').value; const res = await fetch(`${API}/login`, { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({login, password}) }); const data = await res.json(); if (res.ok) { token = data.token; localStorage.setItem('token', token); parseToken(token); showPanel(true); loadUsers(); document.getElementById('auth-msg').innerText = ''; } else { document.getElementById('auth-msg').innerText = data.error; document.getElementById('auth-msg').className = 'error'; } } async function doRegister() { const login = document.getElementById('login').value; const password = document.getElementById('password').value; const res = await fetch(`${API}/register`, { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({login, password}) }); if(res.ok) alert('✅ Успешно! Теперь войдите.'); else alert('❌ Ошибка регистрации'); } function doLogout() { localStorage.removeItem('token'); token = null; currentUser = null; location.reload(); } // --- ЗАДАНИЕ 1: Смена пароля (User) --- async function changePassword() { const oldPassword = document.getElementById('oldPass').value; const newPassword = document.getElementById('newPass').value; const res = await fetch(`${API}/change-password`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` }, body: JSON.stringify({oldPassword, newPassword}) }); const data = await res.json(); const p = document.getElementById('pass-msg'); p.innerText = res.ok ? data.message : data.error; p.className = res.ok ? 'success' : 'error'; } // --- ЗАДАНИЕ 2: Управление пользователями (Admin) --- async function loadUsers() { const headers = {}; if (token) headers['Authorization'] = `Bearer ${token}`; const res = await fetch(`${API}/users`, { headers }); const users = await res.json(); const tbody = document.querySelector('#users-table tbody'); tbody.innerHTML = ''; users.forEach(u => { const tr = document.createElement('tr'); let actions = '-'; if (currentUser && currentUser.role === 'admin') { actions = ` <button class="warning" onclick="startEdit('${u.id}', '${u.login}', '${u.role}')">✏️ Edit</button> <button class="danger" onclick="deleteUser(${u.id})">🗑️ Del</button> `; } tr.innerHTML = ` <td>${u.id || '-'}</td> <td>${u.login}</td> <td>${u.role || '-'}</td> <td>${actions}</td> `; tbody.appendChild(tr); }); } async function createUserAdmin() { const login = document.getElementById('new-u-login').value; const password = document.getElementById('new-u-pass').value; const role = document.getElementById('new-u-role').value; const res = await fetch(`${API}/users`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` }, body: JSON.stringify({login, password, role}) }); if(res.ok) { alert('✅ Пользователь создан'); document.getElementById('new-u-login').value = ''; document.getElementById('new-u-pass').value = ''; loadUsers(); } else { alert('❌ Ошибка создания'); } } async function deleteUser(id) { if(!confirm('Удалить пользователя?')) return; const res = await fetch(`${API}/users/${id}`, { method: 'DELETE', headers: { 'Authorization': `Bearer ${token}` } }); if(res.ok) loadUsers(); else alert('❌ Ошибка удаления'); } // --- НОВЫЙ ФУНКЦИОНАЛ: РЕДАКТИРОВАНИЕ --- function startEdit(id, login, role) { document.getElementById('admin-edit-form').classList.remove('hidden'); document.getElementById('edit-id-display').innerText = id; document.getElementById('edit-u-id').value = id; document.getElementById('edit-u-login').value = login; document.getElementById('edit-u-role').value = role; document.getElementById('edit-u-pass').value = ''; document.getElementById('admin-edit-form').scrollIntoView({behavior: 'smooth'}); } function cancelEdit() { document.getElementById('admin-edit-form').classList.add('hidden'); } async function saveUserEdit() { const id = document.getElementById('edit-u-id').value; const login = document.getElementById('edit-u-login').value; const role = document.getElementById('edit-u-role').value; const password = document.getElementById('edit-u-pass').value; const bodyData = { login, role }; if (password && password.trim() !== '') { bodyData.password = password; } const res = await fetch(`${API}/users/${id}`, { method: 'PUT', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` }, body: JSON.stringify(bodyData) }); if (res.ok) { alert('✅ Данные обновлены!'); cancelEdit(); loadUsers(); } else { const data = await res.json(); alert('❌ Ошибка обновления: ' + (data.error || res.statusText)); } } </script> </body> </html>