mirror of
https://github.com/rgrgogu/new_starr.git
synced 2026-09-27 00:12:54 +08:00
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
113 lines
4.9 KiB
JavaScript
113 lines
4.9 KiB
JavaScript
/***********************************************************************************************************************************************************************
|
|
* 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.
|