540 lines
9.6 KiB
JavaScript
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;
|
|
}
|
|
}
|
|
} |