diff --git a/public/index.html b/public/index.html index 9098046..85d31f8 100644 --- a/public/index.html +++ b/public/index.html @@ -302,6 +302,7 @@ @@ -374,6 +375,29 @@
+ + +
+
+

修改登录密码

+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+
+
diff --git a/public/js/app.js b/public/js/app.js index 253ceb0..b8d7fc3 100644 --- a/public/js/app.js +++ b/public/js/app.js @@ -70,9 +70,14 @@ detailMemTotal: document.getElementById('detailMemTotal'), detailUptime: document.getElementById('detailUptime'), detailContainer: document.getElementById('detailContainer'), - sourceFilter: document.getElementById('sourceFilter'), pageSizeSelect: document.getElementById('pageSizeSelect'), - paginationControls: document.getElementById('paginationControls') + paginationControls: document.getElementById('paginationControls'), + // Auth security + oldPasswordInput: document.getElementById('oldPassword'), + newPasswordInput: document.getElementById('newPassword'), + confirmNewPasswordInput: document.getElementById('confirmNewPassword'), + btnChangePassword: document.getElementById('btnChangePassword'), + changePasswordMessage: document.getElementById('changePasswordMessage') }; // ---- State ---- @@ -124,6 +129,11 @@ // Site settings dom.btnSaveSiteSettings.addEventListener('click', saveSiteSettings); + + // Auth password change + if (dom.btnChangePassword) { + dom.btnChangePassword.addEventListener('click', saveChangePassword); + } // Keyboard shortcut document.addEventListener('keydown', (e) => { @@ -721,6 +731,12 @@ dom.settingsModal.classList.remove('active'); hideMessage(); hideSiteMessage(); + hideChangePasswordMessage(); + + // Reset password fields + if (dom.oldPasswordInput) dom.oldPasswordInput.value = ''; + if (dom.newPasswordInput) dom.newPasswordInput.value = ''; + if (dom.confirmNewPasswordInput) dom.confirmNewPasswordInput.value = ''; } // ---- Tab Switching ---- @@ -849,6 +865,76 @@ dom.siteSettingsMessage.className = 'form-message'; } + async function saveChangePassword() { + if (!user) { + showChangePasswordMessage('请先登录后操作', 'error'); + openLoginModal(); + return; + } + + const oldPassword = dom.oldPasswordInput.value; + const newPassword = dom.newPasswordInput.value; + const confirmNewPassword = dom.confirmNewPasswordInput.value; + + if (!oldPassword || !newPassword || !confirmNewPassword) { + showChangePasswordMessage('请填写所有密码字段', 'error'); + return; + } + + if (newPassword !== confirmNewPassword) { + showChangePasswordMessage('两次输入的新密码不一致', 'error'); + return; + } + + if (newPassword.length < 6) { + showChangePasswordMessage('新密码长度至少为 6 位', 'error'); + return; + } + + dom.btnChangePassword.disabled = true; + dom.btnChangePassword.textContent = '提交中...'; + + try { + const response = await fetch('/api/auth/change-password', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + oldPassword, + newPassword + }) + }); + + const data = await response.json(); + + if (response.ok) { + showChangePasswordMessage('密码修改成功', 'success'); + dom.oldPasswordInput.value = ''; + dom.newPasswordInput.value = ''; + dom.confirmNewPasswordInput.value = ''; + } else { + showChangePasswordMessage(data.error || '修改失败', 'error'); + if (response.status === 401 && data.error === 'Auth required') openLoginModal(); + } + } catch (err) { + showChangePasswordMessage(`请求失败: ${err.message}`, 'error'); + } finally { + dom.btnChangePassword.disabled = false; + dom.btnChangePassword.textContent = '提交修改'; + } + } + + function showChangePasswordMessage(text, type) { + dom.changePasswordMessage.textContent = text; + dom.changePasswordMessage.className = `form-message ${type}`; + setTimeout(hideChangePasswordMessage, 5000); + } + + function hideChangePasswordMessage() { + dom.changePasswordMessage.className = 'form-message'; + } + function updateSourceFilterOptions(sources) { if (!dom.sourceFilter) return; const current = dom.sourceFilter.value; diff --git a/server/index.js b/server/index.js index de47ff6..6adf177 100644 --- a/server/index.js +++ b/server/index.js @@ -80,6 +80,34 @@ app.post('/api/auth/logout', (req, res) => { res.json({ success: true }); }); +app.post('/api/auth/change-password', requireAuth, async (req, res) => { + const { oldPassword, newPassword } = req.body; + if (!oldPassword || !newPassword) { + return res.status(400).json({ error: '需要输入旧密码和新密码' }); + } + + try { + const [rows] = await db.query('SELECT * FROM users WHERE id = ?', [req.user.id]); + if (rows.length === 0) return res.status(404).json({ error: '用户不存在' }); + + const user = rows[0]; + const oldHash = crypto.pbkdf2Sync(oldPassword, user.salt, 1000, 64, 'sha512').toString('hex'); + + if (oldHash !== user.password) { + return res.status(401).json({ error: '旧密码输入错误' }); + } + + const newSalt = crypto.randomBytes(16).toString('hex'); + const newHash = crypto.pbkdf2Sync(newPassword, newSalt, 1000, 64, 'sha512').toString('hex'); + + await db.query('UPDATE users SET password = ?, salt = ? WHERE id = ?', [newHash, newSalt, user.id]); + res.json({ success: true, message: '密码修改成功' }); + } catch (err) { + console.error('Password update error:', err); + res.status(500).json({ error: '服务器错误,修改失败' }); + } +}); + app.get('/api/auth/status', (req, res) => { const sessionId = getCookie(req, 'session_id'); if (sessionId && sessions.has(sessionId)) {