feat: Login-as-User (Impersonate) + Default-Path pro User
Admin:
- POST /api/admin/users/{userId}/impersonate – erstellt Token für Zieluser
- Login-as-Button (fa-sign-in) in User-Liste (nicht für sich selbst)
- confirm-Dialog, danach location.reload() mit neuem User-Kontext
- Default-Path Feld + Browse-Button im Admin-Formular
Default-Path:
- Neues Feld default_path in users-Tabelle (TryAddColumn Migration)
- UserRecord.DefaultPath Property
- In allen Endpoints: Admin CRUD, Auth /me, Profile Get/Update
- Profil-Seite: Default-Path Feld + Browse-Button
- Session-Seite: Workspace-Browse nutzt defaultPath als Fallback
This commit is contained in:
1 parent
df68843e82
commit
d1355dcb0d
10 files changed
+144
-17
No files matched your search
@@ -18,6 +18,8 @@ registerPage('admin', {
|
||||
var displayNameInput = document.getElementById('adminDisplayName');
|
||||
var passwordInput = document.getElementById('adminPassword');
|
||||
var passwordLabel = document.getElementById('adminPasswordLabel');
|
||||
var defaultPathInput = document.getElementById('adminDefaultPath');
|
||||
var defaultPathBrowse = document.getElementById('adminDefaultPathBrowse');
|
||||
var isAdminCheckbox = document.getElementById('adminIsAdmin');
|
||||
var saveBtn = document.getElementById('adminFormSaveBtn');
|
||||
var formMessage = document.getElementById('adminFormMessage');
|
||||
@@ -59,7 +61,8 @@ registerPage('admin', {
|
||||
metaSpan.className = 'admin-user-meta';
|
||||
var metaParts = ['@' + user.username];
|
||||
if (user.isAdmin) metaParts.push('Administrator');
|
||||
metaSpan.textContent = metaParts.join(' ▪ ');
|
||||
if (user.defaultPath) metaParts.push(user.defaultPath);
|
||||
metaSpan.textContent = metaParts.join(' · ');
|
||||
|
||||
info.appendChild(nameSpan);
|
||||
info.appendChild(metaSpan);
|
||||
@@ -76,8 +79,17 @@ registerPage('admin', {
|
||||
});
|
||||
actions.appendChild(editBtn);
|
||||
|
||||
// Sich selbst kann man nicht löschen
|
||||
// Login as User (nicht für sich selbst)
|
||||
if (user.userId !== currentUser.userId) {
|
||||
var loginAsBtn = document.createElement('button');
|
||||
loginAsBtn.className = 'icon-btn';
|
||||
loginAsBtn.title = 'Als ' + (user.displayName || user.username) + ' anmelden';
|
||||
loginAsBtn.innerHTML = '<i class="fa fa-sign-in"></i>';
|
||||
loginAsBtn.addEventListener('click', function() {
|
||||
impersonateUser(user);
|
||||
});
|
||||
actions.appendChild(loginAsBtn);
|
||||
|
||||
var deleteBtn = document.createElement('button');
|
||||
deleteBtn.className = 'icon-btn';
|
||||
deleteBtn.title = 'Löschen';
|
||||
@@ -113,6 +125,7 @@ registerPage('admin', {
|
||||
passwordLabel.textContent = 'Neues Passwort (leer = unverändert)';
|
||||
passwordInput.placeholder = 'Neues Passwort (optional)';
|
||||
passwordInput.required = false;
|
||||
defaultPathInput.value = user.defaultPath || '';
|
||||
isAdminCheckbox.checked = user.isAdmin;
|
||||
saveBtn.textContent = 'Speichern';
|
||||
formMessage.hidden = true;
|
||||
@@ -133,8 +146,34 @@ registerPage('admin', {
|
||||
}
|
||||
}
|
||||
|
||||
async function impersonateUser(user) {
|
||||
if (!confirm('Als "' + (user.displayName || user.username) + '" anmelden?\n\nDu wirst abgemeldet und als dieser Benutzer eingeloggt.')) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await apiAdminImpersonate(user.userId);
|
||||
// Seite neu laden, damit der neue User-Kontext geladen wird
|
||||
location.reload();
|
||||
} catch (err) {
|
||||
showPageMessage(err.message, true);
|
||||
}
|
||||
}
|
||||
|
||||
// ── Event-Listener ──
|
||||
|
||||
// Default Path Browse
|
||||
if (defaultPathBrowse) {
|
||||
defaultPathBrowse.addEventListener('click', function() {
|
||||
openFolderBrowser({
|
||||
startPath: defaultPathInput.value.trim() || '/',
|
||||
onSelect: function(path) {
|
||||
defaultPathInput.value = path;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Neuer Benutzer
|
||||
newUserBtn.addEventListener('click', function() {
|
||||
editingUserId = null;
|
||||
@@ -146,6 +185,7 @@ registerPage('admin', {
|
||||
passwordLabel.textContent = 'Passwort';
|
||||
passwordInput.placeholder = 'Passwort';
|
||||
passwordInput.required = true;
|
||||
defaultPathInput.value = '';
|
||||
isAdminCheckbox.checked = false;
|
||||
saveBtn.textContent = 'Erstellen';
|
||||
formMessage.hidden = true;
|
||||
@@ -164,6 +204,7 @@ registerPage('admin', {
|
||||
var data = {
|
||||
username: usernameInput.value.trim(),
|
||||
displayName: displayNameInput.value.trim(),
|
||||
defaultPath: defaultPathInput.value.trim() || null,
|
||||
isAdmin: isAdminCheckbox.checked
|
||||
};
|
||||
await apiAdminUpdateUser(editingUserId, data);
|
||||
@@ -189,6 +230,7 @@ registerPage('admin', {
|
||||
username: username,
|
||||
password: password,
|
||||
displayName: displayNameInput.value.trim(),
|
||||
defaultPath: defaultPathInput.value.trim() || null,
|
||||
isAdmin: isAdminCheckbox.checked
|
||||
});
|
||||
|
||||
|
||||
Reference in new issue
Block a user