Commit aba86181 by tdgiang

Fix critical/important findings from final whole-branch review

parent 5342bbfe
......@@ -206,7 +206,7 @@ exports.listTransactions = function (req, res) {
if (!isAdmin) {
filter.createdByUsername = req.session.username;
} else if (req.query.staff) {
filter.createdByUsername = req.query.staff;
filter.createdByUsername = String(req.query.staff);
}
function render(staffOptions) {
......
......@@ -15,21 +15,26 @@ module.exports = function run(config, callback) {
console.error("adminBootstrap: no admin exists and ADMIN_USER/ADMIN_PASSWORD not set - cannot seed");
return callback(null);
}
AdminUser.create(
{
username: String(config.admin.user).trim().toLowerCase(),
passwordHash: passwordHash.hash(config.admin.password),
role: "admin",
active: true,
},
function (createErr, admin) {
if (createErr) {
return callback(createErr);
}
console.log("adminBootstrap: seeded first admin account:", admin.username);
return backfillTransactions(admin.username, callback);
passwordHash.hash(config.admin.password, function (hashErr, hashed) {
if (hashErr) {
return callback(hashErr);
}
);
AdminUser.create(
{
username: String(config.admin.user).trim().toLowerCase(),
passwordHash: hashed,
role: "admin",
active: true,
},
function (createErr, admin) {
if (createErr) {
return callback(createErr);
}
console.log("adminBootstrap: seeded first admin account:", admin.username);
return backfillTransactions(admin.username, callback);
}
);
});
});
};
......
"use strict";
var bcrypt = require("bcryptjs");
function hash(plain) {
return bcrypt.hashSync(plain, 10);
function hash(plain, callback) {
bcrypt.hash(plain, 10, callback);
}
function compare(plain, hashed) {
return bcrypt.compareSync(plain, hashed);
function compare(plain, hashed, callback) {
bcrypt.compare(plain, hashed, callback);
}
module.exports = {
......
"use strict";
var AdminUser = require("../models/AdminUser");
module.exports = function requireLogin(req, res, next) {
if (req.session && req.session.userId) {
return next();
}
if (req.is("json")) {
return res.status(401).json({ code: "99", data: "LOGIN_REQUIRED" });
if (!req.session || !req.session.userId) {
if (req.is("json")) {
return res.status(401).json({ code: "99", data: "LOGIN_REQUIRED" });
}
return res.redirect("/admin/login");
}
return res.redirect("/admin/login");
AdminUser.findById(req.session.userId, function (err, user) {
if (err) {
console.error("requireLogin: DB error:", err.message);
if (req.is("json")) {
return res.status(503).json({ code: "99", data: "SERVICE_UNAVAILABLE" });
}
return res.status(503).send("Hệ thống đang bận, vui lòng thử lại sau.");
}
if (!user || !user.active) {
return req.session.destroy(function () {
if (req.is("json")) {
return res.status(401).json({ code: "99", data: "LOGIN_REQUIRED" });
}
return res.redirect("/admin/login");
});
}
// Keep the session's cached role/username in sync with the DB on every request,
// in case an admin changed either after this session was created.
req.session.role = user.role;
req.session.username = user.username;
return next();
});
};
......@@ -100,6 +100,9 @@
{% if error == "MISSING_FIELDS" %}<div class="alert alert-error">Vui lòng nhập đủ thông tin.</div>{% endif %}
{% if error == "DUPLICATE_USERNAME" %}<div class="alert alert-error">Tên đăng nhập đã tồn tại.</div>{% endif %}
{% if error == "DB_ERROR" %}<div class="alert alert-error">Lỗi cơ sở dữ liệu, vui lòng thử lại.</div>{% endif %}
{% if error == "PASSWORD_TOO_SHORT" %}<div class="alert alert-error">Mật khẩu phải có ít nhất 8 ký tự.</div>{% endif %}
{% if error == "CANNOT_LOCK_SELF" %}<div class="alert alert-error">Bạn không thể khoá tài khoản của chính mình.</div>{% endif %}
{% if error == "LAST_ADMIN" %}<div class="alert alert-error">Không thể khoá — đây là tài khoản admin đang hoạt động cuối cùng.</div>{% endif %}
<div class="card">
<h2>Tạo tài khoản nhân viên mới</h2>
......@@ -111,7 +114,7 @@
</div>
<div class="form-field">
<label for="password">Mật khẩu</label>
<input type="password" id="password" name="password" required>
<input type="password" id="password" name="password" required minlength="8">
</div>
<div class="form-field" style="max-width:140px;">
<label for="role">Vai trò</label>
......@@ -144,11 +147,15 @@
<td><span class="pill pill-{% if u.active %}active{% else %}locked{% endif %}">{% if u.active %}Hoạt động{% else %}Đã khoá{% endif %}</span></td>
<td>
<div class="row-actions">
{% if u.username == currentUsername %}
<span style="font-size:12.5px;color:var(--color-text-muted);">Tài khoản của bạn</span>
{% else %}
<form method="POST" action="/admin/accounts/{{u._id}}/toggle">
<button type="submit" class="secondary">{% if u.active %}Khoá{% else %}Mở khoá{% endif %}</button>
</form>
{% endif %}
<form method="POST" action="/admin/accounts/{{u._id}}/reset-password">
<input type="password" name="newPassword" placeholder="Mật khẩu mới" required>
<input type="password" name="newPassword" placeholder="Mật khẩu mới" required minlength="8">
<button type="submit" class="secondary">Đặt lại</button>
</form>
</div>
......
......@@ -71,9 +71,9 @@
</tbody>
</table>
<p class="pager">
{% if hasPrev %}<a href="/admin/transactions?page={{prevPage}}">&laquo; Trang trước</a>{% endif %}
{% if hasPrev %}<a href="/admin/transactions?page={{prevPage}}{% if selectedStaff %}&staff={{selectedStaff}}{% endif %}">&laquo; Trang trước</a>{% endif %}
Trang {{page}}
{% if hasNext %}<a href="/admin/transactions?page={{nextPage}}">Trang sau &raquo;</a>{% endif %}
{% if hasNext %}<a href="/admin/transactions?page={{nextPage}}{% if selectedStaff %}&staff={{selectedStaff}}{% endif %}">Trang sau &raquo;</a>{% endif %}
</p>
</body>
</html>
......@@ -100,6 +100,7 @@ module.exports = function() {
cookie: {
secure: cookieSecure,
httpOnly: true,
sameSite: 'lax',
maxAge: config.session_max_age
},
// store: new mongoStore({
......
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