Commit 162461d0 by tdgiang

Add self-service change-password for logged-in accounts

parent 41ae2a28
...@@ -122,3 +122,33 @@ exports.resetAccountPassword = function (req, res) { ...@@ -122,3 +122,33 @@ exports.resetAccountPassword = function (req, res) {
}); });
}); });
}; };
exports.changePasswordForm = function (req, res) {
res.render("admin/change-password", { error: null, success: false });
};
exports.changePassword = function (req, res) {
var currentPassword = String(req.body.currentPassword || "");
var newPassword = String(req.body.newPassword || "");
AdminUser.findById(req.session.userId, function (err, user) {
if (err) {
console.error("changePassword: DB error:", err.message);
return res.status(503).render("admin/change-password", { error: "Hệ thống đang bận, vui lòng thử lại sau", success: false });
}
if (!user || !passwordHash.compare(currentPassword, user.passwordHash)) {
return res.render("admin/change-password", { error: "Mật khẩu hiện tại không đúng", success: false });
}
if (!newPassword) {
return res.render("admin/change-password", { error: "Vui lòng nhập mật khẩu mới", success: false });
}
user.passwordHash = passwordHash.hash(newPassword);
user.save(function (saveErr) {
if (saveErr) {
console.error("changePassword: DB error saving:", saveErr.message);
return res.status(500).render("admin/change-password", { error: "Lỗi cơ sở dữ liệu", success: false });
}
return res.render("admin/change-password", { error: null, success: true });
});
});
};
...@@ -18,6 +18,10 @@ module.exports = function (app) { ...@@ -18,6 +18,10 @@ module.exports = function (app) {
app.route("/admin/accounts/:id/toggle").post(requireLogin, requireAdmin, adminAuth.toggleAccount); app.route("/admin/accounts/:id/toggle").post(requireLogin, requireAdmin, adminAuth.toggleAccount);
app.route("/admin/accounts/:id/reset-password").post(requireLogin, requireAdmin, adminAuth.resetAccountPassword); app.route("/admin/accounts/:id/reset-password").post(requireLogin, requireAdmin, adminAuth.resetAccountPassword);
app.route("/admin/account/password")
.get(requireLogin, adminAuth.changePasswordForm)
.post(requireLogin, adminAuth.changePassword);
app.route("/admin/transactions") app.route("/admin/transactions")
.get(requireLogin, admin.listTransactions) .get(requireLogin, admin.listTransactions)
.post(requireLogin, admin.createTransaction); .post(requireLogin, admin.createTransaction);
......
<!DOCTYPE html>
<html lang="vi">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Đổi mật khẩu</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
<style>
:root {
--color-bg: #F1F5F9;
--color-surface: #FFFFFF;
--color-primary: #1E40AF;
--color-primary-hover: #1D4ED8;
--color-text: #0F172A;
--color-text-muted: #64748B;
--color-border: #E2E8F0;
--color-danger-bg: #FEF2F2;
--color-danger-border: #FECACA;
--color-success-bg: #F0FDF4;
--color-success-border: #BBF7D0;
--radius: 10px;
--shadow-card: 0 1px 2px rgba(15, 23, 42, 0.04), 0 8px 24px rgba(15, 23, 42, 0.06);
}
* { box-sizing: border-box; }
body {
margin: 0; min-height: 100vh; background: var(--color-bg);
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
color: var(--color-text); display: flex; justify-content: center; padding: 40px 20px;
}
.page { width: 100%; max-width: 420px; }
.nav { display: flex; gap: 16px; font-size: 13px; margin-bottom: 20px; color: var(--color-text-muted); }
.nav a { color: inherit; text-decoration: none; font-weight: 500; }
.nav a:hover { color: var(--color-primary); }
.card {
background: var(--color-surface); border: 1px solid var(--color-border);
border-radius: var(--radius); box-shadow: var(--shadow-card); padding: 32px;
}
h1 { font-size: 20px; font-weight: 700; letter-spacing: -0.01em; margin: 0 0 20px; }
.field { margin-bottom: 16px; }
label { display: block; font-size: 13px; font-weight: 600; margin-bottom: 6px; }
input {
width: 100%; height: 44px; padding: 0 14px; font-size: 15px; font-family: inherit;
border: 1px solid var(--color-border); border-radius: 8px; outline: none;
}
input:focus { border-color: var(--color-primary); box-shadow: 0 0 0 3px rgba(30, 64, 175, 0.12); }
button {
width: 100%; height: 46px; margin-top: 8px; background: var(--color-primary); color: #fff;
font-family: inherit; font-size: 15px; font-weight: 600; border: none; border-radius: 8px; cursor: pointer;
}
button:hover { background: var(--color-primary-hover); }
.alert { padding: 12px 14px; border-radius: 8px; font-size: 13.5px; margin-bottom: 16px; border: 1px solid; }
.alert-error { background: var(--color-danger-bg); border-color: var(--color-danger-border); color: #7F1D1D; }
.alert-success { background: var(--color-success-bg); border-color: var(--color-success-border); color: #14532D; }
</style>
</head>
<body>
<div class="page">
<div class="nav">
<a href="/admin/transactions">&laquo; Lịch sử giao dịch</a>
</div>
<div class="card">
<h1>Đổi mật khẩu</h1>
{% if success %}<div class="alert alert-success">Đã đổi mật khẩu thành công.</div>{% endif %}
{% if error %}<div class="alert alert-error">{{error}}</div>{% endif %}
<form method="POST" action="/admin/account/password" id="changePasswordForm">
<div class="field">
<label for="currentPassword">Mật khẩu hiện tại</label>
<input type="password" id="currentPassword" name="currentPassword" autocomplete="current-password" required>
</div>
<div class="field">
<label for="newPassword">Mật khẩu mới</label>
<input type="password" id="newPassword" name="newPassword" autocomplete="new-password" required>
</div>
<div class="field">
<label for="confirmPassword">Nhập lại mật khẩu mới</label>
<input type="password" id="confirmPassword" autocomplete="new-password" required>
</div>
<button type="submit">Đổi mật khẩu</button>
</form>
</div>
</div>
<script>
document.getElementById('changePasswordForm').addEventListener('submit', function (e) {
var newPassword = document.getElementById('newPassword').value;
var confirmPassword = document.getElementById('confirmPassword').value;
if (newPassword !== confirmPassword) {
e.preventDefault();
alert('Mật khẩu mới nhập lại không khớp.');
}
});
</script>
</body>
</html>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment