espid2sensor: User-Liste mit Passwort-Reset und Löschen im User-Tab

Ersetzt das manuelle E-Mail-Eingabefeld für den Passwort-Reset durch
eine Liste aller angemeldeten User. Pro User stehen zwei Aktionen zur
Verfügung: Passwort zurücksetzen (nutzt die bestehende
/api/resetPassword-Route) und Löschen (neue DELETE /api/users/:id-Route,
admin-only, verhindert Löschen des eigenen Accounts). Neue GET
/api/users-Route liefert Email/Rolle aller User (admin-only).
This commit is contained in:
2026-08-07 08:03:27 +00:00
parent 278f91ed3c
commit 19c6facf71
3 changed files with 104 additions and 38 deletions
+70 -29
View File
@@ -34,6 +34,7 @@ document.addEventListener('DOMContentLoaded', () => {
const data = await res.json();
if (data.success) {
userResult.textContent = 'User erfolgreich angelegt!';
if (window.loadUsers) window.loadUsers();
} else {
userResult.textContent = data.error || 'Fehler beim Anlegen.';
}
@@ -77,34 +78,6 @@ document.addEventListener('DOMContentLoaded', () => {
});
}
const resetPasswordBtn = document.getElementById('resetPasswordBtn');
if (resetPasswordBtn) {
resetPasswordBtn.addEventListener('click', async () => {
const email = document.getElementById('resetEmail').value.trim();
const newPassword = document.getElementById('resetNewPassword').value;
const resetPasswordResult = document.getElementById('resetPasswordResult');
if (!email || !newPassword) {
resetPasswordResult.textContent = 'Email und neues Passwort erforderlich.';
return;
}
try {
const res = await fetch('/api/resetPassword', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, newPassword })
});
const data = await res.json();
if (data.success) {
resetPasswordResult.textContent = 'Passwort erfolgreich zurückgesetzt!';
document.getElementById('resetPasswordForm').reset();
} else {
resetPasswordResult.textContent = data.error || 'Fehler beim Zurücksetzen.';
}
} catch (err) {
resetPasswordResult.textContent = 'Serverfehler.';
}
});
}
});
function updateSortArrows() {
@@ -467,6 +440,73 @@ function showModal(message, showCancelButton, callback) {
});
}
async function loadUsers() {
const usersTableBody = document.querySelector('#usersTable tbody');
const usersResult = document.getElementById('usersResult');
if (!usersTableBody) return;
try {
const res = await fetch('/api/users');
const users = await res.json();
usersTableBody.innerHTML = '';
users.forEach(u => {
const tr = document.createElement('tr');
tr.innerHTML = `
<td>${u.email}</td>
<td>${u.role}</td>
<td>
<div class="twobuttons">
<button data-id="${u._id}" data-email="${u.email}" class="editBtn resetUserBtn" title="Passwort zurücksetzen">🔑</button>
<button data-id="${u._id}" class="deleteBtn deleteUserBtn" title="Löschen">🗑️</button>
</div>
</td>
`;
usersTableBody.appendChild(tr);
});
document.querySelectorAll('.resetUserBtn').forEach(btn => {
btn.addEventListener('click', async () => {
const email = btn.getAttribute('data-email');
const newPassword = prompt(`Neues Passwort für ${email}:`);
if (!newPassword) return;
try {
const res = await fetch('/api/resetPassword', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, newPassword })
});
const data = await res.json();
usersResult.textContent = data.success ? `Passwort für ${email} zurückgesetzt.` : (data.error || 'Fehler beim Zurücksetzen.');
} catch (err) {
usersResult.textContent = 'Serverfehler.';
}
});
});
document.querySelectorAll('.deleteUserBtn').forEach(btn => {
btn.addEventListener('click', () => {
const id = btn.getAttribute('data-id');
showModal('User wirklich löschen?', true, async (confirmed) => {
if (!confirmed) return;
try {
const res = await fetch(`/api/users/${id}`, { method: 'DELETE' });
const data = await res.json();
if (data.success) {
usersResult.textContent = 'User gelöscht.';
await loadUsers();
} else {
usersResult.textContent = data.error || 'Fehler beim Löschen.';
}
} catch (err) {
usersResult.textContent = 'Serverfehler.';
}
});
});
});
} catch (err) {
console.error(err);
}
}
saveBtn.addEventListener('click', saveEntry);
refreshBtn.addEventListener('click', loadEntries);
cancelBtn.addEventListener('click', () => clearForm(true));
@@ -475,9 +515,10 @@ function showModal(message, showCancelButton, callback) {
tabInput.addEventListener('click', () => showTab('input'))
tabList.addEventListener('click', () => showTab('list'))
const tabUser = document.getElementById('tabUser');
if (tabUser) tabUser.addEventListener('click', () => showTab('user'))
if (tabUser) tabUser.addEventListener('click', () => { showTab('user'); loadUsers(); })
loadEntries();
window.loadUsers = loadUsers;
});
window.showTab = showTab;