Nutzerverwaltung-Upgrade: 3 Rollen, Ordner-Zuweisung, Passwort-Aenderung
- 3 Rollen: admin (voll), user (permanent), client (30 Tage Ablauf) - Ordner-basierte Bildzuweisung statt Einzelbilder (user_folders Tabelle) - Root-Bilder oeffentlich, Unterordner nur fuer zugewiesene Nutzer - Passwort-Aenderung fuer alle Nutzer (Self-Service + Admin) - Admin kann beliebige Admins erstellen/loeschen - Thumbnail-Generierung fuer Unterordner - Admin-Dashboard komplett ueberarbeitet Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,101 +1,167 @@
|
||||
$(document).ready(function() {
|
||||
// Check if admin
|
||||
// Auth check
|
||||
$.get('/api/auth/me')
|
||||
.done(function(user) {
|
||||
if(user.role !== 'admin') {
|
||||
alert("Kein Zugriff!");
|
||||
alert('Kein Zugriff!');
|
||||
window.location.href = '/';
|
||||
} else {
|
||||
loadUsers();
|
||||
loadAssignmentImages();
|
||||
loadFolders();
|
||||
$('body').removeClass('is-preload');
|
||||
}
|
||||
})
|
||||
.fail(function() {
|
||||
window.location.href = '/';
|
||||
});
|
||||
.fail(function() { window.location.href = '/'; });
|
||||
|
||||
// Logout
|
||||
$('#logout-btn').click(function(e) {
|
||||
e.preventDefault();
|
||||
$.post('/api/auth/logout').done(function() { window.location.href = '/'; });
|
||||
});
|
||||
|
||||
// ==================== NUTZER ====================
|
||||
|
||||
function loadUsers() {
|
||||
$.get('/api/admin/users').done(function(users) {
|
||||
$('#users-tbody').empty();
|
||||
$('#user-select').html('<option value="">Nutzer wählen...</option>');
|
||||
$('#user-select').html('<option value="">Nutzer waehlen...</option>');
|
||||
|
||||
users.forEach(function(u) {
|
||||
var badge = '<span class="badge badge-' + u.role + '">' + u.role + '</span>';
|
||||
var created = u.created_at ? new Date(u.created_at).toLocaleDateString('de-DE') : '-';
|
||||
var info = created;
|
||||
|
||||
if (u.role === 'client' && u.expires_at) {
|
||||
var expDate = new Date(u.expires_at).toLocaleDateString('de-DE');
|
||||
if (u.expired) {
|
||||
badge = '<span class="badge badge-expired">abgelaufen</span>';
|
||||
info = 'Abgelaufen: ' + expDate;
|
||||
} else {
|
||||
info = 'Bis: ' + expDate;
|
||||
}
|
||||
}
|
||||
|
||||
var actions = '<button class="btn-small" onclick="changePassword(' + u.id + ', \'' + u.username.replace(/'/g, "\\'") + '\')">PW</button>';
|
||||
actions += '<button class="btn-small" onclick="deleteUser(' + u.id + ', \'' + u.username.replace(/'/g, "\\'") + '\')">X</button>';
|
||||
|
||||
$('#users-tbody').append(
|
||||
'<tr>' +
|
||||
'<td>' + u.id + '</td>' +
|
||||
'<td>' + u.username + ' ' + (u.role === 'admin' ? '(Admin)' : '') + '</td>' +
|
||||
'<td>' + (u.id !== 1 ? '<button onclick="deleteUser(' + u.id + ')">Löschen</button>' : '') + '</td>' +
|
||||
'</tr>'
|
||||
'<tr><td>' + u.id + '</td><td>' + u.username + ' ' + badge +
|
||||
'</td><td>' + u.role + '</td><td>' + info +
|
||||
'</td><td>' + actions + '</td></tr>'
|
||||
);
|
||||
if(u.role !== 'admin') {
|
||||
$('#user-select').append('<option value="' + u.id + '">' + u.username + '</option>');
|
||||
|
||||
// Dropdown fuer Ordner-Zuweisung (nur non-admin)
|
||||
if (u.role !== 'admin') {
|
||||
$('#user-select').append('<option value="' + u.id + '">' + u.username + ' (' + u.role + ')</option>');
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
window.deleteUser = function(id) {
|
||||
if(confirm("Nutzer loeschen?")) {
|
||||
$.ajax({url: '/api/admin/users/'+id, type: 'DELETE'}).done(loadUsers);
|
||||
// Nutzer erstellen
|
||||
$('#create-user-form').submit(function(e) {
|
||||
e.preventDefault();
|
||||
$.post('/api/admin/users', {
|
||||
username: $('#new-username').val(),
|
||||
password: $('#new-password').val(),
|
||||
role: $('#new-role').val()
|
||||
}).done(function() {
|
||||
$('#new-username').val('');
|
||||
$('#new-password').val('');
|
||||
loadUsers();
|
||||
}).fail(function(err) {
|
||||
alert('Fehler: ' + (err.responseJSON ? err.responseJSON.error : 'Unbekannt'));
|
||||
});
|
||||
});
|
||||
|
||||
// Nutzer loeschen
|
||||
window.deleteUser = function(id, name) {
|
||||
if (confirm('Nutzer "' + name + '" wirklich loeschen?')) {
|
||||
$.ajax({ url: '/api/admin/users/' + id, type: 'DELETE' })
|
||||
.done(loadUsers)
|
||||
.fail(function(err) { alert(err.responseJSON ? err.responseJSON.error : 'Fehler'); });
|
||||
}
|
||||
};
|
||||
|
||||
$('#create-user-form').submit(function(e) {
|
||||
e.preventDefault();
|
||||
$.post('/api/admin/users', { username: $('#new-username').val(), password: $('#new-password').val(), role: 'user' })
|
||||
.done(function() {
|
||||
$('#new-username').val('');
|
||||
$('#new-password').val('');
|
||||
loadUsers();
|
||||
}).fail(function(err) { alert("Fehler: " + err.responseJSON.error); });
|
||||
});
|
||||
// Passwort aendern (Admin setzt fuer anderen User)
|
||||
window.changePassword = function(id, name) {
|
||||
var newPw = prompt('Neues Passwort fuer "' + name + '":');
|
||||
if (!newPw) return;
|
||||
$.ajax({
|
||||
url: '/api/admin/users/' + id + '/password',
|
||||
type: 'PUT',
|
||||
contentType: 'application/json',
|
||||
data: JSON.stringify({ new_password: newPw })
|
||||
}).done(function() {
|
||||
alert('Passwort geaendert!');
|
||||
}).fail(function(err) {
|
||||
alert(err.responseJSON ? err.responseJSON.error : 'Fehler');
|
||||
});
|
||||
};
|
||||
|
||||
function loadAssignmentImages() {
|
||||
$.get('/images/fulls/').done(function(files) {
|
||||
$('#image-checkboxes').empty();
|
||||
files.forEach(function(f) {
|
||||
$('#image-checkboxes').append(
|
||||
'<label class="img-check">' +
|
||||
'<img src="/images/thumbs/' + encodeURIComponent(f.name) + '" />' +
|
||||
'<br/><input type="checkbox" value="' + f.name + '" class="img-cb"/>' +
|
||||
'</label>'
|
||||
// ==================== ORDNER-ZUWEISUNG ====================
|
||||
|
||||
function loadFolders() {
|
||||
$.get('/api/admin/folders').done(function(folders) {
|
||||
$('#folder-checkboxes').empty();
|
||||
if (folders.length === 0) {
|
||||
$('#folder-checkboxes').html('<p>Keine Unterordner in Websitebilder gefunden. Erstelle Ordner in Nextcloud.</p>');
|
||||
return;
|
||||
}
|
||||
folders.forEach(function(f) {
|
||||
$('#folder-checkboxes').append(
|
||||
'<div class="folder-check"><label>' +
|
||||
'<input type="checkbox" value="' + f + '" class="folder-cb"/> ' +
|
||||
'<i class="fa fa-folder"></i> ' + f +
|
||||
'</label></div>'
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Bei User-Auswahl: zugewiesene Ordner laden
|
||||
$('#user-select').change(function() {
|
||||
var uid = $(this).val();
|
||||
$('.img-cb').prop('checked', false);
|
||||
if(!uid) return;
|
||||
$.get('/api/admin/assign').done(function(assignments) {
|
||||
assignments.filter(function(a) { return a.user_id == uid; }).forEach(function(a) {
|
||||
$('.img-cb[value="' + a.image_name + '"]').prop('checked', true);
|
||||
$('.folder-cb').prop('checked', false);
|
||||
if (!uid) return;
|
||||
$.get('/api/admin/assign?user_id=' + uid).done(function(assignedFolders) {
|
||||
assignedFolders.forEach(function(f) {
|
||||
$('.folder-cb[value="' + f + '"]').prop('checked', true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Zuweisung speichern
|
||||
$('#save-assignments').click(function() {
|
||||
var uid = $('#user-select').val();
|
||||
if(!uid) return alert('Bitte Nutzer waehlen');
|
||||
if (!uid) return alert('Bitte Nutzer waehlen');
|
||||
var selected = [];
|
||||
$('.img-cb:checked').each(function() { selected.push($(this).val()); });
|
||||
|
||||
$.post('/api/admin/assign', { user_id: uid, image_names: selected })
|
||||
.done(function() { alert("Zuweisung gespeichert!"); });
|
||||
$('.folder-cb:checked').each(function() { selected.push($(this).val()); });
|
||||
|
||||
$.ajax({
|
||||
url: '/api/admin/assign',
|
||||
type: 'POST',
|
||||
contentType: 'application/json',
|
||||
data: JSON.stringify({ user_id: uid, folders: selected })
|
||||
}).done(function() {
|
||||
$('#assign-status').text('Gespeichert!').fadeIn().delay(2000).fadeOut();
|
||||
}).fail(function() {
|
||||
alert('Fehler beim Speichern');
|
||||
});
|
||||
});
|
||||
|
||||
$('#update-btn').click(function() {
|
||||
if(!confirm("Achtung: Dies startet den Server auf dem NAS neu. Fortfahren?")) return;
|
||||
$('#update-status').text('Update laeuft...');
|
||||
$.post('/api/admin/update').done(function(res) {
|
||||
$('#update-status').text(res.message);
|
||||
setTimeout(function() { window.location.reload(); }, 5000);
|
||||
// ==================== EIGENES PASSWORT ====================
|
||||
|
||||
$('#admin-pw-form').submit(function(e) {
|
||||
e.preventDefault();
|
||||
$.post('/api/auth/change-password', {
|
||||
old_password: $('#admin-pw-old').val(),
|
||||
new_password: $('#admin-pw-new').val()
|
||||
}).done(function() {
|
||||
alert('Passwort geaendert!');
|
||||
$('#admin-pw-old, #admin-pw-new').val('');
|
||||
}).fail(function(err) {
|
||||
alert(err.responseJSON ? err.responseJSON.error : 'Fehler');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
2
assets/js/admin.min.js
vendored
2
assets/js/admin.min.js
vendored
@@ -1 +1 @@
|
||||
$(document).ready(function(){function e(){$.get("/api/admin/users").done(function(e){$("#users-tbody").empty(),$("#user-select").html('<option value="">Nutzer wählen...</option>'),e.forEach(function(e){$("#users-tbody").append("<tr><td>"+e.id+"</td><td>"+e.username+" "+("admin"===e.role?"(Admin)":"")+"</td><td>"+(1!==e.id?'<button onclick="deleteUser('+e.id+')">Löschen</button>':"")+"</td></tr>"),"admin"!==e.role&&$("#user-select").append('<option value="'+e.id+'">'+e.username+"</option>")})})}$.get("/api/auth/me").done(function(n){"admin"!==n.role?(alert("Kein Zugriff!"),window.location.href="/"):(e(),$.get("/images/fulls/").done(function(e){$("#image-checkboxes").empty(),e.forEach(function(e){$("#image-checkboxes").append('<label class="img-check"><img src="/images/thumbs/'+encodeURIComponent(e.name)+'" /><br/><input type="checkbox" value="'+e.name+'" class="img-cb"/></label>')})}),$("body").removeClass("is-preload"))}).fail(function(){window.location.href="/"}),$("#logout-btn").click(function(e){e.preventDefault(),$.post("/api/auth/logout").done(function(){window.location.href="/"})}),window.deleteUser=function(n){confirm("Nutzer loeschen?")&&$.ajax({url:"/api/admin/users/"+n,type:"DELETE"}).done(e)},$("#create-user-form").submit(function(n){n.preventDefault(),$.post("/api/admin/users",{username:$("#new-username").val(),password:$("#new-password").val(),role:"user"}).done(function(){$("#new-username").val(""),$("#new-password").val(""),e()}).fail(function(e){alert("Fehler: "+e.responseJSON.error)})}),$("#user-select").change(function(){var e=$(this).val();$(".img-cb").prop("checked",!1),e&&$.get("/api/admin/assign").done(function(n){n.filter(function(n){return n.user_id==e}).forEach(function(e){$('.img-cb[value="'+e.image_name+'"]').prop("checked",!0)})})}),$("#save-assignments").click(function(){var e=$("#user-select").val();if(!e)return alert("Bitte Nutzer waehlen");var n=[];$(".img-cb:checked").each(function(){n.push($(this).val())}),$.post("/api/admin/assign",{user_id:e,image_names:n}).done(function(){alert("Zuweisung gespeichert!")})}),$("#update-btn").click(function(){confirm("Achtung: Dies startet den Server auf dem NAS neu. Fortfahren?")&&($("#update-status").text("Update laeuft..."),$.post("/api/admin/update").done(function(e){$("#update-status").text(e.message),setTimeout(function(){window.location.reload()},5e3)}))})});
|
||||
$(document).ready(function(){function e(){$.get("/api/admin/users").done(function(e){$("#users-tbody").empty(),$("#user-select").html('<option value="">Nutzer waehlen...</option>'),e.forEach(function(e){var n='<span class="badge badge-'+e.role+'">'+e.role+"</span>",a=e.created_at?new Date(e.created_at).toLocaleDateString("de-DE"):"-";if("client"===e.role&&e.expires_at){var t=new Date(e.expires_at).toLocaleDateString("de-DE");e.expired?(n='<span class="badge badge-expired">abgelaufen</span>',a="Abgelaufen: "+t):a="Bis: "+t}var r='<button class="btn-small" onclick="changePassword('+e.id+", '"+e.username.replace(/'/g,"\\'")+"')\">PW</button>";r+='<button class="btn-small" onclick="deleteUser('+e.id+", '"+e.username.replace(/'/g,"\\'")+"')\">X</button>",$("#users-tbody").append("<tr><td>"+e.id+"</td><td>"+e.username+" "+n+"</td><td>"+e.role+"</td><td>"+a+"</td><td>"+r+"</td></tr>"),"admin"!==e.role&&$("#user-select").append('<option value="'+e.id+'">'+e.username+" ("+e.role+")</option>")})})}$.get("/api/auth/me").done(function(n){"admin"!==n.role?(alert("Kein Zugriff!"),window.location.href="/"):(e(),$.get("/api/admin/folders").done(function(e){$("#folder-checkboxes").empty(),0!==e.length?e.forEach(function(e){$("#folder-checkboxes").append('<div class="folder-check"><label><input type="checkbox" value="'+e+'" class="folder-cb"/> <i class="fa fa-folder"></i> '+e+"</label></div>")}):$("#folder-checkboxes").html("<p>Keine Unterordner in Websitebilder gefunden. Erstelle Ordner in Nextcloud.</p>")}),$("body").removeClass("is-preload"))}).fail(function(){window.location.href="/"}),$("#logout-btn").click(function(e){e.preventDefault(),$.post("/api/auth/logout").done(function(){window.location.href="/"})}),$("#create-user-form").submit(function(n){n.preventDefault(),$.post("/api/admin/users",{username:$("#new-username").val(),password:$("#new-password").val(),role:$("#new-role").val()}).done(function(){$("#new-username").val(""),$("#new-password").val(""),e()}).fail(function(e){alert("Fehler: "+(e.responseJSON?e.responseJSON.error:"Unbekannt"))})}),window.deleteUser=function(n,a){confirm('Nutzer "'+a+'" wirklich loeschen?')&&$.ajax({url:"/api/admin/users/"+n,type:"DELETE"}).done(e).fail(function(e){alert(e.responseJSON?e.responseJSON.error:"Fehler")})},window.changePassword=function(e,n){var a=prompt('Neues Passwort fuer "'+n+'":');a&&$.ajax({url:"/api/admin/users/"+e+"/password",type:"PUT",contentType:"application/json",data:JSON.stringify({new_password:a})}).done(function(){alert("Passwort geaendert!")}).fail(function(e){alert(e.responseJSON?e.responseJSON.error:"Fehler")})},$("#user-select").change(function(){var e=$(this).val();$(".folder-cb").prop("checked",!1),e&&$.get("/api/admin/assign?user_id="+e).done(function(e){e.forEach(function(e){$('.folder-cb[value="'+e+'"]').prop("checked",!0)})})}),$("#save-assignments").click(function(){var e=$("#user-select").val();if(!e)return alert("Bitte Nutzer waehlen");var n=[];$(".folder-cb:checked").each(function(){n.push($(this).val())}),$.ajax({url:"/api/admin/assign",type:"POST",contentType:"application/json",data:JSON.stringify({user_id:e,folders:n})}).done(function(){$("#assign-status").text("Gespeichert!").fadeIn().delay(2e3).fadeOut()}).fail(function(){alert("Fehler beim Speichern")})}),$("#admin-pw-form").submit(function(e){e.preventDefault(),$.post("/api/auth/change-password",{old_password:$("#admin-pw-old").val(),new_password:$("#admin-pw-new").val()}).done(function(){alert("Passwort geaendert!"),$("#admin-pw-old, #admin-pw-new").val("")}).fail(function(e){alert(e.responseJSON?e.responseJSON.error:"Fehler")})})});
|
||||
@@ -182,8 +182,11 @@
|
||||
|
||||
images.forEach(function(file) {
|
||||
var article = $('<article class="thumb"></article>');
|
||||
var link = $('<a class="image" href="/images/fulls/' + encodeURIComponent(file.name) + '"></a>');
|
||||
var img = $('<img src="/images/thumbs/' + encodeURIComponent(file.name) + '" alt="" data-name="' + file.name.replace(/"/g, '"') + '" />');
|
||||
var prefix = file.folder ? encodeURIComponent(file.folder) + '/' : '';
|
||||
var fullPath = '/images/fulls/' + prefix + encodeURIComponent(file.name);
|
||||
var thumbPath = '/images/thumbs/' + prefix + encodeURIComponent(file.name);
|
||||
var link = $('<a class="image" href="' + fullPath + '"></a>');
|
||||
var img = $('<img src="' + thumbPath + '" alt="" data-name="' + file.name.replace(/"/g, '"') + '" />');
|
||||
link.append(img);
|
||||
article.append(link);
|
||||
$main.append(article);
|
||||
@@ -286,6 +289,7 @@
|
||||
$.get('/api/auth/me').done(function(u) {
|
||||
$('#login-username, #login-password, #login-submit').hide();
|
||||
$('#logout-btn').show().css('display', 'inline-block');
|
||||
$('#pw-change-section').show();
|
||||
if(u.role === 'admin') {
|
||||
$('#admin-link').html('<a href="/admin.html" class="button">Admin-Dashboard</a>');
|
||||
}
|
||||
@@ -296,7 +300,13 @@
|
||||
e.preventDefault();
|
||||
$.post('/api/auth/login', { username: $('#login-username').val(), password: $('#login-password').val() })
|
||||
.done(function() { window.location.reload(); })
|
||||
.fail(function(err) { alert("Login fehlgeschlagen. Bitte Zugangsdaten ueberpruefen."); });
|
||||
.fail(function(xhr) {
|
||||
var msg = 'Login fehlgeschlagen.';
|
||||
if (xhr.responseJSON && xhr.responseJSON.error === 'Account abgelaufen') {
|
||||
msg = 'Dein Account ist abgelaufen. Bitte kontaktiere den Administrator.';
|
||||
}
|
||||
alert(msg);
|
||||
});
|
||||
});
|
||||
|
||||
$('#logout-btn').click(function(e) {
|
||||
@@ -304,4 +314,14 @@
|
||||
$.post('/api/auth/logout').done(function() { window.location.reload(); });
|
||||
});
|
||||
|
||||
$('#pw-change-form').submit(function(e) {
|
||||
e.preventDefault();
|
||||
var newPw = $('#pw-new').val();
|
||||
var confirmPw = $('#pw-confirm').val();
|
||||
if (newPw !== confirmPw) { alert('Passwoerter stimmen nicht ueberein.'); return; }
|
||||
$.post('/api/auth/change-password', { old_password: $('#pw-old').val(), new_password: newPw })
|
||||
.done(function() { alert('Passwort geaendert!'); $('#pw-old, #pw-new, #pw-confirm').val(''); })
|
||||
.fail(function(xhr) { alert(xhr.responseJSON ? xhr.responseJSON.error : 'Fehler'); });
|
||||
});
|
||||
|
||||
})(jQuery);
|
||||
|
||||
2
assets/js/main.min.js
vendored
2
assets/js/main.min.js
vendored
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user