mirror of
https://github.com/rgrgogu/new_starr.git
synced 2026-09-27 00:12:54 +08:00
98 lines
4.1 KiB
JavaScript
98 lines
4.1 KiB
JavaScript
const { Op } = require("sequelize");
|
|
const sequelize = require("../config/db.config");
|
|
const Sequelize = require("sequelize");
|
|
const R = require("../utils/response.util"); // adjust path as needed
|
|
|
|
const auditByFields = ["createdBy", "updatedBy", "deletedBy"];
|
|
|
|
const getFieldValues = (Model, logTag, options = {}) => async (req, res) => {
|
|
const {
|
|
blockedFields = [],
|
|
extraDateFields = [],
|
|
allowJsonb = false,
|
|
paranoid = true,
|
|
selfJoin = false, // for users auditing users
|
|
} = options;
|
|
|
|
try {
|
|
const { field } = req.query;
|
|
if (!field) return R.error(res, "Field is required.", 400);
|
|
|
|
const dateFields = ["createdAt", "updatedAt", "deletedAt", ...extraDateFields];
|
|
|
|
// ─── Non-JSONB path ───────────────────────────────────────────────────
|
|
if (!field.includes(".")) {
|
|
const allowedFields = blockedFields.length
|
|
? Object.keys(Model.rawAttributes).filter((f) => !blockedFields.includes(f))
|
|
: Object.keys(Model.rawAttributes).filter((f) => Model.rawAttributes[f].filterable === true);
|
|
|
|
if (!allowedFields.includes(field) && !dateFields.includes(field))
|
|
return R.error(res, "Invalid or restricted field.", 400);
|
|
|
|
if (auditByFields.includes(field)) {
|
|
const tableName = Model.getTableName();
|
|
const [rows] = selfJoin
|
|
? await sequelize.query(`
|
|
SELECT DISTINCT u2."personal_info"->'name'->>'full_name' AS value
|
|
FROM "${tableName}" u1
|
|
JOIN "${tableName}" u2 ON u2.user_id = u1."${field}"
|
|
WHERE u1."${field}" IS NOT NULL
|
|
AND u2."personal_info"->'name'->>'full_name' IS NOT NULL
|
|
ORDER BY value ASC
|
|
`)
|
|
: await sequelize.query(`
|
|
SELECT DISTINCT u."personal_info"->'name'->>'full_name' AS value
|
|
FROM "${tableName}" t
|
|
JOIN users u ON u.user_id = t."${field}"
|
|
WHERE t."${field}" IS NOT NULL
|
|
AND u."personal_info"->'name'->>'full_name' IS NOT NULL
|
|
ORDER BY value ASC
|
|
`);
|
|
return R.success(res, "Field values retrieved.", rows.map((r) => r.value).filter(Boolean));
|
|
}
|
|
|
|
if (dateFields.includes(field)) {
|
|
const results = await Model.findAll({
|
|
attributes: [[Sequelize.fn("DISTINCT", Sequelize.fn("DATE", Sequelize.col(field))), "value"]],
|
|
where: { [field]: { [Op.ne]: null } },
|
|
order: [[Sequelize.fn("DATE", Sequelize.col(field)), "DESC"]],
|
|
paranoid,
|
|
raw: true,
|
|
});
|
|
return R.success(res, "Field values retrieved.", results.map((r) => r.value).filter(Boolean));
|
|
}
|
|
|
|
const results = await Model.findAll({
|
|
attributes: [[Sequelize.fn("DISTINCT", Sequelize.col(field)), "value"]],
|
|
where: { [field]: { [Op.ne]: null } },
|
|
paranoid,
|
|
raw: true,
|
|
});
|
|
return R.success(res, "Field values retrieved.", results.map((r) => r.value).filter(Boolean).sort());
|
|
}
|
|
|
|
// ─── JSONB dot-notation path ──────────────────────────────────────────
|
|
if (!allowJsonb)
|
|
return R.error(res, "JSONB fields are not supported.", 400);
|
|
|
|
const [column, ...pathParts] = field.split(".");
|
|
const keys = [...pathParts];
|
|
const lastKey = keys.pop();
|
|
const jsonbPath = keys.length
|
|
? `"${column}"->${keys.map((k) => `'${k}'`).join("->")}->>'${lastKey}'`
|
|
: `"${column}"->>'${lastKey}'`;
|
|
|
|
const results = await Model.findAll({
|
|
attributes: [[Sequelize.literal(`DISTINCT ${jsonbPath}`), "value"]],
|
|
where: Sequelize.literal(`${jsonbPath} IS NOT NULL`),
|
|
raw: true,
|
|
});
|
|
return R.success(res, "Field values retrieved.", results.map((r) => r.value).filter(Boolean).sort());
|
|
|
|
} catch (err) {
|
|
console.error(`[${logTag}][GET FIELD VALUES]`, err);
|
|
return R.error(res, "Could not retrieve field values.", 500);
|
|
}
|
|
};
|
|
|
|
module.exports = { getFieldValues }; |