pushy

Signed-off-by: Kenneth Obsequio <k80308392@gmail.com>
This commit is contained in:
2026-06-28 11:29:07 +08:00
parent 463a8d3978
commit 89acdfc239
67 changed files with 2736 additions and 306 deletions
+286
View File
@@ -14,9 +14,11 @@ const crypto = require('crypto');
const mdl_Users = require('../../models/users/users.mdl');
const mdl_UserSessions = require('../../models/users/user_sessions.mdl');
const mdl_Achievements = require('../../models/users/achievements.mdl');
const mdl_UserBans = require('../../models/users/user_bans.mdl');
const { mdl_UserGroups, mdl_UserGroupMembers } = require('../../models/users/user_groups.mdl');
const { sendEmail } = require('../../services/email.service');
const { fmtDate } = require('../../utils/datetime.util');
const logActivity = require('../../utils/logActivity.util');
const R = require('../../utils/response.util');
const { paginate } = require('../../utils/paginate.util');
@@ -383,4 +385,288 @@ exports.getUserAchievements = async (req, res) => {
console.error('[ADMIN][GET USER ACHIEVEMENTS]', err);
return R.error(res, 'Could not retrieve achievements.', 500);
}
};
// ─── BAN USER ─────────────────────────────────────────────────────────────────
exports.banUser = async (req, res) => {
try {
const { id } = req.params;
if (Number(id) === req.user.user_id)
return R.error(res, 'You cannot ban your own account.', 400);
const user = await mdl_Users.findByPk(id);
if (!user) return R.error(res, 'User not found.', 404);
if (user.is_banned) return R.error(res, 'User is already banned.', 400);
const { reason, ban_type, expires_at } = req.body;
if (!reason?.trim()) return R.error(res, 'Ban reason is required.', 400);
if (!['temporary', 'permanent'].includes(ban_type))
return R.error(res, 'Invalid ban type. Must be "temporary" or "permanent".', 400);
if (ban_type === 'temporary' && !expires_at)
return R.error(res, 'Expiry date is required for temporary bans.', 400);
if (ban_type === 'temporary' && new Date(expires_at) <= new Date())
return R.error(res, 'Expiry date must be in the future.', 400);
const banExpiresAt = ban_type === 'temporary' ? new Date(expires_at) : null;
await sequelize.transaction(async (t) => {
await mdl_UserBans.create({
user_id: id,
banned_by: req.user.user_id,
reason: reason.trim(),
ban_type,
banned_at: new Date(),
expires_at: banExpiresAt,
}, { transaction: t });
await user.update({ is_banned: true, ban_expires_at: banExpiresAt }, { transaction: t });
await mdl_UserSessions.update(
{ is_active: false, logout_info: { date: new Date().toISOString(), forced_by: req.user.user_id } },
{ where: { user_id: id }, transaction: t }
);
});
logActivity(req.user.user_id, 'ban_user', {
entityType: 'user',
entityId: Number(id),
details: { reason: reason.trim(), ban_type, expires_at: banExpiresAt },
});
sendEmail({
to: user.email,
type: 'BANNED',
data: {
name: user.personal_info?.name?.full_name ?? 'User',
email: user.email,
date: fmtDate(new Date()),
reason: reason.trim(),
ban_type,
},
}).catch((err) => console.error('[ADMIN][BAN USER] Email failed:', err));
return R.success(res, 'User banned successfully.');
} catch (err) {
console.error('[ADMIN][BAN USER]', err);
return R.error(res, 'Could not ban user.', 500);
}
};
// ─── UNBAN USER ───────────────────────────────────────────────────────────────
exports.unbanUser = async (req, res) => {
try {
const { id } = req.params;
const user = await mdl_Users.findByPk(id);
if (!user) return R.error(res, 'User not found.', 404);
if (!user.is_banned) return R.error(res, 'User is not currently banned.', 400);
const { lift_reason } = req.body;
const activeBan = await mdl_UserBans.findOne({
where: { user_id: id, is_lifted: false },
order: [['banned_at', 'DESC']],
});
await sequelize.transaction(async (t) => {
if (activeBan) {
await activeBan.update({
is_lifted: true,
lifted_at: new Date(),
lifted_by: req.user.user_id,
lift_reason: lift_reason?.trim() || null,
}, { transaction: t });
}
await user.update({ is_banned: false, ban_expires_at: null }, { transaction: t });
});
logActivity(req.user.user_id, 'unban_user', { entityType: 'user', entityId: Number(id) });
sendEmail({
to: user.email,
type: 'BAN_LIFTED',
data: {
name: user.personal_info?.name?.full_name ?? 'User',
email: user.email,
date: fmtDate(new Date()),
},
}).catch((err) => console.error('[ADMIN][UNBAN USER] Email failed:', err));
return R.success(res, 'User unbanned successfully.');
} catch (err) {
console.error('[ADMIN][UNBAN USER]', err);
return R.error(res, 'Could not unban user.', 500);
}
};
// ─── BULK BAN ─────────────────────────────────────────────────────────────────
exports.bulkBanUsers = async (req, res) => {
try {
const { ids, reason, ban_type, expires_at } = req.body;
if (!Array.isArray(ids) || !ids.length)
return R.error(res, 'No user IDs provided.', 400);
if (ids.includes(req.user.user_id))
return R.error(res, 'You cannot ban your own account.', 400);
if (!reason?.trim()) return R.error(res, 'Ban reason is required.', 400);
if (!['temporary', 'permanent'].includes(ban_type))
return R.error(res, 'Invalid ban type.', 400);
if (ban_type === 'temporary' && !expires_at)
return R.error(res, 'Expiry date is required for temporary bans.', 400);
if (ban_type === 'temporary' && new Date(expires_at) <= new Date())
return R.error(res, 'Expiry date must be in the future.', 400);
const users = await mdl_Users.findAll({ where: { user_id: ids } });
if (!users.length) return R.error(res, 'No users found.', 404);
const unbannedUsers = users.filter((u) => !u.is_banned);
if (!unbannedUsers.length)
return R.error(res, 'All selected users are already banned.', 400);
const targetIds = unbannedUsers.map((u) => u.user_id);
const banExpiresAt = ban_type === 'temporary' ? new Date(expires_at) : null;
const now = new Date();
await sequelize.transaction(async (t) => {
await mdl_UserBans.bulkCreate(
targetIds.map((uid) => ({
user_id: uid,
banned_by: req.user.user_id,
reason: reason.trim(),
ban_type,
banned_at: now,
expires_at: banExpiresAt,
})),
{ transaction: t }
);
await mdl_Users.update(
{ is_banned: true, ban_expires_at: banExpiresAt },
{ where: { user_id: targetIds }, transaction: t }
);
await mdl_UserSessions.update(
{ is_active: false, logout_info: { date: now.toISOString(), forced_by: req.user.user_id } },
{ where: { user_id: targetIds }, transaction: t }
);
});
const dateStr = fmtDate(new Date());
unbannedUsers.forEach((u) => {
sendEmail({
to: u.email,
type: 'BANNED',
data: {
name: u.personal_info?.name?.full_name ?? 'User',
email: u.email,
date: dateStr,
reason: reason.trim(),
ban_type,
},
}).catch((err) => console.error('[ADMIN][BULK BAN] Email failed:', u.email, err));
});
return R.success(res, `${targetIds.length} user(s) banned successfully.`, {
banned_ids: targetIds,
skipped_ids: ids.filter((id) => !targetIds.includes(Number(id))),
});
} catch (err) {
console.error('[ADMIN][BULK BAN USERS]', err);
return R.error(res, 'Could not ban users.', 500);
}
};
// ─── GET USER BAN HISTORY ─────────────────────────────────────────────────────
exports.getUserBans = async (req, res) => {
try {
const { id } = req.params;
const user = await mdl_Users.findByPk(id, { attributes: ['user_id'] });
if (!user) return R.error(res, 'User not found.', 404);
const bans = await mdl_UserBans.findAll({
where: { user_id: id },
include: [
{ model: mdl_Users, as: 'banner', attributes: ['user_id', 'email', 'personal_info'] },
{ model: mdl_Users, as: 'lifter', attributes: ['user_id', 'email', 'personal_info'] },
],
order: [['banned_at', 'DESC']],
});
return R.success(res, 'Ban history retrieved.', bans);
} catch (err) {
console.error('[ADMIN][GET USER BANS]', err);
return R.error(res, 'Could not retrieve ban history.', 500);
}
};
// ─── BULK UNBAN ───────────────────────────────────────────────────────────────
exports.bulkUnbanUsers = async (req, res) => {
try {
const { ids, lift_reason } = req.body;
if (!Array.isArray(ids) || !ids.length)
return R.error(res, 'No user IDs provided.', 400);
const users = await mdl_Users.findAll({ where: { user_id: ids } });
if (!users.length) return R.error(res, 'No users found.', 404);
const bannedUsers = users.filter((u) => u.is_banned);
if (!bannedUsers.length)
return R.error(res, 'None of the selected users are currently banned.', 400);
const targetIds = bannedUsers.map((u) => u.user_id);
const now = new Date();
await sequelize.transaction(async (t) => {
await mdl_UserBans.update(
{
is_lifted: true,
lifted_at: now,
lifted_by: req.user.user_id,
lift_reason: lift_reason?.trim() || null,
},
{ where: { user_id: targetIds, is_lifted: false }, transaction: t }
);
await mdl_Users.update(
{ is_banned: false, ban_expires_at: null },
{ where: { user_id: targetIds }, transaction: t }
);
});
logActivity(req.user.user_id, 'bulk_unban_users', {
entityType: 'user',
details: { unban_ids: targetIds },
});
const dateStr = fmtDate(now);
bannedUsers.forEach((u) => {
sendEmail({
to: u.email,
type: 'BAN_LIFTED',
data: {
name: u.personal_info?.name?.full_name ?? 'User',
email: u.email,
date: dateStr,
},
}).catch((err) => console.error('[ADMIN][BULK UNBAN] Email failed:', u.email, err));
});
return R.success(res, `${targetIds.length} user(s) unbanned successfully.`, {
unbanned_ids: targetIds,
skipped_ids: ids.filter((id) => !targetIds.includes(Number(id))),
});
} catch (err) {
console.error('[ADMIN][BULK UNBAN USERS]', err);
return R.error(res, 'Could not unban users.', 500);
}
};