Files
starr-philproperties/controllers/admin/user_activity.controller.js
T
kennethobsequio f5254f7571 test again
Signed-off-by: Kenneth Obsequio <k80308392@gmail.com>
2026-07-13 12:46:38 +08:00

137 lines
4.9 KiB
JavaScript

/***********************************************************************************************************************************************************************
* File Name: user_activity.controller.js (admin)
* Type of Program: Controller
* Description: Admin-only view of the user_activity log.
*
* GET /api/admin/activity → global paginated activity feed (all users)
* GET /api/admin/users/:id/activity → paginated activity for a single user
*
* Author: Kenneth Obsequio (@lash0000)
* Date Created: Jun. 21, 2026
***********************************************************************************************************************************************************************/
const { Op } = require('sequelize');
const mdl_UserActivity = require('../../models/users/user_activity.mdl');
const mdl_Users = require('../../models/users/users.mdl');
const R = require('../../utils/response.util');
const USER_ATTRS = [
'user_id', 'email', 'acc_type',
// full_name from JSONB — resolved in the association below
];
// ─── Shared query builder ─────────────────────────────────────────────────────
function buildWhere(query, extraWhere = {}) {
const where = { ...extraWhere };
if (query.action)
where.action = query.action;
if (query.from || query.to) {
where.created_at = {};
if (query.from) where.created_at[Op.gte] = new Date(query.from);
if (query.to) {
// `to` arrives as a date-only string (e.g. "2026-06-04"), which parses
// to that day's UTC midnight — an Op.lte against midnight excludes
// every event that happened later the same day. Push it to the last
// instant of that calendar day instead.
const to = new Date(query.to);
to.setUTCHours(23, 59, 59, 999);
where.created_at[Op.lte] = to;
}
}
return where;
}
// ─── GET GLOBAL ACTIVITY FEED ─────────────────────────────────────────────────
exports.getActivity = 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 where = buildWhere(req.query);
const { count, rows } = await mdl_UserActivity.findAndCountAll({
where,
include: [{
model: mdl_Users,
as: 'user',
attributes: ['user_id', 'email', 'acc_type', 'personal_info'],
}],
order: [['created_at', 'DESC']],
limit,
offset,
});
const data = rows.map(formatRow);
return R.success(res, 'Activity feed retrieved.', {
total: count,
page,
totalPages: Math.ceil(count / limit),
activities: data,
});
} catch (err) {
console.error('[ADMIN][GET ACTIVITY FEED]', err);
return R.error(res, 'Could not retrieve activity feed.', 500);
}
};
// ─── GET PER-USER ACTIVITY ────────────────────────────────────────────────────
exports.getUserActivity = async (req, res) => {
try {
const { id } = req.params;
if (!id || id === 'undefined') return R.error(res, 'Invalid User ID.', 400);
const user = await mdl_Users.findByPk(id, { attributes: ['user_id'] });
if (!user) return R.error(res, 'User not found.', 404);
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 where = buildWhere(req.query, { user_id: id });
const { count, rows } = await mdl_UserActivity.findAndCountAll({
where,
order: [['created_at', 'DESC']],
limit,
offset,
});
return R.success(res, 'User activity retrieved.', {
total: count,
page,
totalPages: Math.ceil(count / limit),
activities: rows,
});
} catch (err) {
console.error('[ADMIN][GET USER ACTIVITY]', err);
return R.error(res, 'Could not retrieve user activity.', 500);
}
};
// ─── Helpers ──────────────────────────────────────────────────────────────────
function formatRow(row) {
const r = row.toJSON();
const info = r.user?.personal_info;
return {
activity_id: r.activity_id,
user_id: r.user_id,
email: r.user?.email ?? null,
full_name: info?.name?.full_name ?? null,
avatar_url: info?.avatar?.url ?? null,
acc_type: r.user?.acc_type ?? null,
action: r.action,
entity_type: r.entity_type,
entity_id: r.entity_id,
details: r.details,
created_at: r.created_at,
};
}