/*********************************************************************************************************************************************************************** * File Name: users.controller.js (staff) * Type of Program: Controller * Description: Staff-level user management. * Staff can VIEW any client/staff user and perform limited actions. * Staff CANNOT modify admin accounts or assign/revoke admin roles. * * Endpoints (require authenticate → requireStaff()): * GET /api/staff/users → paginated user list (non-admins) * GET /api/staff/users/:id → view any non-admin user * PUT /api/staff/users/:id/status → activate / deactivate user * GET /api/staff/users/:id/sessions → view user sessions * * Author: rgrgogu * Date Created: Oct. 6, 2025 ***********************************************************************************************************************************************************************/ const { Op } = require('sequelize'); const mdl_Users = require('../../models/users/users.mdl'); const mdl_UserSessions = require('../../models/users/user_sessions.mdl'); const logActivity = require('../../utils/logActivity.util'); const R = require('../../utils/response.util'); const EXCLUDED = ['password', 'otp_code', 'otp_expires_at']; // ─── GET paginated list (no admins) ──────────────────────────────────────────── exports.getUsers = async (req, res) => { try { const page = Math.max(1, parseInt(req.query.page) || 1); const limit = Math.min(100, parseInt(req.query.limit) || 20); const offset = (page - 1) * limit; const { count, rows } = await mdl_Users.findAndCountAll({ where: { acc_type: { [Op.ne]: 'admin' } }, attributes: { exclude: EXCLUDED }, limit, offset, order: [['createdAt', 'DESC']], }); return R.success(res, 'Users retrieved.', { total: count, page, totalPages: Math.ceil(count / limit), users: rows, }); } catch (err) { console.error('[STAFF] getUsers error:', err); return R.error(res, 'Could not retrieve users.', 500); } }; // ─── GET single non-admin user ───────────────────────────────────────────────── exports.getUser = async (req, res) => { try { const user = await mdl_Users.findOne({ where: { user_id: req.params.id, acc_type: { [Op.ne]: 'admin' } }, attributes: { exclude: EXCLUDED }, }); if (!user) return R.error(res, 'User not found.', 404); return R.success(res, 'User retrieved.', user); } catch (err) { return R.error(res, 'Could not retrieve user.', 500); } }; // ─── PUT activate / deactivate ───────────────────────────────────────────────── exports.setUserStatus = async (req, res) => { try { const { is_active } = req.body; if (typeof is_active !== 'boolean') return R.error(res, 'is_active must be a boolean.', 400); const user = await mdl_Users.findOne({ where: { user_id: req.params.id, acc_type: { [Op.ne]: 'admin' } }, }); if (!user) return R.error(res, 'User not found or operation not permitted.', 404); await user.update({ is_active }); logActivity(req.user.user_id, 'set_user_status', { entityType: 'user', entityId: Number(req.params.id), metadata: { is_active }, }); return R.success(res, `User ${is_active ? 'activated' : 'deactivated'}.`); } catch (err) { return R.error(res, 'Could not update user status.', 500); } }; // ─── GET user sessions ───────────────────────────────────────────────────────── exports.getUserSessions = async (req, res) => { try { const user = await mdl_Users.findOne({ where: { user_id: req.params.id, acc_type: { [Op.ne]: 'admin' } }, }); if (!user) return R.error(res, 'User not found.', 404); const sessions = await mdl_UserSessions.findAll({ where: { user_id: req.params.id }, attributes: { exclude: ['refresh_token_hash'] }, order: [['createdAt', 'DESC']], }); return R.success(res, 'Sessions retrieved.', sessions); } catch (err) { return R.error(res, 'Could not retrieve sessions.', 500); } }; // ─── Staff profile (re-use client controller) ────────────────────────────────── // Staff also manage their own profile through the same client endpoints. // No additional staff-specific profile endpoints needed.