Backend: - AdminEndpoint.cs: 5 Endpunkte (List, Create, Update, Reset PW, Delete) - Admin-only - DatabaseService: CreateUser(), UpdateUserAdmin(), DeleteUser() (kaskadiert Tokens + Shares) - Route in Program.cs registriert Frontend: - admin-page.js: User-Liste mit Edit/Delete, Inline-Formular Create/Edit - api.js: 5 Admin-API-Funktionen - index.html: Template tpl-admin, Script-Tag, Dashboard-Link 'Benutzerverwaltung' - dashboard.js: Admin-Link nur für Admins sichtbar - form.css: Styles für admin-user-list, admin-user-row, admin-checkbox-row - dashboard.css: Header-Overflow-Fix, Agents vor Cronjobs - index.html: Template-Reihenfolge Agents vor Cronjobs TODO: User-Management als erledigt markiert
213 lines
6.7 KiB
JavaScript
213 lines
6.7 KiB
JavaScript
// ── Admin Page (User Management) ──
|
|
|
|
registerPage('admin', {
|
|
async init() {
|
|
if (!currentUser || !currentUser.isAdmin) {
|
|
navigateTo('dashboard');
|
|
return;
|
|
}
|
|
|
|
var users = [];
|
|
var editingUserId = null;
|
|
|
|
var list = document.getElementById('adminUserList');
|
|
var newUserBtn = document.getElementById('adminNewUserBtn');
|
|
var userForm = document.getElementById('adminUserForm');
|
|
var formTitle = document.getElementById('adminFormTitle');
|
|
var usernameInput = document.getElementById('adminUsername');
|
|
var displayNameInput = document.getElementById('adminDisplayName');
|
|
var passwordInput = document.getElementById('adminPassword');
|
|
var passwordLabel = document.getElementById('adminPasswordLabel');
|
|
var isAdminCheckbox = document.getElementById('adminIsAdmin');
|
|
var saveBtn = document.getElementById('adminFormSaveBtn');
|
|
var formMessage = document.getElementById('adminFormMessage');
|
|
var pageMessage = document.getElementById('adminMessage');
|
|
|
|
// ── Hilfsfunktionen ──
|
|
|
|
function showFormMessage(text, isError) {
|
|
formMessage.textContent = text;
|
|
formMessage.className = 'profile-message ' + (isError ? 'profile-message-error' : 'profile-message-success');
|
|
formMessage.hidden = false;
|
|
if (!isError) {
|
|
setTimeout(function() { formMessage.hidden = true; }, 3000);
|
|
}
|
|
}
|
|
|
|
function showPageMessage(text, isError) {
|
|
pageMessage.textContent = text;
|
|
pageMessage.className = 'profile-message ' + (isError ? 'profile-message-error' : 'profile-message-success');
|
|
pageMessage.hidden = false;
|
|
setTimeout(function() { pageMessage.hidden = true; }, 4000);
|
|
}
|
|
|
|
function renderUsers() {
|
|
list.innerHTML = '';
|
|
|
|
users.forEach(function(user) {
|
|
var row = document.createElement('div');
|
|
row.className = 'admin-user-row';
|
|
|
|
var info = document.createElement('div');
|
|
info.className = 'admin-user-info';
|
|
|
|
var nameSpan = document.createElement('span');
|
|
nameSpan.className = 'admin-user-name';
|
|
nameSpan.textContent = user.displayName || user.username;
|
|
|
|
var metaSpan = document.createElement('span');
|
|
metaSpan.className = 'admin-user-meta';
|
|
var metaParts = ['@' + user.username];
|
|
if (user.isAdmin) metaParts.push('Administrator');
|
|
metaSpan.textContent = metaParts.join(' ▪ ');
|
|
|
|
info.appendChild(nameSpan);
|
|
info.appendChild(metaSpan);
|
|
|
|
var actions = document.createElement('div');
|
|
actions.className = 'admin-user-actions';
|
|
|
|
var editBtn = document.createElement('button');
|
|
editBtn.className = 'icon-btn';
|
|
editBtn.title = 'Bearbeiten';
|
|
editBtn.innerHTML = '<i class="fa fa-pencil"></i>';
|
|
editBtn.addEventListener('click', function() {
|
|
startEdit(user);
|
|
});
|
|
actions.appendChild(editBtn);
|
|
|
|
// Sich selbst kann man nicht löschen
|
|
if (user.userId !== currentUser.userId) {
|
|
var deleteBtn = document.createElement('button');
|
|
deleteBtn.className = 'icon-btn';
|
|
deleteBtn.title = 'Löschen';
|
|
deleteBtn.innerHTML = '<i class="fa fa-trash"></i>';
|
|
deleteBtn.addEventListener('click', function() {
|
|
deleteUser(user);
|
|
});
|
|
actions.appendChild(deleteBtn);
|
|
}
|
|
|
|
row.appendChild(info);
|
|
row.appendChild(actions);
|
|
list.appendChild(row);
|
|
});
|
|
}
|
|
|
|
async function loadUsers() {
|
|
try {
|
|
users = await apiAdminGetUsers();
|
|
renderUsers();
|
|
} catch (err) {
|
|
showPageMessage(err.message, true);
|
|
}
|
|
}
|
|
|
|
function startEdit(user) {
|
|
editingUserId = user.userId;
|
|
formTitle.textContent = 'Benutzer bearbeiten';
|
|
usernameInput.value = user.username;
|
|
usernameInput.disabled = false;
|
|
displayNameInput.value = user.displayName || '';
|
|
passwordInput.value = '';
|
|
passwordLabel.textContent = 'Neues Passwort (leer = unverändert)';
|
|
passwordInput.placeholder = 'Neues Passwort (optional)';
|
|
passwordInput.required = false;
|
|
isAdminCheckbox.checked = user.isAdmin;
|
|
saveBtn.textContent = 'Speichern';
|
|
formMessage.hidden = true;
|
|
userForm.hidden = false;
|
|
usernameInput.focus();
|
|
}
|
|
|
|
async function deleteUser(user) {
|
|
if (!confirm('Benutzer "' + (user.displayName || user.username) + '" wirklich löschen?\n\nAlle Tokens werden ebenfalls gelöscht.')) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
await apiAdminDeleteUser(user.userId);
|
|
await loadUsers();
|
|
} catch (err) {
|
|
showPageMessage(err.message, true);
|
|
}
|
|
}
|
|
|
|
// ── Event-Listener ──
|
|
|
|
// Neuer Benutzer
|
|
newUserBtn.addEventListener('click', function() {
|
|
editingUserId = null;
|
|
formTitle.textContent = 'Neuer Benutzer';
|
|
usernameInput.value = '';
|
|
usernameInput.disabled = false;
|
|
displayNameInput.value = '';
|
|
passwordInput.value = '';
|
|
passwordLabel.textContent = 'Passwort';
|
|
passwordInput.placeholder = 'Passwort';
|
|
passwordInput.required = true;
|
|
isAdminCheckbox.checked = false;
|
|
saveBtn.textContent = 'Erstellen';
|
|
formMessage.hidden = true;
|
|
userForm.hidden = false;
|
|
usernameInput.focus();
|
|
});
|
|
|
|
// Speichern
|
|
saveBtn.addEventListener('click', async function() {
|
|
formMessage.hidden = true;
|
|
saveBtn.disabled = true;
|
|
|
|
try {
|
|
if (editingUserId) {
|
|
// Update
|
|
var data = {
|
|
username: usernameInput.value.trim(),
|
|
displayName: displayNameInput.value.trim(),
|
|
isAdmin: isAdminCheckbox.checked
|
|
};
|
|
await apiAdminUpdateUser(editingUserId, data);
|
|
|
|
// Passwort nur wenn ausgefüllt
|
|
var pw = passwordInput.value;
|
|
if (pw) {
|
|
await apiAdminResetPassword(editingUserId, pw);
|
|
}
|
|
|
|
showFormMessage('Benutzer aktualisiert', false);
|
|
} else {
|
|
// Create
|
|
var username = usernameInput.value.trim();
|
|
var password = passwordInput.value;
|
|
|
|
if (!username || !password) {
|
|
showFormMessage('Benutzername und Passwort erforderlich', true);
|
|
return;
|
|
}
|
|
|
|
await apiAdminCreateUser({
|
|
username: username,
|
|
password: password,
|
|
displayName: displayNameInput.value.trim(),
|
|
isAdmin: isAdminCheckbox.checked
|
|
});
|
|
|
|
showFormMessage('Benutzer erstellt', false);
|
|
}
|
|
|
|
userForm.hidden = true;
|
|
await loadUsers();
|
|
} catch (err) {
|
|
showFormMessage(err.message, true);
|
|
} finally {
|
|
saveBtn.disabled = false;
|
|
}
|
|
});
|
|
|
|
// ── Init: User laden ──
|
|
await loadUsers();
|
|
},
|
|
|
|
destroy() {}
|
|
});
|