Files
MicroNAS/static/js/users.js
T
marcandClaude Sonnet 4.6 74737568b0 Fix broken action buttons and add delete confirmation modal
- shares.js, users.js: JSON.stringify() inside onclick="..." attributes
  produced unescaped " chars that broke HTML attribute parsing — all
  dynamic onclick JSON now passed through esc() so the browser decodes
  " back to " before executing the handler
- Replace browser confirm() dialogs with a styled Bootstrap modal
  (#deleteModal) for share and user deletion — reusable across all tabs
- users.js: rename shadow variable 'confirm' → 'pwConfirm' to avoid
  shadowing the now-removed confirm() call
- main.js: auto-refresh now reloads shares and users every 30 s,
  not just the dashboard

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 22:33:37 +02:00

94 lines
3.9 KiB
JavaScript

async function loadUsers() {
const res = await apiFetch('/api/users');
if (!res) return;
const users = await res.json();
const tb = document.getElementById('usersTableBody');
if (!users.length) {
tb.innerHTML = `<tr><td colspan="4" style="color:var(--muted)">No users found.</td></tr>`;
return;
}
tb.innerHTML = users.map(u => {
// esc() HTML-encodes JSON so " inside onclick="..." doesn't break the attribute
const nameJson = esc(JSON.stringify(u.name));
return `<tr>
<td>${esc(u.name)}</td>
<td style="color:var(--muted)">${u.uid}</td>
<td>
<span class="badge rounded-pill"
style="${u.smb ? 'background:#1a3a1f; color:var(--green)' : 'background:var(--surf2); color:var(--muted)'}">
${u.smb ? 'Active' : 'No'}
</span>
</td>
<td>
<button class="btn btn-sm me-1" style="border:1px solid var(--border); color:var(--muted)"
onclick="openPwModal(${nameJson})">Password</button>
<button class="btn btn-sm btn-danger"
onclick="confirmDeleteUser(${nameJson})">Delete</button>
</td>
</tr>`;
}).join('');
}
async function createUser() {
const name = document.getElementById('newUsername').value.trim();
const password = document.getElementById('newPassword').value;
if (!name || !password) return showToast('Username and password are required.', 'danger');
const res = await apiFetch('/api/users', {
method: 'POST', headers: {'Content-Type': 'application/json'},
body: JSON.stringify({name, password}),
});
if (!res) return;
const data = await res.json();
if (!res.ok) return showToast('Error: ' + (data.error || res.status), 'danger');
showToast(`User "${name}" created.`);
document.getElementById('newUsername').value = '';
document.getElementById('newPassword').value = '';
loadUsers();
}
function confirmDeleteUser(name) {
document.getElementById('deleteModalMsg').textContent =
`Are you sure you want to delete user "${name}"? This removes both the system account and SMB access.`;
const modal = new bootstrap.Modal(document.getElementById('deleteModal'));
const btn = document.getElementById('deleteModalConfirm');
const handler = async () => {
btn.removeEventListener('click', handler);
modal.hide();
const res = await apiFetch(`/api/users/${encodeURIComponent(name)}`, { method: 'DELETE' });
if (!res) return;
const data = await res.json();
if (!res.ok) return showToast('Error: ' + (data.error || res.status), 'danger');
showToast(`User "${name}" deleted.`);
loadUsers();
};
btn.addEventListener('click', handler);
modal.show();
}
function openPwModal(name) {
document.getElementById('pwUsername').value = name;
document.getElementById('pwNew').value = '';
document.getElementById('pwConfirm').value = '';
new bootstrap.Modal(document.getElementById('pwModal')).show();
}
async function submitPassword() {
const name = document.getElementById('pwUsername').value;
const pw = document.getElementById('pwNew').value;
const pwConfirm = document.getElementById('pwConfirm').value;
if (pw.length < 8) return showToast('Password must be at least 8 characters.', 'danger');
if (pw !== pwConfirm) return showToast('Passwords do not match.', 'danger');
const res = await apiFetch(`/api/users/${encodeURIComponent(name)}/password`, {
method: 'POST', headers: {'Content-Type': 'application/json'},
body: JSON.stringify({password: pw}),
});
if (!res) return;
const data = await res.json();
bootstrap.Modal.getInstance(document.getElementById('pwModal')).hide();
if (!res.ok) return showToast('Error: ' + (data.error || res.status), 'danger');
showToast(`Password updated for "${name}".`);
}