feat: Add password change UI to admin dashboard

NEW FEATURE: Password Change Form on Admin Dashboard

TEMPLATE CHANGES (admin.ctml):
- Added 'Account Security' section after System Status
- Password change form with:
  - Current password field
  - New password field (min 8 characters)
  - Confirm password field
  - Submit button
  - Message display area for feedback

JAVASCRIPT CHANGES (admin.js):
- changeAdminPassword(event) function
  - Validates passwords match
  - Validates minimum length (8 chars)
  - Calls /api/asteroid/user/change-password
  - Shows success/error messages
  - Clears form on success

- showPasswordMessage(message, type) helper
  - Displays success/error messages
  - Auto-hides success messages after 5 seconds

USER EXPERIENCE:
1. Admin logs in with default password (asteroid123)
2. Sees 'Account Security' section at top of dashboard
3. Fills in current password and new password
4. Clicks 'Change Password'
5. Gets immediate feedback
6. Password is changed - use new password on next login

This makes it easy for admins to change the default password
without needing REPL access or curl commands.

Ref: TODO.org Problem 4 - Security improvements
This commit is contained in:
glenneth 2025-11-03 20:18:17 +03:00
parent 86eef472a9
commit 356c6fbb49
2 changed files with 92 additions and 0 deletions

View File

@ -658,3 +658,70 @@ async function updateLiveStreamInfo() {
}
}
}
// Password change functionality
function changeAdminPassword(event) {
event.preventDefault();
const currentPassword = document.getElementById('current-password').value;
const newPassword = document.getElementById('new-password').value;
const confirmPassword = document.getElementById('confirm-password').value;
const messageEl = document.getElementById('password-message');
// Clear previous messages
messageEl.style.display = 'none';
messageEl.className = 'message';
// Validate passwords match
if (newPassword !== confirmPassword) {
showPasswordMessage('New passwords do not match', 'error');
return;
}
// Validate password length
if (newPassword.length < 8) {
showPasswordMessage('Password must be at least 8 characters', 'error');
return;
}
// Submit password change
const formData = new URLSearchParams();
formData.append('current-password', currentPassword);
formData.append('new-password', newPassword);
fetch('/api/asteroid/user/change-password', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: formData
})
.then(response => response.json())
.then(data => {
if (data.status === 'success') {
showPasswordMessage('Password changed successfully! Please use your new password on next login.', 'success');
// Clear form
document.getElementById('change-password-form').reset();
} else {
showPasswordMessage(data.message || 'Failed to change password', 'error');
}
})
.catch(error => {
console.error('Error changing password:', error);
showPasswordMessage('Error changing password. Please try again.', 'error');
});
}
function showPasswordMessage(message, type) {
const messageEl = document.getElementById('password-message');
messageEl.textContent = message;
messageEl.className = 'message ' + type;
messageEl.style.display = 'block';
// Auto-hide success messages after 5 seconds
if (type === 'success') {
setTimeout(() => {
messageEl.style.display = 'none';
}, 5000);
}
}

View File

@ -42,6 +42,31 @@
</div>
</div>
<!-- Admin Account Security -->
<div class="admin-section">
<h2>🔒 Account Security</h2>
<div class="password-change-form">
<h3>Change Your Password</h3>
<div id="password-message" class="message" style="display: none;"></div>
<form id="change-password-form" onsubmit="changeAdminPassword(event)">
<div class="form-group">
<label for="current-password">Current Password:</label>
<input type="password" id="current-password" name="current-password" required>
</div>
<div class="form-group">
<label for="new-password">New Password:</label>
<input type="password" id="new-password" name="new-password" required minlength="8">
<small>Minimum 8 characters</small>
</div>
<div class="form-group">
<label for="confirm-password">Confirm New Password:</label>
<input type="password" id="confirm-password" name="confirm-password" required>
</div>
<button type="submit" class="btn btn-primary">Change Password</button>
</form>
</div>
</div>
<!-- Music Library Management -->
<div class="admin-section">
<h2>Music Library Management</h2>