feat: edit user groups + email via delete+recreate
This commit is contained in:
parent
359a0dccb6
commit
6cf084b931
3 changed files with 95 additions and 3 deletions
|
|
@ -1,5 +1,11 @@
|
|||
# Changelog
|
||||
|
||||
## 0.1.0.0043 — 2026-07-11
|
||||
|
||||
### Added
|
||||
- Edit user button in Access tab — admin can change email and groups (delete + recreate approach)
|
||||
- Edit user modal with email, groups fields, and new password display
|
||||
|
||||
## 0.1.0.0039 — 2026-07-11
|
||||
|
||||
### Changed
|
||||
|
|
|
|||
2
VERSION
2
VERSION
|
|
@ -1 +1 @@
|
|||
0.1.0.0039
|
||||
0.1.0.0043
|
||||
|
|
|
|||
90
main.go
90
main.go
|
|
@ -1316,6 +1316,8 @@ const adminHTML = `<!DOCTYPE html>
|
|||
.btn { display: inline-flex; align-items: center; gap: 0.35rem; padding: 0.45rem 0.9rem; border-radius: 6px; font-size: 0.85rem; font-weight: 500; cursor: pointer; border: none; text-decoration: none; transition: all .12s; }
|
||||
.btn-primary { background: #1a1a2e; color: #fff; }
|
||||
.btn-primary:hover { background: #2d3748; }
|
||||
.btn-edit { background: #fff; color: #3182ce; border: 1px solid #bee3f8; }
|
||||
.btn-edit:hover { background: #ebf8ff; }
|
||||
.btn-danger { background: #fff; color: #e53e3e; border: 1px solid #fed7d7; }
|
||||
.btn-danger:hover { background: #fff5f5; }
|
||||
.btn-ghost { background: transparent; color: #718096; border: 1px solid #e2e8f0; }
|
||||
|
|
@ -1459,6 +1461,30 @@ const adminHTML = `<!DOCTYPE html>
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Edit User Modal -->
|
||||
<div id="edit-user-modal" style="display:none;position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.5);z-index:1000;">
|
||||
<div style="background:#fff;border-radius:12px;padding:2rem;width:500px;max-width:90%;margin:5vh auto;">
|
||||
<h3 style="margin-bottom:1.5rem;">Edit User</h3>
|
||||
<p id="edit-username-display" style="font-weight:600;margin-bottom:1rem;"></p>
|
||||
<form id="edit-user-form" onsubmit="return saveEditUser(event)">
|
||||
<input type="hidden" name="edit_username" id="edit-username">
|
||||
<div class="field"><label>Email</label><input type="email" name="edit_email" id="edit-email" required></div>
|
||||
<div class="field"><label>Groups</label>
|
||||
<div class="checkbox-group">
|
||||
<label style="font-size:0.9rem;display:flex;align-items:center;gap:0.3rem;"><input type="checkbox" name="edit_groups" value="users" checked> User (access to all apps)</label>
|
||||
<label style="font-size:0.9rem;display:flex;align-items:center;gap:0.3rem;"><input type="checkbox" name="edit_groups" value="admins"> Admin (access to config panel)</label>
|
||||
</div>
|
||||
</div>
|
||||
<p style="color:#718096;font-size:0.82rem;margin:0.5rem 0;">The user will receive a new generated password. Share it with them.</p>
|
||||
<div style="display:flex;gap:0.75rem;margin-top:1.5rem;">
|
||||
<button type="submit" class="btn btn-primary">Save</button>
|
||||
<button type="button" class="btn btn-ghost" onclick="closeEditUserModal()">Cancel</button>
|
||||
</div>
|
||||
</form>
|
||||
<div id="edit-result" style="display:none;margin-top:1rem;"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Security -->
|
||||
<div id="page-security" class="page hidden">
|
||||
<div class="page-header"><h2>Security</h2><p>Authentication policies, tokens, and session configuration.</p></div>
|
||||
|
|
@ -1527,8 +1553,9 @@ const adminHTML = `<!DOCTYPE html>
|
|||
tbody.innerHTML = users.map(u => {
|
||||
const groups = (u.groups||[]).map(g => '<span class="badge">' + esc(g) + '</span>').join(' ');
|
||||
const status = u.disabled ? '<span style="color:#e53e3e;font-weight:500;">Disabled</span>' : '<span style="color:#38a169;font-weight:500;">Active</span>';
|
||||
const del = u.username === 'master' ? '<button class="btn btn-danger btn-sm" disabled title="Cannot delete master">Delete</button>' : '<button class="btn btn-danger btn-sm" onclick="deleteUser(\'' + u.username + '\')">Delete</button>';
|
||||
return '<tr><td style="padding:12px 16px;border-top:1px solid #edf2f7;"><strong>' + esc(u.username) + '</strong></td><td style="padding:12px 16px;border-top:1px solid #edf2f7;">' + esc(u.display_name||'') + '</td><td style="padding:12px 16px;border-top:1px solid #edf2f7;">' + esc(u.email||'') + '</td><td style="padding:12px 16px;border-top:1px solid #edf2f7;">' + groups + '</td><td style="padding:12px 16px;border-top:1px solid #edf2f7;">' + status + '</td><td style="padding:12px 16px;border-top:1px solid #edf2f7;">' + del + '</td></tr>';
|
||||
const editBtn = '<button class="btn btn-edit btn-sm" onclick="editUser(\'' + u.username + '\',\'' + esc(u.email||'') + '\',\'' + (u.groups||[]).join(',') + '\')">Edit</button>';
|
||||
const delBtn = u.username === 'master' ? '<button class="btn btn-danger btn-sm" disabled title="Cannot delete master">Delete</button>' : '<button class="btn btn-danger btn-sm" onclick="deleteUser(\'' + u.username + '\')">Delete</button>';
|
||||
return '<tr><td style="padding:12px 16px;border-top:1px solid #edf2f7;"><strong>' + esc(u.username) + '</strong></td><td style="padding:12px 16px;border-top:1px solid #edf2f7;">' + esc(u.display_name||'') + '</td><td style="padding:12px 16px;border-top:1px solid #edf2f7;">' + esc(u.email||'') + '</td><td style="padding:12px 16px;border-top:1px solid #edf2f7;">' + groups + '</td><td style="padding:12px 16px;border-top:1px solid #edf2f7;">' + status + '</td><td style="padding:12px 16px;border-top:1px solid #edf2f7;">' + editBtn + ' ' + delBtn + '</td></tr>';
|
||||
}).join('');
|
||||
}
|
||||
|
||||
|
|
@ -1560,6 +1587,65 @@ const adminHTML = `<!DOCTYPE html>
|
|||
else alert('Failed to delete user');
|
||||
}
|
||||
|
||||
function editUser(username, email, groups) {
|
||||
document.getElementById('edit-username').value = username;
|
||||
document.getElementById('edit-username-display').textContent = 'Editing: ' + username;
|
||||
document.getElementById('edit-email').value = email;
|
||||
const groupList = groups.split(',');
|
||||
document.querySelectorAll('#edit-user-form input[name="edit_groups"]').forEach(cb => {
|
||||
cb.checked = groupList.includes(cb.value);
|
||||
});
|
||||
document.getElementById('edit-result').style.display = 'none';
|
||||
document.getElementById('edit-user-modal').style.display = 'block';
|
||||
}
|
||||
|
||||
function closeEditUserModal() {
|
||||
document.getElementById('edit-user-modal').style.display = 'none';
|
||||
}
|
||||
|
||||
async function saveEditUser(event) {
|
||||
event.preventDefault();
|
||||
const username = document.getElementById('edit-username').value;
|
||||
const email = document.getElementById('edit-email').value;
|
||||
const groups = [];
|
||||
document.querySelectorAll('#edit-user-form input[name="edit_groups"]:checked').forEach(cb => {
|
||||
groups.push(cb.value);
|
||||
});
|
||||
|
||||
// 1. Delete user
|
||||
const delResp = await fetch('/api/users/' + username, { method: 'DELETE' });
|
||||
if (!delResp.ok) { alert('Failed to delete user for re-creation'); return; }
|
||||
|
||||
// 2. Recreate with new data
|
||||
const body = JSON.stringify({
|
||||
users: [{
|
||||
username: username,
|
||||
display_name: username,
|
||||
email: email,
|
||||
groups: groups
|
||||
}]
|
||||
});
|
||||
const createResp = await fetch('/api/users/bulk', {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: body
|
||||
});
|
||||
|
||||
const result = await createResp.json();
|
||||
const resultDiv = document.getElementById('edit-result');
|
||||
resultDiv.style.display = 'block';
|
||||
|
||||
if (createResp.ok && result.users && result.users[0]) {
|
||||
const pwd = result.users[0].placeholder_password || '(unchanged)';
|
||||
resultDiv.innerHTML = '<div style="padding:0.75rem 1rem;background:#f0fff4;border:1px solid #c6f6d5;border-radius:8px;color:#276749;font-size:0.88rem;">✅ User updated.<br>New password: <code style="background:#edf2f7;padding:0.15rem 0.4rem;border-radius:4px;font-size:0.82rem;">' + pwd + '</code><br>Share this with the user.</div>';
|
||||
closeEditUserModal();
|
||||
loadUsers();
|
||||
} else {
|
||||
resultDiv.innerHTML = '<div style="padding:0.75rem 1rem;background:#fff5f5;border:1px solid #fed7d7;border-radius:8px;color:#c53030;font-size:0.88rem;">❌ Failed to recreate user: ' + JSON.stringify(result) + '</div>';
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function showCreateModal() { document.getElementById('createModal').style.display = 'block'; }
|
||||
function closeCreateModal() { document.getElementById('createModal').style.display = 'none'; }
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue