This commit is contained in:
rgrgogu
2026-05-07 12:36:39 +08:00
parent 21ba6122f7
commit 09660d7503
2 changed files with 107 additions and 3 deletions
+102
View File
@@ -129,6 +129,66 @@ exports.deactivateUser = async (req, res) => {
}
};
// ─── BULK DEACTIVATE users ────────────────────────────────────────────────────
exports.bulkDeactivateUsers = async (req, res) => {
try {
const { ids } = req.body; // expects { ids: [1, 2, 3] }
if (!Array.isArray(ids) || ids.length === 0)
return R.error(res, 'No user IDs provided.', 400);
if (ids.includes(req.user.user_id))
return R.error(res, 'You cannot deactivate your own account.', 400);
const users = await mdl_Users.findAll({
where: { user_id: ids },
});
if (!users.length)
return R.error(res, 'No users found.', 404);
// Filter only currently active users — skip already-deactivated ones
const activeUsers = users.filter((u) => u.is_active && !u.deletedAt);
if (!activeUsers.length)
return R.error(res, 'All selected users are already deactivated.', 400);
const activeIds = activeUsers.map((u) => u.user_id);
// Soft-delete + mark who deactivated them
await mdl_Users.update(
{ is_active: false, deletedBy: req.user.user_id },
{ where: { user_id: activeIds } }
);
// Sequelize paranoid bulk destroy (sets deletedAt)
await mdl_Users.destroy({
where: { user_id: activeIds },
});
// Force logout all their active sessions
await mdl_UserSessions.update(
{
is_active: false,
logout_info: {
date: new Date().toISOString(),
forced_by: req.user.user_id,
},
},
{ where: { user_id: activeIds } }
);
return R.success(res, `${activeIds.length} user(s) deactivated successfully.`, {
deactivated_ids: activeIds,
// Inform caller if some were already inactive and got skipped
skipped_ids: ids.filter((id) => !activeIds.includes(id)),
});
} catch (err) {
console.error("[ADMIN][BULK DEACTIVATE USERS]", err);
return R.error(res, 'Could not deactivate users.', 500);
}
};
// ─── RESTORE user ──────────────────────────────────────────────────────────────
exports.restoreUser = async (req, res) => {
try {
@@ -143,6 +203,7 @@ exports.restoreUser = async (req, res) => {
await user.update({
is_active: true,
updatedBy: req.user.user_id, // ← who restored
deletedBy: null
});
return R.success(res, 'User restored successfully.');
@@ -152,6 +213,47 @@ exports.restoreUser = async (req, res) => {
}
};
// ─── BULK RESTORE users ────────────────────────────────────────────────────────
exports.bulkRestoreUsers = async (req, res) => {
try {
const { ids } = req.body;
if (!Array.isArray(ids) || ids.length === 0)
return R.error(res, 'No user IDs provided.', 400);
const users = await mdl_Users.findAll({
where: { user_id: ids },
paranoid: false,
});
if (!users.length)
return R.error(res, 'No users found.', 404);
// Filter only soft-deleted users — skip already-active ones
const deletedUsers = users.filter((u) => u.deletedAt);
if (!deletedUsers.length)
return R.error(res, 'All selected users are already active.', 400);
const deletedIds = deletedUsers.map((u) => u.user_id);
await mdl_Users.restore({ where: { user_id: deletedIds } });
await mdl_Users.update(
{ is_active: true, updatedBy: req.user.user_id, deletedBy: null },
{ where: { user_id: deletedIds }, paranoid: false }
);
return R.success(res, `${deletedIds.length} user(s) restored successfully.`, {
restored_ids: deletedIds,
skipped_ids: ids.filter((id) => !deletedIds.includes(id)),
});
} catch (err) {
console.error("[ADMIN][BULK RESTORE USERS]", err);
return R.error(res, 'Could not restore users.', 500);
}
};
exports.getGroups = async (req, res) => {
try {
const result = await paginate(mdl_UserGroups, req, {
+2
View File
@@ -36,6 +36,8 @@ const { adminLimiter, sensitiveOpsLimiter } = require('../../middleware/rateLimi
router.use(authenticate, requireAdmin(), adminLimiter);
// ── User management ────────────────────────────────────────────────────────────
router.post('/users/bulk/restore', sensitiveOpsLimiter, usersCtrl.bulkRestoreUsers); // ← before /:id
router.delete('/users/bulk', sensitiveOpsLimiter, usersCtrl.bulkDeactivateUsers); // ← before /:id
router.get('/users/field-values', usersCtrl.getUserFieldValues); // ← before /:id
router.get('/users/archived', usersCtrl.getArchivedUsers); // ← before /:id
router.get('/users', usersCtrl.getUsers);