2026-03-17 06:59:09 +00:00

540 lines
9.6 KiB
JavaScript

const db = require('../db/models');
const ValidationError = require('./notifications/errors/validation');
const Sequelize = db.Sequelize;
const Op = Sequelize.Op;
/**
* @param {string} permission
* @param {object} currentUser
*/
async function checkPermissions(permission, currentUser) {
if (!currentUser) {
throw new ValidationError('auth.unauthorized');
}
const userPermission = currentUser.custom_permissions.find(
(cp) => cp.name === permission,
);
if (userPermission) {
return true;
}
try {
if (!currentUser.app_role) {
throw new ValidationError('auth.forbidden');
}
const permissions = await currentUser.app_role.getPermissions();
return !!permissions.find((p) => p.name === permission);
} catch (e) {
throw e;
}
}
module.exports = class SearchService {
static async search(searchQuery, currentUser ) {
try {
if (!searchQuery) {
throw new ValidationError('iam.errors.searchQueryRequired');
}
const tableColumns = {
"users": [
"firstName",
"lastName",
"phoneNumber",
"email",
],
"kelas": [
"nama_kelas",
"tingkat",
"tahun_ajaran",
],
"guru": [
"nip",
"nama",
"jenis_kelamin",
"tempat_lahir",
"alamat",
"no_hp",
],
"siswa": [
"nisn",
"nis",
"nama",
"jenis_kelamin",
"tempat_lahir",
"alamat",
"nama_ortu",
"no_hp_ortu",
],
"mata_pelajaran": [
"kode_mapel",
"nama_mapel",
],
"kktp": [
"fase",
"deskripsi_umum",
],
"kktp_detail": [
"deskripsi",
],
"nilai": [
"judul_penilaian",
"deskripsi_kktp",
],
"sikap": [
"tahun_ajaran",
"catatan_wali_kelas",
],
"materi": [
"judul",
"deskripsi",
"url_video",
],
"tugas": [
"judul",
"instruksi",
],
"pengumpulan_tugas": [
"jawaban_teks",
"feedback_guru",
],
"absensi": [
"keterangan",
],
"pengumuman": [
"judul",
"isi",
],
"notifikasi": [
"judul",
"pesan",
],
"ai_insights": [
"ringkasan",
"rekomendasi",
],
"raport_descriptions": [
"tahun_ajaran",
"deskripsi_kktp",
"ai_prompt",
"ai_output",
],
"audit_logs": [
"entity_name",
"entity_key",
"summary",
"ip_address",
"user_agent",
],
"import_jobs": [
"error_report",
],
};
const columnsInt = {
"mata_pelajaran": [
"urutan",
],
"kktp": [
"nilai_minimum",
],
"kktp_detail": [
"nilai_min",
"nilai_max",
],
"nilai": [
"skor",
],
"tugas": [
"maks_nilai",
],
"pengumpulan_tugas": [
"nilai",
],
"ai_insights": [
"skor_keyakinan",
],
"raport_descriptions": [
"nilai_akhir",
"ranking_kelas",
"rata_rata_kelas",
],
"import_jobs": [
"total_rows",
"success_rows",
"failed_rows",
],
};
let allFoundRecords = [];
for (const tableName in tableColumns) {
if (tableColumns.hasOwnProperty(tableName)) {
const attributesToSearch = tableColumns[tableName];
const attributesIntToSearch = columnsInt[tableName] || [];
const whereCondition = {
[Op.or]: [
...attributesToSearch.map(attribute => ({
[attribute]: {
[Op.iLike] : `%${searchQuery}%`,
},
})),
...attributesIntToSearch.map(attribute => (
Sequelize.where(
Sequelize.cast(Sequelize.col(`${tableName}.${attribute}`), 'varchar'),
{ [Op.iLike]: `%${searchQuery}%` }
)
)),
],
};
const hasPermission = await checkPermissions(`READ_${tableName.toUpperCase()}`, currentUser);
if (!hasPermission) {
continue;
}
const foundRecords = await db[tableName].findAll({
where: whereCondition,
attributes: [...tableColumns[tableName], 'id', ...attributesIntToSearch],
});
const modifiedRecords = foundRecords.map((record) => {
const matchAttribute = [];
for (const attribute of attributesToSearch) {
if (record[attribute]?.toLowerCase()?.includes(searchQuery.toLowerCase())) {
matchAttribute.push(attribute);
}
}
for (const attribute of attributesIntToSearch) {
const castedValue = String(record[attribute]);
if (castedValue && castedValue.toLowerCase().includes(searchQuery.toLowerCase())) {
matchAttribute.push(attribute);
}
}
return {
...record.get(),
matchAttribute,
tableName,
};
});
allFoundRecords = allFoundRecords.concat(modifiedRecords);
}
}
return allFoundRecords;
} catch (error) {
throw error;
}
}
}