Compare commits
No commits in common. "ai-dev" and "master" have entirely different histories.
@ -154,7 +154,7 @@ async function awaitResponse(aiRequestId, options = {}) {
|
||||
const interval = Math.max(Number(options.interval ?? 5), 1);
|
||||
const deadline = Date.now() + Math.max(timeout, interval) * 1000;
|
||||
|
||||
while (Date.now() < deadline) {
|
||||
while (true) {
|
||||
const statusResp = await fetchStatus(aiRequestId, {
|
||||
headers: options.headers,
|
||||
timeout: options.timeout_per_call,
|
||||
@ -184,14 +184,16 @@ async function awaitResponse(aiRequestId, options = {}) {
|
||||
return statusResp;
|
||||
}
|
||||
|
||||
if (Date.now() >= deadline) {
|
||||
return {
|
||||
success: false,
|
||||
error: "timeout",
|
||||
message: "Timed out waiting for AI response.",
|
||||
};
|
||||
}
|
||||
|
||||
await sleep(interval * 1000);
|
||||
}
|
||||
|
||||
return {
|
||||
success: false,
|
||||
error: "timeout",
|
||||
message: "Timed out waiting for AI response.",
|
||||
};
|
||||
}
|
||||
|
||||
function extractText(response) {
|
||||
|
||||
@ -56,7 +56,7 @@ passport.use(new MicrosoftStrategy({
|
||||
));
|
||||
|
||||
function socialStrategy(email, profile, provider, done) {
|
||||
db.users.findOrCreate({where: {email, provider}}).then(([user]) => {
|
||||
db.users.findOrCreate({where: {email, provider}}).then(([user, created]) => {
|
||||
const body = {
|
||||
id: user.id,
|
||||
email: user.email,
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
|
||||
const db = require('../models');
|
||||
const FileDBApi = require('./file');
|
||||
const crypto = require('crypto');
|
||||
const Utils = require('../utils');
|
||||
const { applySchoolScope, applySchoolScopeById, assertRecordInCurrentSchool, assertRelatedRecordInCurrentSchool, resolveSchoolIdForMutation } = require('./schoolScope');
|
||||
|
||||
|
||||
|
||||
@ -48,21 +49,18 @@ module.exports = class AssessmentsDBApi {
|
||||
);
|
||||
|
||||
|
||||
await assessments.setSchool(resolveSchoolIdForMutation(data.school, currentUser), {
|
||||
await assessments.setSchool( data.school || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
await assertRelatedRecordInCurrentSchool('students', data.student, currentUser, 'schoolId', { transaction });
|
||||
await assessments.setStudent( data.student || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
await assertRelatedRecordInCurrentSchool('subjects', data.subject, currentUser, 'schoolId', { transaction });
|
||||
await assessments.setSubject( data.subject || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
await assertRelatedRecordInCurrentSchool('teachers', data.teacher, currentUser, 'schoolId', { transaction });
|
||||
await assessments.setTeacher( data.teacher || null, {
|
||||
transaction,
|
||||
});
|
||||
@ -107,7 +105,6 @@ module.exports = class AssessmentsDBApi {
|
||||
importHash: item.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
schoolId: resolveSchoolIdForMutation(item.school, currentUser),
|
||||
createdAt: new Date(Date.now() + index * 1000),
|
||||
}));
|
||||
|
||||
@ -123,8 +120,9 @@ module.exports = class AssessmentsDBApi {
|
||||
static async update(id, data, options) {
|
||||
const currentUser = (options && options.currentUser) || {id: null};
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
const globalAccess = currentUser.app_role?.globalAccess;
|
||||
|
||||
const assessments = await db.assessments.findByPk(id, {}, {transaction});
|
||||
assertRecordInCurrentSchool(assessments, currentUser, 'schoolId');
|
||||
|
||||
|
||||
|
||||
@ -152,14 +150,13 @@ module.exports = class AssessmentsDBApi {
|
||||
if (data.school !== undefined) {
|
||||
await assessments.setSchool(
|
||||
|
||||
resolveSchoolIdForMutation(data.school, currentUser),
|
||||
data.school,
|
||||
|
||||
{ transaction }
|
||||
);
|
||||
}
|
||||
|
||||
if (data.student !== undefined) {
|
||||
await assertRelatedRecordInCurrentSchool('students', data.student, currentUser, 'schoolId', { transaction });
|
||||
await assessments.setStudent(
|
||||
|
||||
data.student,
|
||||
@ -169,7 +166,6 @@ module.exports = class AssessmentsDBApi {
|
||||
}
|
||||
|
||||
if (data.subject !== undefined) {
|
||||
await assertRelatedRecordInCurrentSchool('subjects', data.subject, currentUser, 'schoolId', { transaction });
|
||||
await assessments.setSubject(
|
||||
|
||||
data.subject,
|
||||
@ -179,7 +175,6 @@ module.exports = class AssessmentsDBApi {
|
||||
}
|
||||
|
||||
if (data.teacher !== undefined) {
|
||||
await assertRelatedRecordInCurrentSchool('teachers', data.teacher, currentUser, 'schoolId', { transaction });
|
||||
await assessments.setTeacher(
|
||||
|
||||
data.teacher,
|
||||
@ -210,8 +205,6 @@ module.exports = class AssessmentsDBApi {
|
||||
transaction,
|
||||
});
|
||||
|
||||
assessments.forEach((record) => assertRecordInCurrentSchool(record, currentUser, 'schoolId'));
|
||||
|
||||
await db.sequelize.transaction(async (transaction) => {
|
||||
for (const record of assessments) {
|
||||
await record.update(
|
||||
@ -233,7 +226,6 @@ module.exports = class AssessmentsDBApi {
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const assessments = await db.assessments.findByPk(id, options);
|
||||
assertRecordInCurrentSchool(assessments, currentUser, 'schoolId');
|
||||
|
||||
await assessments.update({
|
||||
deletedBy: currentUser.id
|
||||
@ -250,9 +242,6 @@ module.exports = class AssessmentsDBApi {
|
||||
|
||||
static async findBy(where, options) {
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
const currentUser = (options && options.currentUser) || null;
|
||||
|
||||
applySchoolScope(where, currentUser?.app_role?.globalAccess, currentUser, 'schoolId');
|
||||
|
||||
const assessments = await db.assessments.findOne(
|
||||
{ where },
|
||||
@ -320,11 +309,26 @@ module.exports = class AssessmentsDBApi {
|
||||
let offset = 0;
|
||||
let where = {};
|
||||
const currentPage = +filter.page;
|
||||
|
||||
|
||||
const user = (options && options.currentUser) || null;
|
||||
const userSchools = (user && user.schools?.id) || null;
|
||||
|
||||
|
||||
|
||||
if (userSchools) {
|
||||
if (options?.currentUser?.schoolsId) {
|
||||
where.schoolsId = options.currentUser.schoolsId;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
offset = currentPage * limit;
|
||||
|
||||
const orderBy = null;
|
||||
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
let include = [
|
||||
|
||||
{
|
||||
@ -526,7 +530,11 @@ module.exports = class AssessmentsDBApi {
|
||||
}
|
||||
|
||||
|
||||
applySchoolScope(where, globalAccess, user, 'schoolId');
|
||||
|
||||
if (globalAccess) {
|
||||
delete where.schoolsId;
|
||||
}
|
||||
|
||||
|
||||
const queryOptions = {
|
||||
where,
|
||||
@ -557,9 +565,14 @@ module.exports = class AssessmentsDBApi {
|
||||
}
|
||||
}
|
||||
|
||||
static async findAllAutocomplete(query, limit, offset, globalAccess, schoolId,) {
|
||||
static async findAllAutocomplete(query, limit, offset, globalAccess, organizationId,) {
|
||||
let where = {};
|
||||
|
||||
|
||||
if (!globalAccess && organizationId) {
|
||||
where.organizationId = organizationId;
|
||||
}
|
||||
|
||||
|
||||
if (query) {
|
||||
where = {
|
||||
@ -574,8 +587,6 @@ module.exports = class AssessmentsDBApi {
|
||||
};
|
||||
}
|
||||
|
||||
applySchoolScopeById(where, globalAccess, schoolId, 'schoolId');
|
||||
|
||||
const records = await db.assessments.findAll({
|
||||
attributes: [ 'id', 'tipo' ],
|
||||
where,
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
|
||||
const db = require('../models');
|
||||
const FileDBApi = require('./file');
|
||||
const crypto = require('crypto');
|
||||
const Utils = require('../utils');
|
||||
const { applySchoolScope, applySchoolScopeById, assertRecordInCurrentSchool, assertRelatedRecordInCurrentSchool, resolveSchoolIdForMutation } = require('./schoolScope');
|
||||
|
||||
|
||||
|
||||
@ -44,11 +45,10 @@ module.exports = class AttendanceDBApi {
|
||||
);
|
||||
|
||||
|
||||
await attendance.setSchool(resolveSchoolIdForMutation(data.school, currentUser), {
|
||||
await attendance.setSchool( data.school || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
await assertRelatedRecordInCurrentSchool('students', data.student, currentUser, 'schoolId', { transaction });
|
||||
await attendance.setStudent( data.student || null, {
|
||||
transaction,
|
||||
});
|
||||
@ -89,7 +89,6 @@ module.exports = class AttendanceDBApi {
|
||||
importHash: item.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
schoolId: resolveSchoolIdForMutation(item.school, currentUser),
|
||||
createdAt: new Date(Date.now() + index * 1000),
|
||||
}));
|
||||
|
||||
@ -105,8 +104,9 @@ module.exports = class AttendanceDBApi {
|
||||
static async update(id, data, options) {
|
||||
const currentUser = (options && options.currentUser) || {id: null};
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
const globalAccess = currentUser.app_role?.globalAccess;
|
||||
|
||||
const attendance = await db.attendance.findByPk(id, {}, {transaction});
|
||||
assertRecordInCurrentSchool(attendance, currentUser, 'schoolId');
|
||||
|
||||
|
||||
|
||||
@ -131,14 +131,13 @@ module.exports = class AttendanceDBApi {
|
||||
if (data.school !== undefined) {
|
||||
await attendance.setSchool(
|
||||
|
||||
resolveSchoolIdForMutation(data.school, currentUser),
|
||||
data.school,
|
||||
|
||||
{ transaction }
|
||||
);
|
||||
}
|
||||
|
||||
if (data.student !== undefined) {
|
||||
await assertRelatedRecordInCurrentSchool('students', data.student, currentUser, 'schoolId', { transaction });
|
||||
await attendance.setStudent(
|
||||
|
||||
data.student,
|
||||
@ -169,8 +168,6 @@ module.exports = class AttendanceDBApi {
|
||||
transaction,
|
||||
});
|
||||
|
||||
attendance.forEach((record) => assertRecordInCurrentSchool(record, currentUser, 'schoolId'));
|
||||
|
||||
await db.sequelize.transaction(async (transaction) => {
|
||||
for (const record of attendance) {
|
||||
await record.update(
|
||||
@ -192,7 +189,6 @@ module.exports = class AttendanceDBApi {
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const attendance = await db.attendance.findByPk(id, options);
|
||||
assertRecordInCurrentSchool(attendance, currentUser, 'schoolId');
|
||||
|
||||
await attendance.update({
|
||||
deletedBy: currentUser.id
|
||||
@ -209,9 +205,6 @@ module.exports = class AttendanceDBApi {
|
||||
|
||||
static async findBy(where, options) {
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
const currentUser = (options && options.currentUser) || null;
|
||||
|
||||
applySchoolScope(where, currentUser?.app_role?.globalAccess, currentUser, 'schoolId');
|
||||
|
||||
const attendance = await db.attendance.findOne(
|
||||
{ where },
|
||||
@ -269,11 +262,26 @@ module.exports = class AttendanceDBApi {
|
||||
let offset = 0;
|
||||
let where = {};
|
||||
const currentPage = +filter.page;
|
||||
|
||||
|
||||
const user = (options && options.currentUser) || null;
|
||||
const userSchools = (user && user.schools?.id) || null;
|
||||
|
||||
|
||||
|
||||
if (userSchools) {
|
||||
if (options?.currentUser?.schoolsId) {
|
||||
where.schoolsId = options.currentUser.schoolsId;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
offset = currentPage * limit;
|
||||
|
||||
const orderBy = null;
|
||||
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
let include = [
|
||||
|
||||
{
|
||||
@ -413,7 +421,11 @@ module.exports = class AttendanceDBApi {
|
||||
}
|
||||
|
||||
|
||||
applySchoolScope(where, globalAccess, user, 'schoolId');
|
||||
|
||||
if (globalAccess) {
|
||||
delete where.schoolsId;
|
||||
}
|
||||
|
||||
|
||||
const queryOptions = {
|
||||
where,
|
||||
@ -444,9 +456,14 @@ module.exports = class AttendanceDBApi {
|
||||
}
|
||||
}
|
||||
|
||||
static async findAllAutocomplete(query, limit, offset, globalAccess, schoolId,) {
|
||||
static async findAllAutocomplete(query, limit, offset, globalAccess, organizationId,) {
|
||||
let where = {};
|
||||
|
||||
|
||||
if (!globalAccess && organizationId) {
|
||||
where.organizationId = organizationId;
|
||||
}
|
||||
|
||||
|
||||
if (query) {
|
||||
where = {
|
||||
@ -461,8 +478,6 @@ module.exports = class AttendanceDBApi {
|
||||
};
|
||||
}
|
||||
|
||||
applySchoolScopeById(where, globalAccess, schoolId, 'schoolId');
|
||||
|
||||
const records = await db.attendance.findAll({
|
||||
attributes: [ 'id', 'observacao' ],
|
||||
where,
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
|
||||
const db = require('../models');
|
||||
const FileDBApi = require('./file');
|
||||
const crypto = require('crypto');
|
||||
const Utils = require('../utils');
|
||||
const { applySchoolScope, applySchoolScopeById, assertRecordInCurrentSchool, assertRelatedRecordInCurrentSchool, resolveSchoolIdForMutation } = require('./schoolScope');
|
||||
|
||||
|
||||
|
||||
@ -53,16 +54,14 @@ module.exports = class Book_loansDBApi {
|
||||
);
|
||||
|
||||
|
||||
await book_loans.setSchool(resolveSchoolIdForMutation(data.school, currentUser), {
|
||||
await book_loans.setSchool( data.school || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
await assertRelatedRecordInCurrentSchool('books', data.book, currentUser, 'schoolId', { transaction });
|
||||
await book_loans.setBook( data.book || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
await assertRelatedRecordInCurrentSchool('students', data.student, currentUser, 'schoolId', { transaction });
|
||||
await book_loans.setStudent( data.student || null, {
|
||||
transaction,
|
||||
});
|
||||
@ -112,7 +111,6 @@ module.exports = class Book_loansDBApi {
|
||||
importHash: item.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
schoolId: resolveSchoolIdForMutation(item.school, currentUser),
|
||||
createdAt: new Date(Date.now() + index * 1000),
|
||||
}));
|
||||
|
||||
@ -128,8 +126,9 @@ module.exports = class Book_loansDBApi {
|
||||
static async update(id, data, options) {
|
||||
const currentUser = (options && options.currentUser) || {id: null};
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
const globalAccess = currentUser.app_role?.globalAccess;
|
||||
|
||||
const book_loans = await db.book_loans.findByPk(id, {}, {transaction});
|
||||
assertRecordInCurrentSchool(book_loans, currentUser, 'schoolId');
|
||||
|
||||
|
||||
|
||||
@ -160,14 +159,13 @@ module.exports = class Book_loansDBApi {
|
||||
if (data.school !== undefined) {
|
||||
await book_loans.setSchool(
|
||||
|
||||
resolveSchoolIdForMutation(data.school, currentUser),
|
||||
data.school,
|
||||
|
||||
{ transaction }
|
||||
);
|
||||
}
|
||||
|
||||
if (data.book !== undefined) {
|
||||
await assertRelatedRecordInCurrentSchool('books', data.book, currentUser, 'schoolId', { transaction });
|
||||
await book_loans.setBook(
|
||||
|
||||
data.book,
|
||||
@ -177,7 +175,6 @@ module.exports = class Book_loansDBApi {
|
||||
}
|
||||
|
||||
if (data.student !== undefined) {
|
||||
await assertRelatedRecordInCurrentSchool('students', data.student, currentUser, 'schoolId', { transaction });
|
||||
await book_loans.setStudent(
|
||||
|
||||
data.student,
|
||||
@ -208,8 +205,6 @@ module.exports = class Book_loansDBApi {
|
||||
transaction,
|
||||
});
|
||||
|
||||
book_loans.forEach((record) => assertRecordInCurrentSchool(record, currentUser, 'schoolId'));
|
||||
|
||||
await db.sequelize.transaction(async (transaction) => {
|
||||
for (const record of book_loans) {
|
||||
await record.update(
|
||||
@ -231,7 +226,6 @@ module.exports = class Book_loansDBApi {
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const book_loans = await db.book_loans.findByPk(id, options);
|
||||
assertRecordInCurrentSchool(book_loans, currentUser, 'schoolId');
|
||||
|
||||
await book_loans.update({
|
||||
deletedBy: currentUser.id
|
||||
@ -248,9 +242,6 @@ module.exports = class Book_loansDBApi {
|
||||
|
||||
static async findBy(where, options) {
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
const currentUser = (options && options.currentUser) || null;
|
||||
|
||||
applySchoolScope(where, currentUser?.app_role?.globalAccess, currentUser, 'schoolId');
|
||||
|
||||
const book_loans = await db.book_loans.findOne(
|
||||
{ where },
|
||||
@ -313,11 +304,26 @@ module.exports = class Book_loansDBApi {
|
||||
let offset = 0;
|
||||
let where = {};
|
||||
const currentPage = +filter.page;
|
||||
|
||||
|
||||
const user = (options && options.currentUser) || null;
|
||||
const userSchools = (user && user.schools?.id) || null;
|
||||
|
||||
|
||||
|
||||
if (userSchools) {
|
||||
if (options?.currentUser?.schoolsId) {
|
||||
where.schoolsId = options.currentUser.schoolsId;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
offset = currentPage * limit;
|
||||
|
||||
const orderBy = null;
|
||||
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
let include = [
|
||||
|
||||
{
|
||||
@ -555,7 +561,11 @@ module.exports = class Book_loansDBApi {
|
||||
}
|
||||
|
||||
|
||||
applySchoolScope(where, globalAccess, user, 'schoolId');
|
||||
|
||||
if (globalAccess) {
|
||||
delete where.schoolsId;
|
||||
}
|
||||
|
||||
|
||||
const queryOptions = {
|
||||
where,
|
||||
@ -586,9 +596,14 @@ module.exports = class Book_loansDBApi {
|
||||
}
|
||||
}
|
||||
|
||||
static async findAllAutocomplete(query, limit, offset, globalAccess, schoolId,) {
|
||||
static async findAllAutocomplete(query, limit, offset, globalAccess, organizationId,) {
|
||||
let where = {};
|
||||
|
||||
|
||||
if (!globalAccess && organizationId) {
|
||||
where.organizationId = organizationId;
|
||||
}
|
||||
|
||||
|
||||
if (query) {
|
||||
where = {
|
||||
@ -603,8 +618,6 @@ module.exports = class Book_loansDBApi {
|
||||
};
|
||||
}
|
||||
|
||||
applySchoolScopeById(where, globalAccess, schoolId, 'schoolId');
|
||||
|
||||
const records = await db.book_loans.findAll({
|
||||
attributes: [ 'id', 'status' ],
|
||||
where,
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
|
||||
const db = require('../models');
|
||||
const FileDBApi = require('./file');
|
||||
const crypto = require('crypto');
|
||||
const Utils = require('../utils');
|
||||
const { applySchoolScope, applySchoolScopeById, assertRecordInCurrentSchool, resolveSchoolIdForMutation } = require('./schoolScope');
|
||||
|
||||
|
||||
|
||||
@ -58,7 +59,7 @@ module.exports = class BooksDBApi {
|
||||
);
|
||||
|
||||
|
||||
await books.setSchool(resolveSchoolIdForMutation(data.school, currentUser), {
|
||||
await books.setSchool( data.school || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
@ -112,7 +113,6 @@ module.exports = class BooksDBApi {
|
||||
importHash: item.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
schoolId: resolveSchoolIdForMutation(item.school, currentUser),
|
||||
createdAt: new Date(Date.now() + index * 1000),
|
||||
}));
|
||||
|
||||
@ -128,8 +128,9 @@ module.exports = class BooksDBApi {
|
||||
static async update(id, data, options) {
|
||||
const currentUser = (options && options.currentUser) || {id: null};
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
const globalAccess = currentUser.app_role?.globalAccess;
|
||||
|
||||
const books = await db.books.findByPk(id, {}, {transaction});
|
||||
assertRecordInCurrentSchool(books, currentUser, 'schoolId');
|
||||
|
||||
|
||||
|
||||
@ -163,7 +164,7 @@ module.exports = class BooksDBApi {
|
||||
if (data.school !== undefined) {
|
||||
await books.setSchool(
|
||||
|
||||
resolveSchoolIdForMutation(data.school, currentUser),
|
||||
data.school,
|
||||
|
||||
{ transaction }
|
||||
);
|
||||
@ -191,8 +192,6 @@ module.exports = class BooksDBApi {
|
||||
transaction,
|
||||
});
|
||||
|
||||
books.forEach((record) => assertRecordInCurrentSchool(record, currentUser, 'schoolId'));
|
||||
|
||||
await db.sequelize.transaction(async (transaction) => {
|
||||
for (const record of books) {
|
||||
await record.update(
|
||||
@ -214,7 +213,6 @@ module.exports = class BooksDBApi {
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const books = await db.books.findByPk(id, options);
|
||||
assertRecordInCurrentSchool(books, currentUser, 'schoolId');
|
||||
|
||||
await books.update({
|
||||
deletedBy: currentUser.id
|
||||
@ -231,9 +229,6 @@ module.exports = class BooksDBApi {
|
||||
|
||||
static async findBy(where, options) {
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
const currentUser = (options && options.currentUser) || null;
|
||||
|
||||
applySchoolScope(where, currentUser?.app_role?.globalAccess, currentUser, 'schoolId');
|
||||
|
||||
const books = await db.books.findOne(
|
||||
{ where },
|
||||
@ -290,11 +285,26 @@ module.exports = class BooksDBApi {
|
||||
let offset = 0;
|
||||
let where = {};
|
||||
const currentPage = +filter.page;
|
||||
|
||||
|
||||
const user = (options && options.currentUser) || null;
|
||||
const userSchools = (user && user.schools?.id) || null;
|
||||
|
||||
|
||||
|
||||
if (userSchools) {
|
||||
if (options?.currentUser?.schoolsId) {
|
||||
where.schoolsId = options.currentUser.schoolsId;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
offset = currentPage * limit;
|
||||
|
||||
const orderBy = null;
|
||||
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
let include = [
|
||||
|
||||
{
|
||||
@ -461,7 +471,11 @@ module.exports = class BooksDBApi {
|
||||
}
|
||||
|
||||
|
||||
applySchoolScope(where, globalAccess, user, 'schoolId');
|
||||
|
||||
if (globalAccess) {
|
||||
delete where.schoolsId;
|
||||
}
|
||||
|
||||
|
||||
const queryOptions = {
|
||||
where,
|
||||
@ -492,9 +506,14 @@ module.exports = class BooksDBApi {
|
||||
}
|
||||
}
|
||||
|
||||
static async findAllAutocomplete(query, limit, offset, globalAccess, schoolId,) {
|
||||
static async findAllAutocomplete(query, limit, offset, globalAccess, organizationId,) {
|
||||
let where = {};
|
||||
|
||||
|
||||
if (!globalAccess && organizationId) {
|
||||
where.organizationId = organizationId;
|
||||
}
|
||||
|
||||
|
||||
if (query) {
|
||||
where = {
|
||||
@ -509,8 +528,6 @@ module.exports = class BooksDBApi {
|
||||
};
|
||||
}
|
||||
|
||||
applySchoolScopeById(where, globalAccess, schoolId, 'schoolId');
|
||||
|
||||
const records = await db.books.findAll({
|
||||
attributes: [ 'id', 'titulo' ],
|
||||
where,
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
|
||||
const db = require('../models');
|
||||
const FileDBApi = require('./file');
|
||||
const crypto = require('crypto');
|
||||
const Utils = require('../utils');
|
||||
const { applySchoolScope, applySchoolScopeById, assertRecordInCurrentSchool, assertRelatedRecordInCurrentSchool, resolveSchoolIdForMutation } = require('./schoolScope');
|
||||
|
||||
|
||||
|
||||
@ -53,11 +54,10 @@ module.exports = class ClassesDBApi {
|
||||
);
|
||||
|
||||
|
||||
await classes.setSchool(resolveSchoolIdForMutation(data.school, currentUser), {
|
||||
await classes.setSchool( data.school || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
await assertRelatedRecordInCurrentSchool('grades', data.grade, currentUser, 'schoolId', { transaction });
|
||||
await classes.setGrade( data.grade || null, {
|
||||
transaction,
|
||||
});
|
||||
@ -107,7 +107,6 @@ module.exports = class ClassesDBApi {
|
||||
importHash: item.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
schoolId: resolveSchoolIdForMutation(item.school, currentUser),
|
||||
createdAt: new Date(Date.now() + index * 1000),
|
||||
}));
|
||||
|
||||
@ -123,8 +122,9 @@ module.exports = class ClassesDBApi {
|
||||
static async update(id, data, options) {
|
||||
const currentUser = (options && options.currentUser) || {id: null};
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
const globalAccess = currentUser.app_role?.globalAccess;
|
||||
|
||||
const classes = await db.classes.findByPk(id, {}, {transaction});
|
||||
assertRecordInCurrentSchool(classes, currentUser, 'schoolId');
|
||||
|
||||
|
||||
|
||||
@ -155,14 +155,13 @@ module.exports = class ClassesDBApi {
|
||||
if (data.school !== undefined) {
|
||||
await classes.setSchool(
|
||||
|
||||
resolveSchoolIdForMutation(data.school, currentUser),
|
||||
data.school,
|
||||
|
||||
{ transaction }
|
||||
);
|
||||
}
|
||||
|
||||
if (data.grade !== undefined) {
|
||||
await assertRelatedRecordInCurrentSchool('grades', data.grade, currentUser, 'schoolId', { transaction });
|
||||
await classes.setGrade(
|
||||
|
||||
data.grade,
|
||||
@ -193,8 +192,6 @@ module.exports = class ClassesDBApi {
|
||||
transaction,
|
||||
});
|
||||
|
||||
classes.forEach((record) => assertRecordInCurrentSchool(record, currentUser, 'schoolId'));
|
||||
|
||||
await db.sequelize.transaction(async (transaction) => {
|
||||
for (const record of classes) {
|
||||
await record.update(
|
||||
@ -216,7 +213,6 @@ module.exports = class ClassesDBApi {
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const classes = await db.classes.findByPk(id, options);
|
||||
assertRecordInCurrentSchool(classes, currentUser, 'schoolId');
|
||||
|
||||
await classes.update({
|
||||
deletedBy: currentUser.id
|
||||
@ -233,9 +229,6 @@ module.exports = class ClassesDBApi {
|
||||
|
||||
static async findBy(where, options) {
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
const currentUser = (options && options.currentUser) || null;
|
||||
|
||||
applySchoolScope(where, currentUser?.app_role?.globalAccess, currentUser, 'schoolId');
|
||||
|
||||
const classes = await db.classes.findOne(
|
||||
{ where },
|
||||
@ -297,11 +290,26 @@ module.exports = class ClassesDBApi {
|
||||
let offset = 0;
|
||||
let where = {};
|
||||
const currentPage = +filter.page;
|
||||
|
||||
|
||||
const user = (options && options.currentUser) || null;
|
||||
const userSchools = (user && user.schools?.id) || null;
|
||||
|
||||
|
||||
|
||||
if (userSchools) {
|
||||
if (options?.currentUser?.schoolsId) {
|
||||
where.schoolsId = options.currentUser.schoolsId;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
offset = currentPage * limit;
|
||||
|
||||
const orderBy = null;
|
||||
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
let include = [
|
||||
|
||||
{
|
||||
@ -459,7 +467,11 @@ module.exports = class ClassesDBApi {
|
||||
}
|
||||
|
||||
|
||||
applySchoolScope(where, globalAccess, user, 'schoolId');
|
||||
|
||||
if (globalAccess) {
|
||||
delete where.schoolsId;
|
||||
}
|
||||
|
||||
|
||||
const queryOptions = {
|
||||
where,
|
||||
@ -490,9 +502,14 @@ module.exports = class ClassesDBApi {
|
||||
}
|
||||
}
|
||||
|
||||
static async findAllAutocomplete(query, limit, offset, globalAccess, schoolId,) {
|
||||
static async findAllAutocomplete(query, limit, offset, globalAccess, organizationId,) {
|
||||
let where = {};
|
||||
|
||||
|
||||
if (!globalAccess && organizationId) {
|
||||
where.organizationId = organizationId;
|
||||
}
|
||||
|
||||
|
||||
if (query) {
|
||||
where = {
|
||||
@ -507,8 +524,6 @@ module.exports = class ClassesDBApi {
|
||||
};
|
||||
}
|
||||
|
||||
applySchoolScopeById(where, globalAccess, schoolId, 'schoolId');
|
||||
|
||||
const records = await db.classes.findAll({
|
||||
attributes: [ 'id', 'nome' ],
|
||||
where,
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
|
||||
const db = require('../models');
|
||||
const FileDBApi = require('./file');
|
||||
const crypto = require('crypto');
|
||||
const Utils = require('../utils');
|
||||
const { applySchoolScope, applySchoolScopeById, assertRecordInCurrentSchool, resolveSchoolIdForMutation } = require('./schoolScope');
|
||||
|
||||
|
||||
|
||||
@ -43,7 +44,7 @@ module.exports = class CoursesDBApi {
|
||||
);
|
||||
|
||||
|
||||
await courses.setSchool(resolveSchoolIdForMutation(data.school, currentUser), {
|
||||
await courses.setSchool( data.school || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
@ -82,7 +83,6 @@ module.exports = class CoursesDBApi {
|
||||
importHash: item.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
schoolId: resolveSchoolIdForMutation(item.school, currentUser),
|
||||
createdAt: new Date(Date.now() + index * 1000),
|
||||
}));
|
||||
|
||||
@ -98,8 +98,9 @@ module.exports = class CoursesDBApi {
|
||||
static async update(id, data, options) {
|
||||
const currentUser = (options && options.currentUser) || {id: null};
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
const globalAccess = currentUser.app_role?.globalAccess;
|
||||
|
||||
const courses = await db.courses.findByPk(id, {}, {transaction});
|
||||
assertRecordInCurrentSchool(courses, currentUser, 'schoolId');
|
||||
|
||||
|
||||
|
||||
@ -124,7 +125,7 @@ module.exports = class CoursesDBApi {
|
||||
if (data.school !== undefined) {
|
||||
await courses.setSchool(
|
||||
|
||||
resolveSchoolIdForMutation(data.school, currentUser),
|
||||
data.school,
|
||||
|
||||
{ transaction }
|
||||
);
|
||||
@ -152,8 +153,6 @@ module.exports = class CoursesDBApi {
|
||||
transaction,
|
||||
});
|
||||
|
||||
courses.forEach((record) => assertRecordInCurrentSchool(record, currentUser, 'schoolId'));
|
||||
|
||||
await db.sequelize.transaction(async (transaction) => {
|
||||
for (const record of courses) {
|
||||
await record.update(
|
||||
@ -175,7 +174,6 @@ module.exports = class CoursesDBApi {
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const courses = await db.courses.findByPk(id, options);
|
||||
assertRecordInCurrentSchool(courses, currentUser, 'schoolId');
|
||||
|
||||
await courses.update({
|
||||
deletedBy: currentUser.id
|
||||
@ -192,9 +190,6 @@ module.exports = class CoursesDBApi {
|
||||
|
||||
static async findBy(where, options) {
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
const currentUser = (options && options.currentUser) || null;
|
||||
|
||||
applySchoolScope(where, currentUser?.app_role?.globalAccess, currentUser, 'schoolId');
|
||||
|
||||
const courses = await db.courses.findOne(
|
||||
{ where },
|
||||
@ -247,11 +242,26 @@ module.exports = class CoursesDBApi {
|
||||
let offset = 0;
|
||||
let where = {};
|
||||
const currentPage = +filter.page;
|
||||
|
||||
|
||||
const user = (options && options.currentUser) || null;
|
||||
const userSchools = (user && user.schools?.id) || null;
|
||||
|
||||
|
||||
|
||||
if (userSchools) {
|
||||
if (options?.currentUser?.schoolsId) {
|
||||
where.schoolsId = options.currentUser.schoolsId;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
offset = currentPage * limit;
|
||||
|
||||
const orderBy = null;
|
||||
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
let include = [
|
||||
|
||||
{
|
||||
@ -359,7 +369,11 @@ module.exports = class CoursesDBApi {
|
||||
}
|
||||
|
||||
|
||||
applySchoolScope(where, globalAccess, user, 'schoolId');
|
||||
|
||||
if (globalAccess) {
|
||||
delete where.schoolsId;
|
||||
}
|
||||
|
||||
|
||||
const queryOptions = {
|
||||
where,
|
||||
@ -390,9 +404,14 @@ module.exports = class CoursesDBApi {
|
||||
}
|
||||
}
|
||||
|
||||
static async findAllAutocomplete(query, limit, offset, globalAccess, schoolId,) {
|
||||
static async findAllAutocomplete(query, limit, offset, globalAccess, organizationId,) {
|
||||
let where = {};
|
||||
|
||||
|
||||
if (!globalAccess && organizationId) {
|
||||
where.organizationId = organizationId;
|
||||
}
|
||||
|
||||
|
||||
if (query) {
|
||||
where = {
|
||||
@ -407,8 +426,6 @@ module.exports = class CoursesDBApi {
|
||||
};
|
||||
}
|
||||
|
||||
applySchoolScopeById(where, globalAccess, schoolId, 'schoolId');
|
||||
|
||||
const records = await db.courses.findAll({
|
||||
attributes: [ 'id', 'nome' ],
|
||||
where,
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
|
||||
const db = require('../models');
|
||||
const FileDBApi = require('./file');
|
||||
const crypto = require('crypto');
|
||||
const Utils = require('../utils');
|
||||
const { applySchoolScope, applySchoolScopeById, assertRecordInCurrentSchool, resolveSchoolIdForMutation } = require('./schoolScope');
|
||||
|
||||
|
||||
|
||||
@ -58,7 +59,7 @@ module.exports = class EmployeesDBApi {
|
||||
);
|
||||
|
||||
|
||||
await employees.setSchool(resolveSchoolIdForMutation(data.school, currentUser), {
|
||||
await employees.setSchool( data.school || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
@ -112,7 +113,6 @@ module.exports = class EmployeesDBApi {
|
||||
importHash: item.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
schoolId: resolveSchoolIdForMutation(item.school, currentUser),
|
||||
createdAt: new Date(Date.now() + index * 1000),
|
||||
}));
|
||||
|
||||
@ -128,8 +128,9 @@ module.exports = class EmployeesDBApi {
|
||||
static async update(id, data, options) {
|
||||
const currentUser = (options && options.currentUser) || {id: null};
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
const globalAccess = currentUser.app_role?.globalAccess;
|
||||
|
||||
const employees = await db.employees.findByPk(id, {}, {transaction});
|
||||
assertRecordInCurrentSchool(employees, currentUser, 'schoolId');
|
||||
|
||||
|
||||
|
||||
@ -163,7 +164,7 @@ module.exports = class EmployeesDBApi {
|
||||
if (data.school !== undefined) {
|
||||
await employees.setSchool(
|
||||
|
||||
resolveSchoolIdForMutation(data.school, currentUser),
|
||||
data.school,
|
||||
|
||||
{ transaction }
|
||||
);
|
||||
@ -191,8 +192,6 @@ module.exports = class EmployeesDBApi {
|
||||
transaction,
|
||||
});
|
||||
|
||||
employees.forEach((record) => assertRecordInCurrentSchool(record, currentUser, 'schoolId'));
|
||||
|
||||
await db.sequelize.transaction(async (transaction) => {
|
||||
for (const record of employees) {
|
||||
await record.update(
|
||||
@ -214,7 +213,6 @@ module.exports = class EmployeesDBApi {
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const employees = await db.employees.findByPk(id, options);
|
||||
assertRecordInCurrentSchool(employees, currentUser, 'schoolId');
|
||||
|
||||
await employees.update({
|
||||
deletedBy: currentUser.id
|
||||
@ -231,9 +229,6 @@ module.exports = class EmployeesDBApi {
|
||||
|
||||
static async findBy(where, options) {
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
const currentUser = (options && options.currentUser) || null;
|
||||
|
||||
applySchoolScope(where, currentUser?.app_role?.globalAccess, currentUser, 'schoolId');
|
||||
|
||||
const employees = await db.employees.findOne(
|
||||
{ where },
|
||||
@ -286,11 +281,26 @@ module.exports = class EmployeesDBApi {
|
||||
let offset = 0;
|
||||
let where = {};
|
||||
const currentPage = +filter.page;
|
||||
|
||||
|
||||
const user = (options && options.currentUser) || null;
|
||||
const userSchools = (user && user.schools?.id) || null;
|
||||
|
||||
|
||||
|
||||
if (userSchools) {
|
||||
if (options?.currentUser?.schoolsId) {
|
||||
where.schoolsId = options.currentUser.schoolsId;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
offset = currentPage * limit;
|
||||
|
||||
const orderBy = null;
|
||||
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
let include = [
|
||||
|
||||
{
|
||||
@ -457,7 +467,11 @@ module.exports = class EmployeesDBApi {
|
||||
}
|
||||
|
||||
|
||||
applySchoolScope(where, globalAccess, user, 'schoolId');
|
||||
|
||||
if (globalAccess) {
|
||||
delete where.schoolsId;
|
||||
}
|
||||
|
||||
|
||||
const queryOptions = {
|
||||
where,
|
||||
@ -488,9 +502,14 @@ module.exports = class EmployeesDBApi {
|
||||
}
|
||||
}
|
||||
|
||||
static async findAllAutocomplete(query, limit, offset, globalAccess, schoolId,) {
|
||||
static async findAllAutocomplete(query, limit, offset, globalAccess, organizationId,) {
|
||||
let where = {};
|
||||
|
||||
|
||||
if (!globalAccess && organizationId) {
|
||||
where.organizationId = organizationId;
|
||||
}
|
||||
|
||||
|
||||
if (query) {
|
||||
where = {
|
||||
@ -505,8 +524,6 @@ module.exports = class EmployeesDBApi {
|
||||
};
|
||||
}
|
||||
|
||||
applySchoolScopeById(where, globalAccess, schoolId, 'schoolId');
|
||||
|
||||
const records = await db.employees.findAll({
|
||||
attributes: [ 'id', 'nome' ],
|
||||
where,
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
|
||||
const db = require('../models');
|
||||
const FileDBApi = require('./file');
|
||||
const crypto = require('crypto');
|
||||
const Utils = require('../utils');
|
||||
const { applySchoolScope, applySchoolScopeById, assertRecordInCurrentSchool, assertRelatedRecordInCurrentSchool, resolveSchoolIdForMutation } = require('./schoolScope');
|
||||
|
||||
|
||||
|
||||
@ -43,16 +44,14 @@ module.exports = class EnrollmentsDBApi {
|
||||
);
|
||||
|
||||
|
||||
await enrollments.setSchool(resolveSchoolIdForMutation(data.school, currentUser), {
|
||||
await enrollments.setSchool( data.school || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
await assertRelatedRecordInCurrentSchool('students', data.student, currentUser, 'schoolId', { transaction });
|
||||
await enrollments.setStudent( data.student || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
await assertRelatedRecordInCurrentSchool('classes', data.class, currentUser, 'schoolId', { transaction });
|
||||
await enrollments.setClass( data.class || null, {
|
||||
transaction,
|
||||
});
|
||||
@ -92,7 +91,6 @@ module.exports = class EnrollmentsDBApi {
|
||||
importHash: item.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
schoolId: resolveSchoolIdForMutation(item.school, currentUser),
|
||||
createdAt: new Date(Date.now() + index * 1000),
|
||||
}));
|
||||
|
||||
@ -108,8 +106,9 @@ module.exports = class EnrollmentsDBApi {
|
||||
static async update(id, data, options) {
|
||||
const currentUser = (options && options.currentUser) || {id: null};
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
const globalAccess = currentUser.app_role?.globalAccess;
|
||||
|
||||
const enrollments = await db.enrollments.findByPk(id, {}, {transaction});
|
||||
assertRecordInCurrentSchool(enrollments, currentUser, 'schoolId');
|
||||
|
||||
|
||||
|
||||
@ -134,14 +133,13 @@ module.exports = class EnrollmentsDBApi {
|
||||
if (data.school !== undefined) {
|
||||
await enrollments.setSchool(
|
||||
|
||||
resolveSchoolIdForMutation(data.school, currentUser),
|
||||
data.school,
|
||||
|
||||
{ transaction }
|
||||
);
|
||||
}
|
||||
|
||||
if (data.student !== undefined) {
|
||||
await assertRelatedRecordInCurrentSchool('students', data.student, currentUser, 'schoolId', { transaction });
|
||||
await enrollments.setStudent(
|
||||
|
||||
data.student,
|
||||
@ -151,7 +149,6 @@ module.exports = class EnrollmentsDBApi {
|
||||
}
|
||||
|
||||
if (data.class !== undefined) {
|
||||
await assertRelatedRecordInCurrentSchool('classes', data.class, currentUser, 'schoolId', { transaction });
|
||||
await enrollments.setClass(
|
||||
|
||||
data.class,
|
||||
@ -182,8 +179,6 @@ module.exports = class EnrollmentsDBApi {
|
||||
transaction,
|
||||
});
|
||||
|
||||
enrollments.forEach((record) => assertRecordInCurrentSchool(record, currentUser, 'schoolId'));
|
||||
|
||||
await db.sequelize.transaction(async (transaction) => {
|
||||
for (const record of enrollments) {
|
||||
await record.update(
|
||||
@ -205,7 +200,6 @@ module.exports = class EnrollmentsDBApi {
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const enrollments = await db.enrollments.findByPk(id, options);
|
||||
assertRecordInCurrentSchool(enrollments, currentUser, 'schoolId');
|
||||
|
||||
await enrollments.update({
|
||||
deletedBy: currentUser.id
|
||||
@ -222,9 +216,6 @@ module.exports = class EnrollmentsDBApi {
|
||||
|
||||
static async findBy(where, options) {
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
const currentUser = (options && options.currentUser) || null;
|
||||
|
||||
applySchoolScope(where, currentUser?.app_role?.globalAccess, currentUser, 'schoolId');
|
||||
|
||||
const enrollments = await db.enrollments.findOne(
|
||||
{ where },
|
||||
@ -287,11 +278,26 @@ module.exports = class EnrollmentsDBApi {
|
||||
let offset = 0;
|
||||
let where = {};
|
||||
const currentPage = +filter.page;
|
||||
|
||||
|
||||
const user = (options && options.currentUser) || null;
|
||||
const userSchools = (user && user.schools?.id) || null;
|
||||
|
||||
|
||||
|
||||
if (userSchools) {
|
||||
if (options?.currentUser?.schoolsId) {
|
||||
where.schoolsId = options.currentUser.schoolsId;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
offset = currentPage * limit;
|
||||
|
||||
const orderBy = null;
|
||||
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
let include = [
|
||||
|
||||
{
|
||||
@ -450,7 +456,11 @@ module.exports = class EnrollmentsDBApi {
|
||||
}
|
||||
|
||||
|
||||
applySchoolScope(where, globalAccess, user, 'schoolId');
|
||||
|
||||
if (globalAccess) {
|
||||
delete where.schoolsId;
|
||||
}
|
||||
|
||||
|
||||
const queryOptions = {
|
||||
where,
|
||||
@ -481,9 +491,14 @@ module.exports = class EnrollmentsDBApi {
|
||||
}
|
||||
}
|
||||
|
||||
static async findAllAutocomplete(query, limit, offset, globalAccess, schoolId,) {
|
||||
static async findAllAutocomplete(query, limit, offset, globalAccess, organizationId,) {
|
||||
let where = {};
|
||||
|
||||
|
||||
if (!globalAccess && organizationId) {
|
||||
where.organizationId = organizationId;
|
||||
}
|
||||
|
||||
|
||||
if (query) {
|
||||
where = {
|
||||
@ -498,8 +513,6 @@ module.exports = class EnrollmentsDBApi {
|
||||
};
|
||||
}
|
||||
|
||||
applySchoolScopeById(where, globalAccess, schoolId, 'schoolId');
|
||||
|
||||
const records = await db.enrollments.findAll({
|
||||
attributes: [ 'id', 'ano_lectivo' ],
|
||||
where,
|
||||
|
||||
@ -36,17 +36,15 @@ module.exports = class FileDBApi {
|
||||
);
|
||||
|
||||
for (const file of inexistentFiles) {
|
||||
const safeFile = await services.validateFileMetadata(file, relation, { transaction });
|
||||
|
||||
await db.file.create(
|
||||
{
|
||||
belongsTo: relation.belongsTo,
|
||||
belongsToColumn: relation.belongsToColumn,
|
||||
belongsToId: relation.belongsToId,
|
||||
name: safeFile.name,
|
||||
sizeInBytes: safeFile.sizeInBytes,
|
||||
privateUrl: safeFile.privateUrl,
|
||||
publicUrl: safeFile.publicUrl,
|
||||
name: file.name,
|
||||
sizeInBytes: file.sizeInBytes,
|
||||
privateUrl: file.privateUrl,
|
||||
publicUrl: file.publicUrl,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
},
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
|
||||
const db = require('../models');
|
||||
const FileDBApi = require('./file');
|
||||
const crypto = require('crypto');
|
||||
const Utils = require('../utils');
|
||||
const { applySchoolScope, applySchoolScopeById, assertRecordInCurrentSchool, resolveSchoolIdForMutation } = require('./schoolScope');
|
||||
|
||||
|
||||
|
||||
@ -43,7 +44,7 @@ module.exports = class GradesDBApi {
|
||||
);
|
||||
|
||||
|
||||
await grades.setSchool(resolveSchoolIdForMutation(data.school, currentUser), {
|
||||
await grades.setSchool( data.school || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
@ -82,7 +83,6 @@ module.exports = class GradesDBApi {
|
||||
importHash: item.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
schoolId: resolveSchoolIdForMutation(item.school, currentUser),
|
||||
createdAt: new Date(Date.now() + index * 1000),
|
||||
}));
|
||||
|
||||
@ -98,8 +98,9 @@ module.exports = class GradesDBApi {
|
||||
static async update(id, data, options) {
|
||||
const currentUser = (options && options.currentUser) || {id: null};
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
const globalAccess = currentUser.app_role?.globalAccess;
|
||||
|
||||
const grades = await db.grades.findByPk(id, {}, {transaction});
|
||||
assertRecordInCurrentSchool(grades, currentUser, 'schoolId');
|
||||
|
||||
|
||||
|
||||
@ -124,7 +125,7 @@ module.exports = class GradesDBApi {
|
||||
if (data.school !== undefined) {
|
||||
await grades.setSchool(
|
||||
|
||||
resolveSchoolIdForMutation(data.school, currentUser),
|
||||
data.school,
|
||||
|
||||
{ transaction }
|
||||
);
|
||||
@ -152,8 +153,6 @@ module.exports = class GradesDBApi {
|
||||
transaction,
|
||||
});
|
||||
|
||||
grades.forEach((record) => assertRecordInCurrentSchool(record, currentUser, 'schoolId'));
|
||||
|
||||
await db.sequelize.transaction(async (transaction) => {
|
||||
for (const record of grades) {
|
||||
await record.update(
|
||||
@ -175,7 +174,6 @@ module.exports = class GradesDBApi {
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const grades = await db.grades.findByPk(id, options);
|
||||
assertRecordInCurrentSchool(grades, currentUser, 'schoolId');
|
||||
|
||||
await grades.update({
|
||||
deletedBy: currentUser.id
|
||||
@ -192,9 +190,6 @@ module.exports = class GradesDBApi {
|
||||
|
||||
static async findBy(where, options) {
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
const currentUser = (options && options.currentUser) || null;
|
||||
|
||||
applySchoolScope(where, currentUser?.app_role?.globalAccess, currentUser, 'schoolId');
|
||||
|
||||
const grades = await db.grades.findOne(
|
||||
{ where },
|
||||
@ -251,11 +246,26 @@ module.exports = class GradesDBApi {
|
||||
let offset = 0;
|
||||
let where = {};
|
||||
const currentPage = +filter.page;
|
||||
|
||||
|
||||
const user = (options && options.currentUser) || null;
|
||||
const userSchools = (user && user.schools?.id) || null;
|
||||
|
||||
|
||||
|
||||
if (userSchools) {
|
||||
if (options?.currentUser?.schoolsId) {
|
||||
where.schoolsId = options.currentUser.schoolsId;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
offset = currentPage * limit;
|
||||
|
||||
const orderBy = null;
|
||||
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
let include = [
|
||||
|
||||
{
|
||||
@ -376,7 +386,11 @@ module.exports = class GradesDBApi {
|
||||
}
|
||||
|
||||
|
||||
applySchoolScope(where, globalAccess, user, 'schoolId');
|
||||
|
||||
if (globalAccess) {
|
||||
delete where.schoolsId;
|
||||
}
|
||||
|
||||
|
||||
const queryOptions = {
|
||||
where,
|
||||
@ -407,9 +421,14 @@ module.exports = class GradesDBApi {
|
||||
}
|
||||
}
|
||||
|
||||
static async findAllAutocomplete(query, limit, offset, globalAccess, schoolId,) {
|
||||
static async findAllAutocomplete(query, limit, offset, globalAccess, organizationId,) {
|
||||
let where = {};
|
||||
|
||||
|
||||
if (!globalAccess && organizationId) {
|
||||
where.organizationId = organizationId;
|
||||
}
|
||||
|
||||
|
||||
if (query) {
|
||||
where = {
|
||||
@ -424,8 +443,6 @@ module.exports = class GradesDBApi {
|
||||
};
|
||||
}
|
||||
|
||||
applySchoolScopeById(where, globalAccess, schoolId, 'schoolId');
|
||||
|
||||
const records = await db.grades.findAll({
|
||||
attributes: [ 'id', 'nome' ],
|
||||
where,
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
|
||||
const db = require('../models');
|
||||
const FileDBApi = require('./file');
|
||||
const crypto = require('crypto');
|
||||
const Utils = require('../utils');
|
||||
const { applySchoolScope, applySchoolScopeById, assertRecordInCurrentSchool, resolveSchoolIdForMutation } = require('./schoolScope');
|
||||
|
||||
|
||||
|
||||
@ -48,7 +49,7 @@ module.exports = class GuardiansDBApi {
|
||||
);
|
||||
|
||||
|
||||
await guardians.setSchool(resolveSchoolIdForMutation(data.school, currentUser), {
|
||||
await guardians.setSchool( data.school || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
@ -92,7 +93,6 @@ module.exports = class GuardiansDBApi {
|
||||
importHash: item.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
schoolId: resolveSchoolIdForMutation(item.school, currentUser),
|
||||
createdAt: new Date(Date.now() + index * 1000),
|
||||
}));
|
||||
|
||||
@ -108,8 +108,9 @@ module.exports = class GuardiansDBApi {
|
||||
static async update(id, data, options) {
|
||||
const currentUser = (options && options.currentUser) || {id: null};
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
const globalAccess = currentUser.app_role?.globalAccess;
|
||||
|
||||
const guardians = await db.guardians.findByPk(id, {}, {transaction});
|
||||
assertRecordInCurrentSchool(guardians, currentUser, 'schoolId');
|
||||
|
||||
|
||||
|
||||
@ -137,7 +138,7 @@ module.exports = class GuardiansDBApi {
|
||||
if (data.school !== undefined) {
|
||||
await guardians.setSchool(
|
||||
|
||||
resolveSchoolIdForMutation(data.school, currentUser),
|
||||
data.school,
|
||||
|
||||
{ transaction }
|
||||
);
|
||||
@ -165,8 +166,6 @@ module.exports = class GuardiansDBApi {
|
||||
transaction,
|
||||
});
|
||||
|
||||
guardians.forEach((record) => assertRecordInCurrentSchool(record, currentUser, 'schoolId'));
|
||||
|
||||
await db.sequelize.transaction(async (transaction) => {
|
||||
for (const record of guardians) {
|
||||
await record.update(
|
||||
@ -188,7 +187,6 @@ module.exports = class GuardiansDBApi {
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const guardians = await db.guardians.findByPk(id, options);
|
||||
assertRecordInCurrentSchool(guardians, currentUser, 'schoolId');
|
||||
|
||||
await guardians.update({
|
||||
deletedBy: currentUser.id
|
||||
@ -205,9 +203,6 @@ module.exports = class GuardiansDBApi {
|
||||
|
||||
static async findBy(where, options) {
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
const currentUser = (options && options.currentUser) || null;
|
||||
|
||||
applySchoolScope(where, currentUser?.app_role?.globalAccess, currentUser, 'schoolId');
|
||||
|
||||
const guardians = await db.guardians.findOne(
|
||||
{ where },
|
||||
@ -264,11 +259,26 @@ module.exports = class GuardiansDBApi {
|
||||
let offset = 0;
|
||||
let where = {};
|
||||
const currentPage = +filter.page;
|
||||
|
||||
|
||||
const user = (options && options.currentUser) || null;
|
||||
const userSchools = (user && user.schools?.id) || null;
|
||||
|
||||
|
||||
|
||||
if (userSchools) {
|
||||
if (options?.currentUser?.schoolsId) {
|
||||
where.schoolsId = options.currentUser.schoolsId;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
offset = currentPage * limit;
|
||||
|
||||
const orderBy = null;
|
||||
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
let include = [
|
||||
|
||||
{
|
||||
@ -387,7 +397,11 @@ module.exports = class GuardiansDBApi {
|
||||
}
|
||||
|
||||
|
||||
applySchoolScope(where, globalAccess, user, 'schoolId');
|
||||
|
||||
if (globalAccess) {
|
||||
delete where.schoolsId;
|
||||
}
|
||||
|
||||
|
||||
const queryOptions = {
|
||||
where,
|
||||
@ -418,9 +432,14 @@ module.exports = class GuardiansDBApi {
|
||||
}
|
||||
}
|
||||
|
||||
static async findAllAutocomplete(query, limit, offset, globalAccess, schoolId,) {
|
||||
static async findAllAutocomplete(query, limit, offset, globalAccess, organizationId,) {
|
||||
let where = {};
|
||||
|
||||
|
||||
if (!globalAccess && organizationId) {
|
||||
where.organizationId = organizationId;
|
||||
}
|
||||
|
||||
|
||||
if (query) {
|
||||
where = {
|
||||
@ -435,8 +454,6 @@ module.exports = class GuardiansDBApi {
|
||||
};
|
||||
}
|
||||
|
||||
applySchoolScopeById(where, globalAccess, schoolId, 'schoolId');
|
||||
|
||||
const records = await db.guardians.findAll({
|
||||
attributes: [ 'id', 'nome' ],
|
||||
where,
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
|
||||
const db = require('../models');
|
||||
const FileDBApi = require('./file');
|
||||
const crypto = require('crypto');
|
||||
const Utils = require('../utils');
|
||||
const { applySchoolScope, applySchoolScopeById, assertRecordInCurrentSchool, assertRelatedRecordInCurrentSchool, resolveSchoolIdForMutation } = require('./schoolScope');
|
||||
|
||||
|
||||
|
||||
@ -58,11 +59,10 @@ module.exports = class InvoicesDBApi {
|
||||
);
|
||||
|
||||
|
||||
await invoices.setSchool(resolveSchoolIdForMutation(data.school, currentUser), {
|
||||
await invoices.setSchool( data.school || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
await assertRelatedRecordInCurrentSchool('students', data.student, currentUser, 'schoolId', { transaction });
|
||||
await invoices.setStudent( data.student || null, {
|
||||
transaction,
|
||||
});
|
||||
@ -117,7 +117,6 @@ module.exports = class InvoicesDBApi {
|
||||
importHash: item.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
schoolId: resolveSchoolIdForMutation(item.school, currentUser),
|
||||
createdAt: new Date(Date.now() + index * 1000),
|
||||
}));
|
||||
|
||||
@ -133,8 +132,9 @@ module.exports = class InvoicesDBApi {
|
||||
static async update(id, data, options) {
|
||||
const currentUser = (options && options.currentUser) || {id: null};
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
const globalAccess = currentUser.app_role?.globalAccess;
|
||||
|
||||
const invoices = await db.invoices.findByPk(id, {}, {transaction});
|
||||
assertRecordInCurrentSchool(invoices, currentUser, 'schoolId');
|
||||
|
||||
|
||||
|
||||
@ -168,14 +168,13 @@ module.exports = class InvoicesDBApi {
|
||||
if (data.school !== undefined) {
|
||||
await invoices.setSchool(
|
||||
|
||||
resolveSchoolIdForMutation(data.school, currentUser),
|
||||
data.school,
|
||||
|
||||
{ transaction }
|
||||
);
|
||||
}
|
||||
|
||||
if (data.student !== undefined) {
|
||||
await assertRelatedRecordInCurrentSchool('students', data.student, currentUser, 'schoolId', { transaction });
|
||||
await invoices.setStudent(
|
||||
|
||||
data.student,
|
||||
@ -206,8 +205,6 @@ module.exports = class InvoicesDBApi {
|
||||
transaction,
|
||||
});
|
||||
|
||||
invoices.forEach((record) => assertRecordInCurrentSchool(record, currentUser, 'schoolId'));
|
||||
|
||||
await db.sequelize.transaction(async (transaction) => {
|
||||
for (const record of invoices) {
|
||||
await record.update(
|
||||
@ -229,7 +226,6 @@ module.exports = class InvoicesDBApi {
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const invoices = await db.invoices.findByPk(id, options);
|
||||
assertRecordInCurrentSchool(invoices, currentUser, 'schoolId');
|
||||
|
||||
await invoices.update({
|
||||
deletedBy: currentUser.id
|
||||
@ -246,9 +242,6 @@ module.exports = class InvoicesDBApi {
|
||||
|
||||
static async findBy(where, options) {
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
const currentUser = (options && options.currentUser) || null;
|
||||
|
||||
applySchoolScope(where, currentUser?.app_role?.globalAccess, currentUser, 'schoolId');
|
||||
|
||||
const invoices = await db.invoices.findOne(
|
||||
{ where },
|
||||
@ -310,11 +303,26 @@ module.exports = class InvoicesDBApi {
|
||||
let offset = 0;
|
||||
let where = {};
|
||||
const currentPage = +filter.page;
|
||||
|
||||
|
||||
const user = (options && options.currentUser) || null;
|
||||
const userSchools = (user && user.schools?.id) || null;
|
||||
|
||||
|
||||
|
||||
if (userSchools) {
|
||||
if (options?.currentUser?.schoolsId) {
|
||||
where.schoolsId = options.currentUser.schoolsId;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
offset = currentPage * limit;
|
||||
|
||||
const orderBy = null;
|
||||
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
let include = [
|
||||
|
||||
{
|
||||
@ -513,7 +521,11 @@ module.exports = class InvoicesDBApi {
|
||||
}
|
||||
|
||||
|
||||
applySchoolScope(where, globalAccess, user, 'schoolId');
|
||||
|
||||
if (globalAccess) {
|
||||
delete where.schoolsId;
|
||||
}
|
||||
|
||||
|
||||
const queryOptions = {
|
||||
where,
|
||||
@ -544,9 +556,14 @@ module.exports = class InvoicesDBApi {
|
||||
}
|
||||
}
|
||||
|
||||
static async findAllAutocomplete(query, limit, offset, globalAccess, schoolId,) {
|
||||
static async findAllAutocomplete(query, limit, offset, globalAccess, organizationId,) {
|
||||
let where = {};
|
||||
|
||||
|
||||
if (!globalAccess && organizationId) {
|
||||
where.organizationId = organizationId;
|
||||
}
|
||||
|
||||
|
||||
if (query) {
|
||||
where = {
|
||||
@ -561,8 +578,6 @@ module.exports = class InvoicesDBApi {
|
||||
};
|
||||
}
|
||||
|
||||
applySchoolScopeById(where, globalAccess, schoolId, 'schoolId');
|
||||
|
||||
const records = await db.invoices.findAll({
|
||||
attributes: [ 'id', 'referencia' ],
|
||||
where,
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
|
||||
const db = require('../models');
|
||||
const FileDBApi = require('./file');
|
||||
const crypto = require('crypto');
|
||||
const Utils = require('../utils');
|
||||
const { applySchoolScope, applySchoolScopeById, assertRecordInCurrentSchool, assertRelatedRecordInCurrentSchool, resolveSchoolIdForMutation } = require('./schoolScope');
|
||||
|
||||
|
||||
|
||||
@ -49,11 +49,10 @@ module.exports = class PaymentsDBApi {
|
||||
);
|
||||
|
||||
|
||||
await payments.setSchool(resolveSchoolIdForMutation(data.school, currentUser), {
|
||||
await payments.setSchool( data.school || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
await assertRelatedRecordInCurrentSchool('invoices', data.invoice, currentUser, 'schoolId', { transaction });
|
||||
await payments.setInvoice( data.invoice || null, {
|
||||
transaction,
|
||||
});
|
||||
@ -108,7 +107,6 @@ module.exports = class PaymentsDBApi {
|
||||
importHash: item.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
schoolId: resolveSchoolIdForMutation(item.school, currentUser),
|
||||
createdAt: new Date(Date.now() + index * 1000),
|
||||
}));
|
||||
|
||||
@ -136,8 +134,9 @@ module.exports = class PaymentsDBApi {
|
||||
static async update(id, data, options) {
|
||||
const currentUser = (options && options.currentUser) || {id: null};
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
const globalAccess = currentUser.app_role?.globalAccess;
|
||||
|
||||
const payments = await db.payments.findByPk(id, {}, {transaction});
|
||||
assertRecordInCurrentSchool(payments, currentUser, 'schoolId');
|
||||
|
||||
|
||||
|
||||
@ -165,14 +164,13 @@ module.exports = class PaymentsDBApi {
|
||||
if (data.school !== undefined) {
|
||||
await payments.setSchool(
|
||||
|
||||
resolveSchoolIdForMutation(data.school, currentUser),
|
||||
data.school,
|
||||
|
||||
{ transaction }
|
||||
);
|
||||
}
|
||||
|
||||
if (data.invoice !== undefined) {
|
||||
await assertRelatedRecordInCurrentSchool('invoices', data.invoice, currentUser, 'schoolId', { transaction });
|
||||
await payments.setInvoice(
|
||||
|
||||
data.invoice,
|
||||
@ -186,17 +184,15 @@ module.exports = class PaymentsDBApi {
|
||||
|
||||
|
||||
|
||||
if (data.comprovativo !== undefined) {
|
||||
await FileDBApi.replaceRelationFiles(
|
||||
{
|
||||
belongsTo: db.payments.getTableName(),
|
||||
belongsToColumn: 'comprovativo',
|
||||
belongsToId: payments.id,
|
||||
},
|
||||
data.comprovativo,
|
||||
options,
|
||||
);
|
||||
}
|
||||
await FileDBApi.replaceRelationFiles(
|
||||
{
|
||||
belongsTo: db.payments.getTableName(),
|
||||
belongsToColumn: 'comprovativo',
|
||||
belongsToId: payments.id,
|
||||
},
|
||||
data.comprovativo,
|
||||
options,
|
||||
);
|
||||
|
||||
|
||||
return payments;
|
||||
@ -215,8 +211,6 @@ module.exports = class PaymentsDBApi {
|
||||
transaction,
|
||||
});
|
||||
|
||||
payments.forEach((record) => assertRecordInCurrentSchool(record, currentUser, 'schoolId'));
|
||||
|
||||
await db.sequelize.transaction(async (transaction) => {
|
||||
for (const record of payments) {
|
||||
await record.update(
|
||||
@ -238,7 +232,6 @@ module.exports = class PaymentsDBApi {
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const payments = await db.payments.findByPk(id, options);
|
||||
assertRecordInCurrentSchool(payments, currentUser, 'schoolId');
|
||||
|
||||
await payments.update({
|
||||
deletedBy: currentUser.id
|
||||
@ -255,9 +248,6 @@ module.exports = class PaymentsDBApi {
|
||||
|
||||
static async findBy(where, options) {
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
const currentUser = (options && options.currentUser) || null;
|
||||
|
||||
applySchoolScope(where, currentUser?.app_role?.globalAccess, currentUser, 'schoolId');
|
||||
|
||||
const payments = await db.payments.findOne(
|
||||
{ where },
|
||||
@ -320,11 +310,26 @@ module.exports = class PaymentsDBApi {
|
||||
let offset = 0;
|
||||
let where = {};
|
||||
const currentPage = +filter.page;
|
||||
|
||||
|
||||
const user = (options && options.currentUser) || null;
|
||||
const userSchools = (user && user.schools?.id) || null;
|
||||
|
||||
|
||||
|
||||
if (userSchools) {
|
||||
if (options?.currentUser?.schoolsId) {
|
||||
where.schoolsId = options.currentUser.schoolsId;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
offset = currentPage * limit;
|
||||
|
||||
const orderBy = null;
|
||||
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
let include = [
|
||||
|
||||
{
|
||||
@ -493,7 +498,11 @@ module.exports = class PaymentsDBApi {
|
||||
}
|
||||
|
||||
|
||||
applySchoolScope(where, globalAccess, user, 'schoolId');
|
||||
|
||||
if (globalAccess) {
|
||||
delete where.schoolsId;
|
||||
}
|
||||
|
||||
|
||||
const queryOptions = {
|
||||
where,
|
||||
@ -524,9 +533,14 @@ module.exports = class PaymentsDBApi {
|
||||
}
|
||||
}
|
||||
|
||||
static async findAllAutocomplete(query, limit, offset, globalAccess, schoolId,) {
|
||||
static async findAllAutocomplete(query, limit, offset, globalAccess, organizationId,) {
|
||||
let where = {};
|
||||
|
||||
|
||||
if (!globalAccess && organizationId) {
|
||||
where.organizationId = organizationId;
|
||||
}
|
||||
|
||||
|
||||
if (query) {
|
||||
where = {
|
||||
@ -541,8 +555,6 @@ module.exports = class PaymentsDBApi {
|
||||
};
|
||||
}
|
||||
|
||||
applySchoolScopeById(where, globalAccess, schoolId, 'schoolId');
|
||||
|
||||
const records = await db.payments.findAll({
|
||||
attributes: [ 'id', 'referencia_transacao' ],
|
||||
where,
|
||||
|
||||
@ -1,20 +1,14 @@
|
||||
|
||||
const db = require('../models');
|
||||
const FileDBApi = require('./file');
|
||||
const crypto = require('crypto');
|
||||
const Utils = require('../utils');
|
||||
const ForbiddenError = require('../../services/notifications/errors/forbidden');
|
||||
|
||||
|
||||
|
||||
const Sequelize = db.Sequelize;
|
||||
const Op = Sequelize.Op;
|
||||
|
||||
|
||||
function assertGlobalAccess(currentUser) {
|
||||
if (!currentUser?.app_role?.globalAccess) {
|
||||
throw new ForbiddenError('auth.forbidden');
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = class PermissionsDBApi {
|
||||
|
||||
|
||||
@ -22,7 +16,6 @@ module.exports = class PermissionsDBApi {
|
||||
static async create(data, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
assertGlobalAccess(currentUser);
|
||||
|
||||
const permissions = await db.permissions.create(
|
||||
{
|
||||
@ -53,7 +46,6 @@ module.exports = class PermissionsDBApi {
|
||||
static async bulkImport(data, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
assertGlobalAccess(currentUser);
|
||||
|
||||
// Prepare data - wrapping individual data transformations in a map() method
|
||||
const permissionsData = data.map((item, index) => ({
|
||||
@ -82,7 +74,7 @@ module.exports = class PermissionsDBApi {
|
||||
static async update(id, data, options) {
|
||||
const currentUser = (options && options.currentUser) || {id: null};
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
assertGlobalAccess(currentUser);
|
||||
const globalAccess = currentUser.app_role?.globalAccess;
|
||||
|
||||
const permissions = await db.permissions.findByPk(id, {}, {transaction});
|
||||
|
||||
@ -112,7 +104,6 @@ module.exports = class PermissionsDBApi {
|
||||
static async deleteByIds(ids, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
assertGlobalAccess(currentUser);
|
||||
|
||||
const permissions = await db.permissions.findAll({
|
||||
where: {
|
||||
@ -142,7 +133,6 @@ module.exports = class PermissionsDBApi {
|
||||
static async remove(id, options) {
|
||||
const currentUser = (options && options.currentUser) || {id: null};
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
assertGlobalAccess(currentUser);
|
||||
|
||||
const permissions = await db.permissions.findByPk(id, options);
|
||||
|
||||
@ -210,12 +200,18 @@ module.exports = class PermissionsDBApi {
|
||||
const currentPage = +filter.page;
|
||||
|
||||
|
||||
const user = (options && options.currentUser) || null;
|
||||
const userSchools = (user && user.schools?.id) || null;
|
||||
|
||||
|
||||
|
||||
|
||||
offset = currentPage * limit;
|
||||
|
||||
const orderBy = null;
|
||||
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
let include = [
|
||||
|
||||
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
|
||||
const db = require('../models');
|
||||
const FileDBApi = require('./file');
|
||||
const crypto = require('crypto');
|
||||
const Utils = require('../utils');
|
||||
const { applySchoolScope, applySchoolScopeById, assertRecordInCurrentSchool, resolveSchoolIdForMutation } = require('./schoolScope');
|
||||
|
||||
|
||||
|
||||
@ -53,7 +54,7 @@ module.exports = class ProductsDBApi {
|
||||
);
|
||||
|
||||
|
||||
await products.setSchool(resolveSchoolIdForMutation(data.school, currentUser), {
|
||||
await products.setSchool( data.school || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
@ -102,7 +103,6 @@ module.exports = class ProductsDBApi {
|
||||
importHash: item.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
schoolId: resolveSchoolIdForMutation(item.school, currentUser),
|
||||
createdAt: new Date(Date.now() + index * 1000),
|
||||
}));
|
||||
|
||||
@ -118,8 +118,9 @@ module.exports = class ProductsDBApi {
|
||||
static async update(id, data, options) {
|
||||
const currentUser = (options && options.currentUser) || {id: null};
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
const globalAccess = currentUser.app_role?.globalAccess;
|
||||
|
||||
const products = await db.products.findByPk(id, {}, {transaction});
|
||||
assertRecordInCurrentSchool(products, currentUser, 'schoolId');
|
||||
|
||||
|
||||
|
||||
@ -150,7 +151,7 @@ module.exports = class ProductsDBApi {
|
||||
if (data.school !== undefined) {
|
||||
await products.setSchool(
|
||||
|
||||
resolveSchoolIdForMutation(data.school, currentUser),
|
||||
data.school,
|
||||
|
||||
{ transaction }
|
||||
);
|
||||
@ -178,8 +179,6 @@ module.exports = class ProductsDBApi {
|
||||
transaction,
|
||||
});
|
||||
|
||||
products.forEach((record) => assertRecordInCurrentSchool(record, currentUser, 'schoolId'));
|
||||
|
||||
await db.sequelize.transaction(async (transaction) => {
|
||||
for (const record of products) {
|
||||
await record.update(
|
||||
@ -201,7 +200,6 @@ module.exports = class ProductsDBApi {
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const products = await db.products.findByPk(id, options);
|
||||
assertRecordInCurrentSchool(products, currentUser, 'schoolId');
|
||||
|
||||
await products.update({
|
||||
deletedBy: currentUser.id
|
||||
@ -218,9 +216,6 @@ module.exports = class ProductsDBApi {
|
||||
|
||||
static async findBy(where, options) {
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
const currentUser = (options && options.currentUser) || null;
|
||||
|
||||
applySchoolScope(where, currentUser?.app_role?.globalAccess, currentUser, 'schoolId');
|
||||
|
||||
const products = await db.products.findOne(
|
||||
{ where },
|
||||
@ -273,11 +268,26 @@ module.exports = class ProductsDBApi {
|
||||
let offset = 0;
|
||||
let where = {};
|
||||
const currentPage = +filter.page;
|
||||
|
||||
|
||||
const user = (options && options.currentUser) || null;
|
||||
const userSchools = (user && user.schools?.id) || null;
|
||||
|
||||
|
||||
|
||||
if (userSchools) {
|
||||
if (options?.currentUser?.schoolsId) {
|
||||
where.schoolsId = options.currentUser.schoolsId;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
offset = currentPage * limit;
|
||||
|
||||
const orderBy = null;
|
||||
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
let include = [
|
||||
|
||||
{
|
||||
@ -446,7 +456,11 @@ module.exports = class ProductsDBApi {
|
||||
}
|
||||
|
||||
|
||||
applySchoolScope(where, globalAccess, user, 'schoolId');
|
||||
|
||||
if (globalAccess) {
|
||||
delete where.schoolsId;
|
||||
}
|
||||
|
||||
|
||||
const queryOptions = {
|
||||
where,
|
||||
@ -477,9 +491,14 @@ module.exports = class ProductsDBApi {
|
||||
}
|
||||
}
|
||||
|
||||
static async findAllAutocomplete(query, limit, offset, globalAccess, schoolId,) {
|
||||
static async findAllAutocomplete(query, limit, offset, globalAccess, organizationId,) {
|
||||
let where = {};
|
||||
|
||||
|
||||
if (!globalAccess && organizationId) {
|
||||
where.organizationId = organizationId;
|
||||
}
|
||||
|
||||
|
||||
if (query) {
|
||||
where = {
|
||||
@ -494,8 +513,6 @@ module.exports = class ProductsDBApi {
|
||||
};
|
||||
}
|
||||
|
||||
applySchoolScopeById(where, globalAccess, schoolId, 'schoolId');
|
||||
|
||||
const records = await db.products.findAll({
|
||||
attributes: [ 'id', 'nome' ],
|
||||
where,
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
|
||||
const db = require('../models');
|
||||
const FileDBApi = require('./file');
|
||||
const crypto = require('crypto');
|
||||
const Utils = require('../utils');
|
||||
const ForbiddenError = require('../../services/notifications/errors/forbidden');
|
||||
|
||||
|
||||
const config = require('../../config');
|
||||
@ -10,13 +11,6 @@ const config = require('../../config');
|
||||
const Sequelize = db.Sequelize;
|
||||
const Op = Sequelize.Op;
|
||||
|
||||
|
||||
function assertGlobalAccess(currentUser) {
|
||||
if (!currentUser?.app_role?.globalAccess) {
|
||||
throw new ForbiddenError('auth.forbidden');
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = class RolesDBApi {
|
||||
|
||||
|
||||
@ -24,7 +18,6 @@ module.exports = class RolesDBApi {
|
||||
static async create(data, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
assertGlobalAccess(currentUser);
|
||||
|
||||
const roles = await db.roles.create(
|
||||
{
|
||||
@ -70,7 +63,6 @@ module.exports = class RolesDBApi {
|
||||
static async bulkImport(data, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
assertGlobalAccess(currentUser);
|
||||
|
||||
// Prepare data - wrapping individual data transformations in a map() method
|
||||
const rolesData = data.map((item, index) => ({
|
||||
@ -110,7 +102,7 @@ module.exports = class RolesDBApi {
|
||||
static async update(id, data, options) {
|
||||
const currentUser = (options && options.currentUser) || {id: null};
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
assertGlobalAccess(currentUser);
|
||||
const globalAccess = currentUser.app_role?.globalAccess;
|
||||
|
||||
const roles = await db.roles.findByPk(id, {}, {transaction});
|
||||
|
||||
@ -150,7 +142,6 @@ module.exports = class RolesDBApi {
|
||||
static async deleteByIds(ids, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
assertGlobalAccess(currentUser);
|
||||
|
||||
const roles = await db.roles.findAll({
|
||||
where: {
|
||||
@ -180,7 +171,6 @@ module.exports = class RolesDBApi {
|
||||
static async remove(id, options) {
|
||||
const currentUser = (options && options.currentUser) || {id: null};
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
assertGlobalAccess(currentUser);
|
||||
|
||||
const roles = await db.roles.findByPk(id, options);
|
||||
|
||||
@ -257,12 +247,18 @@ module.exports = class RolesDBApi {
|
||||
const currentPage = +filter.page;
|
||||
|
||||
|
||||
const user = (options && options.currentUser) || null;
|
||||
const userSchools = (user && user.schools?.id) || null;
|
||||
|
||||
|
||||
|
||||
|
||||
offset = currentPage * limit;
|
||||
|
||||
const orderBy = null;
|
||||
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
let include = [
|
||||
|
||||
|
||||
|
||||
@ -1,103 +0,0 @@
|
||||
const ForbiddenError = require('../../services/notifications/errors/forbidden');
|
||||
const db = require('../models');
|
||||
|
||||
function getCurrentUserSchoolId(currentUser) {
|
||||
return currentUser?.schoolsId || currentUser?.schools?.id || null;
|
||||
}
|
||||
|
||||
function applySchoolScopeById(where, globalAccess, schoolId, field = 'schoolId') {
|
||||
if (globalAccess) {
|
||||
return where;
|
||||
}
|
||||
|
||||
if (!schoolId) {
|
||||
where.id = null;
|
||||
return where;
|
||||
}
|
||||
|
||||
if (field === 'id' && where.id && String(where.id) !== String(schoolId)) {
|
||||
where.id = null;
|
||||
return where;
|
||||
}
|
||||
|
||||
where[field] = schoolId;
|
||||
return where;
|
||||
}
|
||||
|
||||
function applySchoolScope(where, globalAccess, currentUser, field = 'schoolId') {
|
||||
if (!currentUser || globalAccess) {
|
||||
return where;
|
||||
}
|
||||
|
||||
return applySchoolScopeById(where, false, getCurrentUserSchoolId(currentUser), field);
|
||||
}
|
||||
|
||||
|
||||
function resolveSchoolIdForMutation(inputSchoolId, currentUser) {
|
||||
if (!currentUser || currentUser?.app_role?.globalAccess) {
|
||||
return inputSchoolId || null;
|
||||
}
|
||||
|
||||
const schoolId = getCurrentUserSchoolId(currentUser);
|
||||
if (!schoolId) {
|
||||
throw new ForbiddenError('auth.forbidden');
|
||||
}
|
||||
|
||||
return schoolId;
|
||||
}
|
||||
|
||||
function assertRecordInCurrentSchool(record, currentUser, field = 'schoolId') {
|
||||
if (!record || !currentUser || currentUser?.app_role?.globalAccess) {
|
||||
return;
|
||||
}
|
||||
|
||||
const schoolId = getCurrentUserSchoolId(currentUser);
|
||||
const recordSchoolId = field === 'id' ? record.id : record[field];
|
||||
|
||||
if (!schoolId || String(recordSchoolId || '') !== String(schoolId)) {
|
||||
throw new ForbiddenError('auth.forbidden');
|
||||
}
|
||||
}
|
||||
|
||||
async function assertRelatedRecordInCurrentSchool(modelName, id, currentUser, field = 'schoolId', options = {}) {
|
||||
if (!id || !currentUser || currentUser?.app_role?.globalAccess) {
|
||||
return;
|
||||
}
|
||||
|
||||
const model = db[modelName];
|
||||
if (!model) {
|
||||
throw new Error(`School scope model not found: ${modelName}`);
|
||||
}
|
||||
|
||||
if (field === 'id') {
|
||||
const schoolId = getCurrentUserSchoolId(currentUser);
|
||||
if (!schoolId || String(id) !== String(schoolId)) {
|
||||
throw new ForbiddenError('auth.forbidden');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!model.rawAttributes?.[field]) {
|
||||
throw new Error(`School scope field ${field} not found on model ${modelName}`);
|
||||
}
|
||||
|
||||
const record = await model.findByPk(id, {
|
||||
attributes: ['id', field],
|
||||
transaction: options.transaction,
|
||||
});
|
||||
|
||||
if (!record) {
|
||||
throw new ForbiddenError('auth.forbidden');
|
||||
}
|
||||
|
||||
assertRecordInCurrentSchool(record, currentUser, field);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
applySchoolScope,
|
||||
applySchoolScopeById,
|
||||
assertRecordInCurrentSchool,
|
||||
assertRelatedRecordInCurrentSchool,
|
||||
resolveSchoolIdForMutation,
|
||||
getCurrentUserSchoolId,
|
||||
};
|
||||
@ -1,7 +1,8 @@
|
||||
|
||||
const db = require('../models');
|
||||
const FileDBApi = require('./file');
|
||||
const crypto = require('crypto');
|
||||
const Utils = require('../utils');
|
||||
const { applySchoolScope, applySchoolScopeById, assertRecordInCurrentSchool } = require('./schoolScope');
|
||||
|
||||
|
||||
|
||||
@ -24,14 +25,6 @@ module.exports = class SchoolsDBApi {
|
||||
||
|
||||
null
|
||||
,
|
||||
nif: data.nif || null,
|
||||
phone: data.phone || null,
|
||||
email: data.email || null,
|
||||
province: data.province || null,
|
||||
municipality: data.municipality || null,
|
||||
address: data.address || null,
|
||||
logoUrl: data.logoUrl || null,
|
||||
status: data.status || 'active',
|
||||
|
||||
importHash: data.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
@ -62,14 +55,6 @@ module.exports = class SchoolsDBApi {
|
||||
||
|
||||
null
|
||||
,
|
||||
nif: item.nif || null,
|
||||
phone: item.phone || null,
|
||||
email: item.email || null,
|
||||
province: item.province || null,
|
||||
municipality: item.municipality || null,
|
||||
address: item.address || null,
|
||||
logoUrl: item.logoUrl || null,
|
||||
status: item.status || 'active',
|
||||
|
||||
importHash: item.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
@ -89,8 +74,9 @@ module.exports = class SchoolsDBApi {
|
||||
static async update(id, data, options) {
|
||||
const currentUser = (options && options.currentUser) || {id: null};
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
const globalAccess = currentUser.app_role?.globalAccess;
|
||||
|
||||
const schools = await db.schools.findByPk(id, {}, {transaction});
|
||||
assertRecordInCurrentSchool(schools, currentUser, 'id');
|
||||
|
||||
|
||||
|
||||
@ -98,14 +84,6 @@ module.exports = class SchoolsDBApi {
|
||||
const updatePayload = {};
|
||||
|
||||
if (data.name !== undefined) updatePayload.name = data.name;
|
||||
if (data.nif !== undefined) updatePayload.nif = data.nif;
|
||||
if (data.phone !== undefined) updatePayload.phone = data.phone;
|
||||
if (data.email !== undefined) updatePayload.email = data.email;
|
||||
if (data.province !== undefined) updatePayload.province = data.province;
|
||||
if (data.municipality !== undefined) updatePayload.municipality = data.municipality;
|
||||
if (data.address !== undefined) updatePayload.address = data.address;
|
||||
if (data.logoUrl !== undefined) updatePayload.logoUrl = data.logoUrl;
|
||||
if (data.status !== undefined) updatePayload.status = data.status;
|
||||
|
||||
|
||||
updatePayload.updatedById = currentUser.id;
|
||||
@ -136,8 +114,6 @@ module.exports = class SchoolsDBApi {
|
||||
transaction,
|
||||
});
|
||||
|
||||
schools.forEach((record) => assertRecordInCurrentSchool(record, currentUser, 'id'));
|
||||
|
||||
await db.sequelize.transaction(async (transaction) => {
|
||||
for (const record of schools) {
|
||||
await record.update(
|
||||
@ -159,7 +135,6 @@ module.exports = class SchoolsDBApi {
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const schools = await db.schools.findByPk(id, options);
|
||||
assertRecordInCurrentSchool(schools, currentUser, 'id');
|
||||
|
||||
await schools.update({
|
||||
deletedBy: currentUser.id
|
||||
@ -176,9 +151,6 @@ module.exports = class SchoolsDBApi {
|
||||
|
||||
static async findBy(where, options) {
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
const currentUser = (options && options.currentUser) || null;
|
||||
|
||||
applySchoolScope(where, currentUser?.app_role?.globalAccess, currentUser, 'id');
|
||||
|
||||
const schools = await db.schools.findOne(
|
||||
{ where },
|
||||
@ -298,11 +270,26 @@ module.exports = class SchoolsDBApi {
|
||||
let offset = 0;
|
||||
let where = {};
|
||||
const currentPage = +filter.page;
|
||||
|
||||
|
||||
const user = (options && options.currentUser) || null;
|
||||
const userSchools = (user && user.schools?.id) || null;
|
||||
|
||||
|
||||
|
||||
if (userSchools) {
|
||||
if (options?.currentUser?.schoolsId) {
|
||||
where.schoolsId = options.currentUser.schoolsId;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
offset = currentPage * limit;
|
||||
|
||||
const orderBy = null;
|
||||
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
let include = [
|
||||
|
||||
|
||||
@ -329,24 +316,6 @@ module.exports = class SchoolsDBApi {
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.status) {
|
||||
where = {
|
||||
...where,
|
||||
status: filter.status,
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.province) {
|
||||
where = {
|
||||
...where,
|
||||
[Op.and]: Utils.ilike(
|
||||
'schools',
|
||||
'province',
|
||||
filter.province,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@ -391,7 +360,11 @@ module.exports = class SchoolsDBApi {
|
||||
}
|
||||
|
||||
|
||||
applySchoolScope(where, globalAccess, user, 'id');
|
||||
|
||||
if (globalAccess) {
|
||||
delete where.schoolsId;
|
||||
}
|
||||
|
||||
|
||||
const queryOptions = {
|
||||
where,
|
||||
@ -422,9 +395,14 @@ module.exports = class SchoolsDBApi {
|
||||
}
|
||||
}
|
||||
|
||||
static async findAllAutocomplete(query, limit, offset, globalAccess, schoolId,) {
|
||||
static async findAllAutocomplete(query, limit, offset, globalAccess, organizationId,) {
|
||||
let where = {};
|
||||
|
||||
|
||||
if (!globalAccess && organizationId) {
|
||||
where.organizationId = organizationId;
|
||||
}
|
||||
|
||||
|
||||
if (query) {
|
||||
where = {
|
||||
@ -439,8 +417,6 @@ module.exports = class SchoolsDBApi {
|
||||
};
|
||||
}
|
||||
|
||||
applySchoolScopeById(where, globalAccess, schoolId, 'id');
|
||||
|
||||
const records = await db.schools.findAll({
|
||||
attributes: [ 'id', 'name' ],
|
||||
where,
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
|
||||
const db = require('../models');
|
||||
const FileDBApi = require('./file');
|
||||
const crypto = require('crypto');
|
||||
const Utils = require('../utils');
|
||||
const { applySchoolScope, applySchoolScopeById, assertRecordInCurrentSchool, assertRelatedRecordInCurrentSchool, resolveSchoolIdForMutation } = require('./schoolScope');
|
||||
|
||||
|
||||
|
||||
@ -39,17 +40,15 @@ module.exports = class Student_guardiansDBApi {
|
||||
);
|
||||
|
||||
|
||||
await assertRelatedRecordInCurrentSchool('students', data.student, currentUser, 'schoolId', { transaction });
|
||||
await student_guardians.setStudent( data.student || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
await assertRelatedRecordInCurrentSchool('guardians', data.guardian, currentUser, 'schoolId', { transaction });
|
||||
await student_guardians.setGuardian( data.guardian || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
await student_guardians.setSchools(resolveSchoolIdForMutation(data.schools, currentUser), {
|
||||
await student_guardians.setSchools( data.schools || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
@ -84,7 +83,6 @@ module.exports = class Student_guardiansDBApi {
|
||||
importHash: item.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
schoolsId: resolveSchoolIdForMutation(item.schools, currentUser),
|
||||
createdAt: new Date(Date.now() + index * 1000),
|
||||
}));
|
||||
|
||||
@ -100,8 +98,9 @@ module.exports = class Student_guardiansDBApi {
|
||||
static async update(id, data, options) {
|
||||
const currentUser = (options && options.currentUser) || {id: null};
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
const globalAccess = currentUser.app_role?.globalAccess;
|
||||
|
||||
const student_guardians = await db.student_guardians.findByPk(id, {}, {transaction});
|
||||
assertRecordInCurrentSchool(student_guardians, currentUser, 'schoolsId');
|
||||
|
||||
|
||||
|
||||
@ -121,7 +120,6 @@ module.exports = class Student_guardiansDBApi {
|
||||
|
||||
|
||||
if (data.student !== undefined) {
|
||||
await assertRelatedRecordInCurrentSchool('students', data.student, currentUser, 'schoolId', { transaction });
|
||||
await student_guardians.setStudent(
|
||||
|
||||
data.student,
|
||||
@ -131,7 +129,6 @@ module.exports = class Student_guardiansDBApi {
|
||||
}
|
||||
|
||||
if (data.guardian !== undefined) {
|
||||
await assertRelatedRecordInCurrentSchool('guardians', data.guardian, currentUser, 'schoolId', { transaction });
|
||||
await student_guardians.setGuardian(
|
||||
|
||||
data.guardian,
|
||||
@ -143,7 +140,7 @@ module.exports = class Student_guardiansDBApi {
|
||||
if (data.schools !== undefined) {
|
||||
await student_guardians.setSchools(
|
||||
|
||||
resolveSchoolIdForMutation(data.schools, currentUser),
|
||||
data.schools,
|
||||
|
||||
{ transaction }
|
||||
);
|
||||
@ -171,8 +168,6 @@ module.exports = class Student_guardiansDBApi {
|
||||
transaction,
|
||||
});
|
||||
|
||||
student_guardians.forEach((record) => assertRecordInCurrentSchool(record, currentUser, 'schoolsId'));
|
||||
|
||||
await db.sequelize.transaction(async (transaction) => {
|
||||
for (const record of student_guardians) {
|
||||
await record.update(
|
||||
@ -194,7 +189,6 @@ module.exports = class Student_guardiansDBApi {
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const student_guardians = await db.student_guardians.findByPk(id, options);
|
||||
assertRecordInCurrentSchool(student_guardians, currentUser, 'schoolsId');
|
||||
|
||||
await student_guardians.update({
|
||||
deletedBy: currentUser.id
|
||||
@ -211,9 +205,6 @@ module.exports = class Student_guardiansDBApi {
|
||||
|
||||
static async findBy(where, options) {
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
const currentUser = (options && options.currentUser) || null;
|
||||
|
||||
applySchoolScope(where, currentUser?.app_role?.globalAccess, currentUser, 'schoolsId');
|
||||
|
||||
const student_guardians = await db.student_guardians.findOne(
|
||||
{ where },
|
||||
@ -276,11 +267,26 @@ module.exports = class Student_guardiansDBApi {
|
||||
let offset = 0;
|
||||
let where = {};
|
||||
const currentPage = +filter.page;
|
||||
|
||||
|
||||
const user = (options && options.currentUser) || null;
|
||||
const userSchools = (user && user.schools?.id) || null;
|
||||
|
||||
|
||||
|
||||
if (userSchools) {
|
||||
if (options?.currentUser?.schoolsId) {
|
||||
where.schoolsId = options.currentUser.schoolsId;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
offset = currentPage * limit;
|
||||
|
||||
const orderBy = null;
|
||||
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
let include = [
|
||||
|
||||
{
|
||||
@ -411,7 +417,11 @@ module.exports = class Student_guardiansDBApi {
|
||||
}
|
||||
|
||||
|
||||
applySchoolScope(where, globalAccess, user, 'schoolsId');
|
||||
|
||||
if (globalAccess) {
|
||||
delete where.schoolsId;
|
||||
}
|
||||
|
||||
|
||||
const queryOptions = {
|
||||
where,
|
||||
@ -442,9 +452,14 @@ module.exports = class Student_guardiansDBApi {
|
||||
}
|
||||
}
|
||||
|
||||
static async findAllAutocomplete(query, limit, offset, globalAccess, schoolId,) {
|
||||
static async findAllAutocomplete(query, limit, offset, globalAccess, organizationId,) {
|
||||
let where = {};
|
||||
|
||||
|
||||
if (!globalAccess && organizationId) {
|
||||
where.organizationId = organizationId;
|
||||
}
|
||||
|
||||
|
||||
if (query) {
|
||||
where = {
|
||||
@ -459,8 +474,6 @@ module.exports = class Student_guardiansDBApi {
|
||||
};
|
||||
}
|
||||
|
||||
applySchoolScopeById(where, globalAccess, schoolId, 'schoolsId');
|
||||
|
||||
const records = await db.student_guardians.findAll({
|
||||
attributes: [ 'id', 'tipo_responsabilidade' ],
|
||||
where,
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
|
||||
const db = require('../models');
|
||||
const FileDBApi = require('./file');
|
||||
const crypto = require('crypto');
|
||||
const Utils = require('../utils');
|
||||
const { applySchoolScope, applySchoolScopeById, assertRecordInCurrentSchool, resolveSchoolIdForMutation } = require('./schoolScope');
|
||||
|
||||
|
||||
|
||||
@ -74,7 +74,7 @@ module.exports = class StudentsDBApi {
|
||||
);
|
||||
|
||||
|
||||
await students.setSchool(resolveSchoolIdForMutation(data.school, currentUser), {
|
||||
await students.setSchool( data.school || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
@ -153,7 +153,6 @@ module.exports = class StudentsDBApi {
|
||||
importHash: item.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
schoolId: resolveSchoolIdForMutation(item.school, currentUser),
|
||||
createdAt: new Date(Date.now() + index * 1000),
|
||||
}));
|
||||
|
||||
@ -181,8 +180,9 @@ module.exports = class StudentsDBApi {
|
||||
static async update(id, data, options) {
|
||||
const currentUser = (options && options.currentUser) || {id: null};
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
const globalAccess = currentUser.app_role?.globalAccess;
|
||||
|
||||
const students = await db.students.findByPk(id, {}, {transaction});
|
||||
assertRecordInCurrentSchool(students, currentUser, 'schoolId');
|
||||
|
||||
|
||||
|
||||
@ -225,7 +225,7 @@ module.exports = class StudentsDBApi {
|
||||
if (data.school !== undefined) {
|
||||
await students.setSchool(
|
||||
|
||||
resolveSchoolIdForMutation(data.school, currentUser),
|
||||
data.school,
|
||||
|
||||
{ transaction }
|
||||
);
|
||||
@ -236,17 +236,15 @@ module.exports = class StudentsDBApi {
|
||||
|
||||
|
||||
|
||||
if (data.foto !== undefined) {
|
||||
await FileDBApi.replaceRelationFiles(
|
||||
{
|
||||
belongsTo: db.students.getTableName(),
|
||||
belongsToColumn: 'foto',
|
||||
belongsToId: students.id,
|
||||
},
|
||||
data.foto,
|
||||
options,
|
||||
);
|
||||
}
|
||||
await FileDBApi.replaceRelationFiles(
|
||||
{
|
||||
belongsTo: db.students.getTableName(),
|
||||
belongsToColumn: 'foto',
|
||||
belongsToId: students.id,
|
||||
},
|
||||
data.foto,
|
||||
options,
|
||||
);
|
||||
|
||||
|
||||
return students;
|
||||
@ -265,8 +263,6 @@ module.exports = class StudentsDBApi {
|
||||
transaction,
|
||||
});
|
||||
|
||||
students.forEach((record) => assertRecordInCurrentSchool(record, currentUser, 'schoolId'));
|
||||
|
||||
await db.sequelize.transaction(async (transaction) => {
|
||||
for (const record of students) {
|
||||
await record.update(
|
||||
@ -288,7 +284,6 @@ module.exports = class StudentsDBApi {
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const students = await db.students.findByPk(id, options);
|
||||
assertRecordInCurrentSchool(students, currentUser, 'schoolId');
|
||||
|
||||
await students.update({
|
||||
deletedBy: currentUser.id
|
||||
@ -305,9 +300,6 @@ module.exports = class StudentsDBApi {
|
||||
|
||||
static async findBy(where, options) {
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
const currentUser = (options && options.currentUser) || null;
|
||||
|
||||
applySchoolScope(where, currentUser?.app_role?.globalAccess, currentUser, 'schoolId');
|
||||
|
||||
const students = await db.students.findOne(
|
||||
{ where },
|
||||
@ -389,11 +381,26 @@ module.exports = class StudentsDBApi {
|
||||
let offset = 0;
|
||||
let where = {};
|
||||
const currentPage = +filter.page;
|
||||
|
||||
|
||||
const user = (options && options.currentUser) || null;
|
||||
const userSchools = (user && user.schools?.id) || null;
|
||||
|
||||
|
||||
|
||||
if (userSchools) {
|
||||
if (options?.currentUser?.schoolsId) {
|
||||
where.schoolsId = options.currentUser.schoolsId;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
offset = currentPage * limit;
|
||||
|
||||
const orderBy = null;
|
||||
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
let include = [
|
||||
|
||||
{
|
||||
@ -581,7 +588,11 @@ module.exports = class StudentsDBApi {
|
||||
}
|
||||
|
||||
|
||||
applySchoolScope(where, globalAccess, user, 'schoolId');
|
||||
|
||||
if (globalAccess) {
|
||||
delete where.schoolsId;
|
||||
}
|
||||
|
||||
|
||||
const queryOptions = {
|
||||
where,
|
||||
@ -612,9 +623,14 @@ module.exports = class StudentsDBApi {
|
||||
}
|
||||
}
|
||||
|
||||
static async findAllAutocomplete(query, limit, offset, globalAccess, schoolId,) {
|
||||
static async findAllAutocomplete(query, limit, offset, globalAccess, organizationId,) {
|
||||
let where = {};
|
||||
|
||||
|
||||
if (!globalAccess && organizationId) {
|
||||
where.organizationId = organizationId;
|
||||
}
|
||||
|
||||
|
||||
if (query) {
|
||||
where = {
|
||||
@ -629,8 +645,6 @@ module.exports = class StudentsDBApi {
|
||||
};
|
||||
}
|
||||
|
||||
applySchoolScopeById(where, globalAccess, schoolId, 'schoolId');
|
||||
|
||||
const records = await db.students.findAll({
|
||||
attributes: [ 'id', 'nome_completo' ],
|
||||
where,
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
|
||||
const db = require('../models');
|
||||
const FileDBApi = require('./file');
|
||||
const crypto = require('crypto');
|
||||
const Utils = require('../utils');
|
||||
const { applySchoolScope, applySchoolScopeById, assertRecordInCurrentSchool, resolveSchoolIdForMutation } = require('./schoolScope');
|
||||
|
||||
|
||||
|
||||
@ -38,7 +39,7 @@ module.exports = class SubjectsDBApi {
|
||||
);
|
||||
|
||||
|
||||
await subjects.setSchool(resolveSchoolIdForMutation(data.school, currentUser), {
|
||||
await subjects.setSchool( data.school || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
@ -72,7 +73,6 @@ module.exports = class SubjectsDBApi {
|
||||
importHash: item.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
schoolId: resolveSchoolIdForMutation(item.school, currentUser),
|
||||
createdAt: new Date(Date.now() + index * 1000),
|
||||
}));
|
||||
|
||||
@ -88,8 +88,9 @@ module.exports = class SubjectsDBApi {
|
||||
static async update(id, data, options) {
|
||||
const currentUser = (options && options.currentUser) || {id: null};
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
const globalAccess = currentUser.app_role?.globalAccess;
|
||||
|
||||
const subjects = await db.subjects.findByPk(id, {}, {transaction});
|
||||
assertRecordInCurrentSchool(subjects, currentUser, 'schoolId');
|
||||
|
||||
|
||||
|
||||
@ -111,7 +112,7 @@ module.exports = class SubjectsDBApi {
|
||||
if (data.school !== undefined) {
|
||||
await subjects.setSchool(
|
||||
|
||||
resolveSchoolIdForMutation(data.school, currentUser),
|
||||
data.school,
|
||||
|
||||
{ transaction }
|
||||
);
|
||||
@ -139,8 +140,6 @@ module.exports = class SubjectsDBApi {
|
||||
transaction,
|
||||
});
|
||||
|
||||
subjects.forEach((record) => assertRecordInCurrentSchool(record, currentUser, 'schoolId'));
|
||||
|
||||
await db.sequelize.transaction(async (transaction) => {
|
||||
for (const record of subjects) {
|
||||
await record.update(
|
||||
@ -162,7 +161,6 @@ module.exports = class SubjectsDBApi {
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const subjects = await db.subjects.findByPk(id, options);
|
||||
assertRecordInCurrentSchool(subjects, currentUser, 'schoolId');
|
||||
|
||||
await subjects.update({
|
||||
deletedBy: currentUser.id
|
||||
@ -179,9 +177,6 @@ module.exports = class SubjectsDBApi {
|
||||
|
||||
static async findBy(where, options) {
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
const currentUser = (options && options.currentUser) || null;
|
||||
|
||||
applySchoolScope(where, currentUser?.app_role?.globalAccess, currentUser, 'schoolId');
|
||||
|
||||
const subjects = await db.subjects.findOne(
|
||||
{ where },
|
||||
@ -238,11 +233,26 @@ module.exports = class SubjectsDBApi {
|
||||
let offset = 0;
|
||||
let where = {};
|
||||
const currentPage = +filter.page;
|
||||
|
||||
|
||||
const user = (options && options.currentUser) || null;
|
||||
const userSchools = (user && user.schools?.id) || null;
|
||||
|
||||
|
||||
|
||||
if (userSchools) {
|
||||
if (options?.currentUser?.schoolsId) {
|
||||
where.schoolsId = options.currentUser.schoolsId;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
offset = currentPage * limit;
|
||||
|
||||
const orderBy = null;
|
||||
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
let include = [
|
||||
|
||||
{
|
||||
@ -339,7 +349,11 @@ module.exports = class SubjectsDBApi {
|
||||
}
|
||||
|
||||
|
||||
applySchoolScope(where, globalAccess, user, 'schoolId');
|
||||
|
||||
if (globalAccess) {
|
||||
delete where.schoolsId;
|
||||
}
|
||||
|
||||
|
||||
const queryOptions = {
|
||||
where,
|
||||
@ -370,9 +384,14 @@ module.exports = class SubjectsDBApi {
|
||||
}
|
||||
}
|
||||
|
||||
static async findAllAutocomplete(query, limit, offset, globalAccess, schoolId,) {
|
||||
static async findAllAutocomplete(query, limit, offset, globalAccess, organizationId,) {
|
||||
let where = {};
|
||||
|
||||
|
||||
if (!globalAccess && organizationId) {
|
||||
where.organizationId = organizationId;
|
||||
}
|
||||
|
||||
|
||||
if (query) {
|
||||
where = {
|
||||
@ -387,8 +406,6 @@ module.exports = class SubjectsDBApi {
|
||||
};
|
||||
}
|
||||
|
||||
applySchoolScopeById(where, globalAccess, schoolId, 'schoolId');
|
||||
|
||||
const records = await db.subjects.findAll({
|
||||
attributes: [ 'id', 'nome' ],
|
||||
where,
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
|
||||
const db = require('../models');
|
||||
const FileDBApi = require('./file');
|
||||
const crypto = require('crypto');
|
||||
const Utils = require('../utils');
|
||||
const { applySchoolScope, applySchoolScopeById, assertRecordInCurrentSchool, resolveSchoolIdForMutation } = require('./schoolScope');
|
||||
|
||||
|
||||
|
||||
@ -53,7 +54,7 @@ module.exports = class TeachersDBApi {
|
||||
);
|
||||
|
||||
|
||||
await teachers.setSchool(resolveSchoolIdForMutation(data.school, currentUser), {
|
||||
await teachers.setSchool( data.school || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
@ -102,7 +103,6 @@ module.exports = class TeachersDBApi {
|
||||
importHash: item.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
schoolId: resolveSchoolIdForMutation(item.school, currentUser),
|
||||
createdAt: new Date(Date.now() + index * 1000),
|
||||
}));
|
||||
|
||||
@ -118,8 +118,9 @@ module.exports = class TeachersDBApi {
|
||||
static async update(id, data, options) {
|
||||
const currentUser = (options && options.currentUser) || {id: null};
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
const globalAccess = currentUser.app_role?.globalAccess;
|
||||
|
||||
const teachers = await db.teachers.findByPk(id, {}, {transaction});
|
||||
assertRecordInCurrentSchool(teachers, currentUser, 'schoolId');
|
||||
|
||||
|
||||
|
||||
@ -150,7 +151,7 @@ module.exports = class TeachersDBApi {
|
||||
if (data.school !== undefined) {
|
||||
await teachers.setSchool(
|
||||
|
||||
resolveSchoolIdForMutation(data.school, currentUser),
|
||||
data.school,
|
||||
|
||||
{ transaction }
|
||||
);
|
||||
@ -178,8 +179,6 @@ module.exports = class TeachersDBApi {
|
||||
transaction,
|
||||
});
|
||||
|
||||
teachers.forEach((record) => assertRecordInCurrentSchool(record, currentUser, 'schoolId'));
|
||||
|
||||
await db.sequelize.transaction(async (transaction) => {
|
||||
for (const record of teachers) {
|
||||
await record.update(
|
||||
@ -201,7 +200,6 @@ module.exports = class TeachersDBApi {
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const teachers = await db.teachers.findByPk(id, options);
|
||||
assertRecordInCurrentSchool(teachers, currentUser, 'schoolId');
|
||||
|
||||
await teachers.update({
|
||||
deletedBy: currentUser.id
|
||||
@ -218,9 +216,6 @@ module.exports = class TeachersDBApi {
|
||||
|
||||
static async findBy(where, options) {
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
const currentUser = (options && options.currentUser) || null;
|
||||
|
||||
applySchoolScope(where, currentUser?.app_role?.globalAccess, currentUser, 'schoolId');
|
||||
|
||||
const teachers = await db.teachers.findOne(
|
||||
{ where },
|
||||
@ -277,11 +272,26 @@ module.exports = class TeachersDBApi {
|
||||
let offset = 0;
|
||||
let where = {};
|
||||
const currentPage = +filter.page;
|
||||
|
||||
|
||||
const user = (options && options.currentUser) || null;
|
||||
const userSchools = (user && user.schools?.id) || null;
|
||||
|
||||
|
||||
|
||||
if (userSchools) {
|
||||
if (options?.currentUser?.schoolsId) {
|
||||
where.schoolsId = options.currentUser.schoolsId;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
offset = currentPage * limit;
|
||||
|
||||
const orderBy = null;
|
||||
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
let include = [
|
||||
|
||||
{
|
||||
@ -411,7 +421,11 @@ module.exports = class TeachersDBApi {
|
||||
}
|
||||
|
||||
|
||||
applySchoolScope(where, globalAccess, user, 'schoolId');
|
||||
|
||||
if (globalAccess) {
|
||||
delete where.schoolsId;
|
||||
}
|
||||
|
||||
|
||||
const queryOptions = {
|
||||
where,
|
||||
@ -442,9 +456,14 @@ module.exports = class TeachersDBApi {
|
||||
}
|
||||
}
|
||||
|
||||
static async findAllAutocomplete(query, limit, offset, globalAccess, schoolId,) {
|
||||
static async findAllAutocomplete(query, limit, offset, globalAccess, organizationId,) {
|
||||
let where = {};
|
||||
|
||||
|
||||
if (!globalAccess && organizationId) {
|
||||
where.organizationId = organizationId;
|
||||
}
|
||||
|
||||
|
||||
if (query) {
|
||||
where = {
|
||||
@ -459,8 +478,6 @@ module.exports = class TeachersDBApi {
|
||||
};
|
||||
}
|
||||
|
||||
applySchoolScopeById(where, globalAccess, schoolId, 'schoolId');
|
||||
|
||||
const records = await db.teachers.findAll({
|
||||
attributes: [ 'id', 'nome' ],
|
||||
where,
|
||||
|
||||
@ -3,8 +3,6 @@ const db = require('../models');
|
||||
const FileDBApi = require('./file');
|
||||
const crypto = require('crypto');
|
||||
const Utils = require('../utils');
|
||||
const ForbiddenError = require('../../services/notifications/errors/forbidden');
|
||||
const { applySchoolScope, applySchoolScopeById, assertRecordInCurrentSchool, resolveSchoolIdForMutation } = require('./schoolScope');
|
||||
|
||||
const bcrypt = require('bcrypt');
|
||||
const config = require('../../config');
|
||||
@ -14,71 +12,12 @@ const config = require('../../config');
|
||||
const Sequelize = db.Sequelize;
|
||||
const Op = Sequelize.Op;
|
||||
|
||||
|
||||
async function assertAssignableRole(roleId, currentUser, transaction) {
|
||||
if (!roleId || currentUser?.app_role?.globalAccess) {
|
||||
return;
|
||||
}
|
||||
|
||||
const role = await db.roles.findByPk(roleId, { transaction });
|
||||
if (!role || role.globalAccess) {
|
||||
throw new ForbiddenError('auth.forbidden');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function assertSafeSelfUpdate(id, data, currentUser) {
|
||||
if (
|
||||
!currentUser?.id ||
|
||||
String(currentUser.id) !== String(id) ||
|
||||
currentUser?.app_role?.globalAccess
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const restrictedFields = [
|
||||
'app_role',
|
||||
'custom_permissions',
|
||||
'schools',
|
||||
'disabled',
|
||||
'email',
|
||||
'emailVerified',
|
||||
'emailVerificationToken',
|
||||
'emailVerificationTokenExpiresAt',
|
||||
'passwordResetToken',
|
||||
'passwordResetTokenExpiresAt',
|
||||
'provider',
|
||||
];
|
||||
|
||||
const hasRestrictedField = restrictedFields.some((field) => (
|
||||
Object.prototype.hasOwnProperty.call(data || {}, field) && data[field] !== undefined
|
||||
));
|
||||
|
||||
if (hasRestrictedField) {
|
||||
throw new ForbiddenError('auth.forbidden');
|
||||
}
|
||||
}
|
||||
|
||||
function assertCanAssignCustomPermissions(customPermissions, currentUser) {
|
||||
if (currentUser?.app_role?.globalAccess || customPermissions === undefined || customPermissions === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Array.isArray(customPermissions) && customPermissions.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
throw new ForbiddenError('auth.forbidden');
|
||||
}
|
||||
|
||||
module.exports = class UsersDBApi {
|
||||
|
||||
static async create(data,globalAccess, options) {
|
||||
const currentUser = (options && options.currentUser) || { id: null };
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
await assertAssignableRole(data.data.app_role, currentUser, transaction);
|
||||
assertCanAssignCustomPermissions(data.data.custom_permissions, currentUser);
|
||||
|
||||
const users = await db.users.create(
|
||||
{
|
||||
@ -173,7 +112,7 @@ module.exports = class UsersDBApi {
|
||||
|
||||
|
||||
|
||||
await users.setSchools(resolveSchoolIdForMutation(data.data.schools, currentUser), {
|
||||
await users.setSchools( data.data.schools || null, {
|
||||
transaction,
|
||||
});
|
||||
|
||||
@ -276,7 +215,6 @@ module.exports = class UsersDBApi {
|
||||
importHash: item.importHash || null,
|
||||
createdById: currentUser.id,
|
||||
updatedById: currentUser.id,
|
||||
schoolsId: resolveSchoolIdForMutation(item.schools, currentUser),
|
||||
createdAt: new Date(Date.now() + index * 1000),
|
||||
}));
|
||||
|
||||
@ -304,20 +242,10 @@ module.exports = class UsersDBApi {
|
||||
static async update(id, data, globalAccess, options) {
|
||||
const currentUser = (options && options.currentUser) || {id: null};
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
assertSafeSelfUpdate(id, data, currentUser);
|
||||
const appRoleProvided = Object.prototype.hasOwnProperty.call(data || {}, 'app_role');
|
||||
const customPermissionsProvided = Object.prototype.hasOwnProperty.call(data || {}, 'custom_permissions');
|
||||
|
||||
|
||||
const users = await db.users.findByPk(id, {}, {transaction});
|
||||
assertRecordInCurrentSchool(users, currentUser, 'schoolsId');
|
||||
|
||||
if (appRoleProvided) {
|
||||
await assertAssignableRole(data.app_role, currentUser, transaction);
|
||||
}
|
||||
if (customPermissionsProvided) {
|
||||
assertCanAssignCustomPermissions(data.custom_permissions, currentUser);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -396,7 +324,7 @@ module.exports = class UsersDBApi {
|
||||
if (data.schools !== undefined) {
|
||||
await users.setSchools(
|
||||
|
||||
resolveSchoolIdForMutation(data.schools, currentUser),
|
||||
data.schools,
|
||||
|
||||
{ transaction }
|
||||
);
|
||||
@ -411,17 +339,15 @@ module.exports = class UsersDBApi {
|
||||
|
||||
|
||||
|
||||
if (data.avatar !== undefined) {
|
||||
await FileDBApi.replaceRelationFiles(
|
||||
{
|
||||
belongsTo: db.users.getTableName(),
|
||||
belongsToColumn: 'avatar',
|
||||
belongsToId: users.id,
|
||||
},
|
||||
data.avatar,
|
||||
options,
|
||||
);
|
||||
}
|
||||
await FileDBApi.replaceRelationFiles(
|
||||
{
|
||||
belongsTo: db.users.getTableName(),
|
||||
belongsToColumn: 'avatar',
|
||||
belongsToId: users.id,
|
||||
},
|
||||
data.avatar,
|
||||
options,
|
||||
);
|
||||
|
||||
|
||||
return users;
|
||||
@ -440,8 +366,6 @@ module.exports = class UsersDBApi {
|
||||
transaction,
|
||||
});
|
||||
|
||||
users.forEach((record) => assertRecordInCurrentSchool(record, currentUser, 'schoolsId'));
|
||||
|
||||
await db.sequelize.transaction(async (transaction) => {
|
||||
for (const record of users) {
|
||||
await record.update(
|
||||
@ -463,7 +387,6 @@ module.exports = class UsersDBApi {
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
const users = await db.users.findByPk(id, options);
|
||||
assertRecordInCurrentSchool(users, currentUser, 'schoolsId');
|
||||
|
||||
await users.update({
|
||||
deletedBy: currentUser.id
|
||||
@ -480,9 +403,6 @@ module.exports = class UsersDBApi {
|
||||
|
||||
static async findBy(where, options) {
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
const currentUser = (options && options.currentUser) || null;
|
||||
|
||||
applySchoolScope(where, currentUser?.app_role?.globalAccess, currentUser, 'schoolsId');
|
||||
|
||||
const users = await db.users.findOne(
|
||||
{ where },
|
||||
@ -556,11 +476,26 @@ module.exports = class UsersDBApi {
|
||||
let offset = 0;
|
||||
let where = {};
|
||||
const currentPage = +filter.page;
|
||||
|
||||
|
||||
const user = (options && options.currentUser) || null;
|
||||
const userSchools = (user && user.schools?.id) || null;
|
||||
|
||||
|
||||
|
||||
if (userSchools) {
|
||||
if (options?.currentUser?.schoolsId) {
|
||||
where.schoolsId = options.currentUser.schoolsId;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
offset = currentPage * limit;
|
||||
|
||||
const orderBy = null;
|
||||
|
||||
const transaction = (options && options.transaction) || undefined;
|
||||
|
||||
let include = [
|
||||
|
||||
{
|
||||
@ -842,7 +777,11 @@ module.exports = class UsersDBApi {
|
||||
}
|
||||
|
||||
|
||||
applySchoolScope(where, globalAccess, user, 'schoolsId');
|
||||
|
||||
if (globalAccess) {
|
||||
delete where.schoolsId;
|
||||
}
|
||||
|
||||
|
||||
const queryOptions = {
|
||||
where,
|
||||
@ -873,9 +812,14 @@ module.exports = class UsersDBApi {
|
||||
}
|
||||
}
|
||||
|
||||
static async findAllAutocomplete(query, limit, offset, globalAccess, schoolId,) {
|
||||
static async findAllAutocomplete(query, limit, offset, globalAccess, organizationId,) {
|
||||
let where = {};
|
||||
|
||||
|
||||
if (!globalAccess && organizationId) {
|
||||
where.organizationId = organizationId;
|
||||
}
|
||||
|
||||
|
||||
if (query) {
|
||||
where = {
|
||||
@ -890,8 +834,6 @@ module.exports = class UsersDBApi {
|
||||
};
|
||||
}
|
||||
|
||||
applySchoolScopeById(where, globalAccess, schoolId, 'schoolsId');
|
||||
|
||||
const records = await db.users.findAll({
|
||||
attributes: [ 'id', 'firstName' ],
|
||||
where,
|
||||
@ -916,7 +858,7 @@ module.exports = class UsersDBApi {
|
||||
authenticationUid: data.authenticationUid,
|
||||
password: data.password,
|
||||
|
||||
schoolsId: data.organizationId,
|
||||
organizationId: data.organizationId,
|
||||
|
||||
},
|
||||
{ transaction },
|
||||
|
||||
@ -2777,7 +2777,7 @@ module.exports = {
|
||||
* @param {Sequelize} Sequelize
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async down(queryInterface) {
|
||||
async down(queryInterface, Sequelize) {
|
||||
/**
|
||||
* @type {Transaction}
|
||||
*/
|
||||
|
||||
@ -1,50 +0,0 @@
|
||||
module.exports = {
|
||||
async up(queryInterface, Sequelize) {
|
||||
const transaction = await queryInterface.sequelize.transaction();
|
||||
|
||||
try {
|
||||
await queryInterface.addColumn('schools', 'nif', { type: Sequelize.DataTypes.TEXT }, { transaction });
|
||||
await queryInterface.addColumn('schools', 'phone', { type: Sequelize.DataTypes.TEXT }, { transaction });
|
||||
await queryInterface.addColumn('schools', 'email', { type: Sequelize.DataTypes.TEXT }, { transaction });
|
||||
await queryInterface.addColumn('schools', 'province', { type: Sequelize.DataTypes.TEXT }, { transaction });
|
||||
await queryInterface.addColumn('schools', 'municipality', { type: Sequelize.DataTypes.TEXT }, { transaction });
|
||||
await queryInterface.addColumn('schools', 'address', { type: Sequelize.DataTypes.TEXT }, { transaction });
|
||||
await queryInterface.addColumn('schools', 'logoUrl', { type: Sequelize.DataTypes.TEXT }, { transaction });
|
||||
await queryInterface.addColumn(
|
||||
'schools',
|
||||
'status',
|
||||
{
|
||||
type: Sequelize.DataTypes.TEXT,
|
||||
allowNull: false,
|
||||
defaultValue: 'active',
|
||||
},
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
await transaction.commit();
|
||||
} catch (error) {
|
||||
await transaction.rollback();
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
async down(queryInterface) {
|
||||
const transaction = await queryInterface.sequelize.transaction();
|
||||
|
||||
try {
|
||||
await queryInterface.removeColumn('schools', 'status', { transaction });
|
||||
await queryInterface.removeColumn('schools', 'logoUrl', { transaction });
|
||||
await queryInterface.removeColumn('schools', 'address', { transaction });
|
||||
await queryInterface.removeColumn('schools', 'municipality', { transaction });
|
||||
await queryInterface.removeColumn('schools', 'province', { transaction });
|
||||
await queryInterface.removeColumn('schools', 'email', { transaction });
|
||||
await queryInterface.removeColumn('schools', 'phone', { transaction });
|
||||
await queryInterface.removeColumn('schools', 'nif', { transaction });
|
||||
|
||||
await transaction.commit();
|
||||
} catch (error) {
|
||||
await transaction.rollback();
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
};
|
||||
@ -1,3 +1,8 @@
|
||||
const config = require('../../config');
|
||||
const providers = config.providers;
|
||||
const crypto = require('crypto');
|
||||
const bcrypt = require('bcrypt');
|
||||
const moment = require('moment');
|
||||
|
||||
module.exports = function(sequelize, DataTypes) {
|
||||
const assessments = sequelize.define(
|
||||
|
||||
@ -1,3 +1,8 @@
|
||||
const config = require('../../config');
|
||||
const providers = config.providers;
|
||||
const crypto = require('crypto');
|
||||
const bcrypt = require('bcrypt');
|
||||
const moment = require('moment');
|
||||
|
||||
module.exports = function(sequelize, DataTypes) {
|
||||
const attendance = sequelize.define(
|
||||
|
||||
@ -1,3 +1,8 @@
|
||||
const config = require('../../config');
|
||||
const providers = config.providers;
|
||||
const crypto = require('crypto');
|
||||
const bcrypt = require('bcrypt');
|
||||
const moment = require('moment');
|
||||
|
||||
module.exports = function(sequelize, DataTypes) {
|
||||
const book_loans = sequelize.define(
|
||||
|
||||
@ -1,3 +1,8 @@
|
||||
const config = require('../../config');
|
||||
const providers = config.providers;
|
||||
const crypto = require('crypto');
|
||||
const bcrypt = require('bcrypt');
|
||||
const moment = require('moment');
|
||||
|
||||
module.exports = function(sequelize, DataTypes) {
|
||||
const books = sequelize.define(
|
||||
|
||||
@ -1,3 +1,8 @@
|
||||
const config = require('../../config');
|
||||
const providers = config.providers;
|
||||
const crypto = require('crypto');
|
||||
const bcrypt = require('bcrypt');
|
||||
const moment = require('moment');
|
||||
|
||||
module.exports = function(sequelize, DataTypes) {
|
||||
const classes = sequelize.define(
|
||||
|
||||
@ -1,3 +1,8 @@
|
||||
const config = require('../../config');
|
||||
const providers = config.providers;
|
||||
const crypto = require('crypto');
|
||||
const bcrypt = require('bcrypt');
|
||||
const moment = require('moment');
|
||||
|
||||
module.exports = function(sequelize, DataTypes) {
|
||||
const courses = sequelize.define(
|
||||
|
||||
@ -1,3 +1,8 @@
|
||||
const config = require('../../config');
|
||||
const providers = config.providers;
|
||||
const crypto = require('crypto');
|
||||
const bcrypt = require('bcrypt');
|
||||
const moment = require('moment');
|
||||
|
||||
module.exports = function(sequelize, DataTypes) {
|
||||
const employees = sequelize.define(
|
||||
|
||||
@ -1,3 +1,8 @@
|
||||
const config = require('../../config');
|
||||
const providers = config.providers;
|
||||
const crypto = require('crypto');
|
||||
const bcrypt = require('bcrypt');
|
||||
const moment = require('moment');
|
||||
|
||||
module.exports = function(sequelize, DataTypes) {
|
||||
const enrollments = sequelize.define(
|
||||
|
||||
@ -1,3 +1,8 @@
|
||||
const config = require('../../config');
|
||||
const providers = config.providers;
|
||||
const crypto = require('crypto');
|
||||
const bcrypt = require('bcrypt');
|
||||
const moment = require('moment');
|
||||
|
||||
module.exports = function(sequelize, DataTypes) {
|
||||
const grades = sequelize.define(
|
||||
|
||||
@ -1,3 +1,8 @@
|
||||
const config = require('../../config');
|
||||
const providers = config.providers;
|
||||
const crypto = require('crypto');
|
||||
const bcrypt = require('bcrypt');
|
||||
const moment = require('moment');
|
||||
|
||||
module.exports = function(sequelize, DataTypes) {
|
||||
const guardians = sequelize.define(
|
||||
|
||||
@ -1,3 +1,8 @@
|
||||
const config = require('../../config');
|
||||
const providers = config.providers;
|
||||
const crypto = require('crypto');
|
||||
const bcrypt = require('bcrypt');
|
||||
const moment = require('moment');
|
||||
|
||||
module.exports = function(sequelize, DataTypes) {
|
||||
const invoices = sequelize.define(
|
||||
|
||||
@ -1,3 +1,8 @@
|
||||
const config = require('../../config');
|
||||
const providers = config.providers;
|
||||
const crypto = require('crypto');
|
||||
const bcrypt = require('bcrypt');
|
||||
const moment = require('moment');
|
||||
|
||||
module.exports = function(sequelize, DataTypes) {
|
||||
const payments = sequelize.define(
|
||||
|
||||
@ -1,3 +1,8 @@
|
||||
const config = require('../../config');
|
||||
const providers = config.providers;
|
||||
const crypto = require('crypto');
|
||||
const bcrypt = require('bcrypt');
|
||||
const moment = require('moment');
|
||||
|
||||
module.exports = function(sequelize, DataTypes) {
|
||||
const permissions = sequelize.define(
|
||||
|
||||
@ -1,3 +1,8 @@
|
||||
const config = require('../../config');
|
||||
const providers = config.providers;
|
||||
const crypto = require('crypto');
|
||||
const bcrypt = require('bcrypt');
|
||||
const moment = require('moment');
|
||||
|
||||
module.exports = function(sequelize, DataTypes) {
|
||||
const products = sequelize.define(
|
||||
|
||||
@ -1,3 +1,8 @@
|
||||
const config = require('../../config');
|
||||
const providers = config.providers;
|
||||
const crypto = require('crypto');
|
||||
const bcrypt = require('bcrypt');
|
||||
const moment = require('moment');
|
||||
|
||||
module.exports = function(sequelize, DataTypes) {
|
||||
const roles = sequelize.define(
|
||||
|
||||
@ -1,3 +1,9 @@
|
||||
const config = require('../../config');
|
||||
const providers = config.providers;
|
||||
const crypto = require('crypto');
|
||||
const bcrypt = require('bcrypt');
|
||||
const moment = require('moment');
|
||||
|
||||
module.exports = function(sequelize, DataTypes) {
|
||||
const schools = sequelize.define(
|
||||
'schools',
|
||||
@ -13,63 +19,6 @@ name: {
|
||||
|
||||
|
||||
|
||||
},
|
||||
|
||||
nif: {
|
||||
type: DataTypes.TEXT,
|
||||
|
||||
|
||||
|
||||
},
|
||||
|
||||
phone: {
|
||||
type: DataTypes.TEXT,
|
||||
|
||||
|
||||
|
||||
},
|
||||
|
||||
email: {
|
||||
type: DataTypes.TEXT,
|
||||
|
||||
|
||||
|
||||
},
|
||||
|
||||
province: {
|
||||
type: DataTypes.TEXT,
|
||||
|
||||
|
||||
|
||||
},
|
||||
|
||||
municipality: {
|
||||
type: DataTypes.TEXT,
|
||||
|
||||
|
||||
|
||||
},
|
||||
|
||||
address: {
|
||||
type: DataTypes.TEXT,
|
||||
|
||||
|
||||
|
||||
},
|
||||
|
||||
logoUrl: {
|
||||
type: DataTypes.TEXT,
|
||||
|
||||
|
||||
|
||||
},
|
||||
|
||||
status: {
|
||||
type: DataTypes.TEXT,
|
||||
allowNull: false,
|
||||
defaultValue: 'active',
|
||||
|
||||
|
||||
},
|
||||
|
||||
importHash: {
|
||||
|
||||
@ -1,3 +1,8 @@
|
||||
const config = require('../../config');
|
||||
const providers = config.providers;
|
||||
const crypto = require('crypto');
|
||||
const bcrypt = require('bcrypt');
|
||||
const moment = require('moment');
|
||||
|
||||
module.exports = function(sequelize, DataTypes) {
|
||||
const student_guardians = sequelize.define(
|
||||
|
||||
@ -1,3 +1,8 @@
|
||||
const config = require('../../config');
|
||||
const providers = config.providers;
|
||||
const crypto = require('crypto');
|
||||
const bcrypt = require('bcrypt');
|
||||
const moment = require('moment');
|
||||
|
||||
module.exports = function(sequelize, DataTypes) {
|
||||
const students = sequelize.define(
|
||||
|
||||
@ -1,3 +1,8 @@
|
||||
const config = require('../../config');
|
||||
const providers = config.providers;
|
||||
const crypto = require('crypto');
|
||||
const bcrypt = require('bcrypt');
|
||||
const moment = require('moment');
|
||||
|
||||
module.exports = function(sequelize, DataTypes) {
|
||||
const subjects = sequelize.define(
|
||||
|
||||
@ -1,3 +1,8 @@
|
||||
const config = require('../../config');
|
||||
const providers = config.providers;
|
||||
const crypto = require('crypto');
|
||||
const bcrypt = require('bcrypt');
|
||||
const moment = require('moment');
|
||||
|
||||
module.exports = function(sequelize, DataTypes) {
|
||||
const teachers = sequelize.define(
|
||||
|
||||
@ -2,6 +2,7 @@ const config = require('../../config');
|
||||
const providers = config.providers;
|
||||
const crypto = require('crypto');
|
||||
const bcrypt = require('bcrypt');
|
||||
const moment = require('moment');
|
||||
|
||||
module.exports = function(sequelize, DataTypes) {
|
||||
const users = sequelize.define(
|
||||
@ -205,13 +206,13 @@ provider: {
|
||||
};
|
||||
|
||||
|
||||
users.beforeCreate((user) => {
|
||||
trimStringFields(user);
|
||||
users.beforeCreate((users, options) => {
|
||||
users = trimStringFields(users);
|
||||
|
||||
if (user.provider !== providers.LOCAL && Object.values(providers).indexOf(user.provider) > -1) {
|
||||
user.emailVerified = true;
|
||||
if (users.provider !== providers.LOCAL && Object.values(providers).indexOf(users.provider) > -1) {
|
||||
users.emailVerified = true;
|
||||
|
||||
if (!user.password) {
|
||||
if (!users.password) {
|
||||
const password = crypto
|
||||
.randomBytes(20)
|
||||
.toString('hex');
|
||||
@ -221,13 +222,13 @@ provider: {
|
||||
config.bcrypt.saltRounds,
|
||||
);
|
||||
|
||||
user.password = hashedPassword
|
||||
users.password = hashedPassword
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
users.beforeUpdate((user) => {
|
||||
trimStringFields(user);
|
||||
users.beforeUpdate((users, options) => {
|
||||
users = trimStringFields(users);
|
||||
});
|
||||
|
||||
|
||||
|
||||
@ -10,7 +10,7 @@ const ids = [
|
||||
]
|
||||
|
||||
module.exports = {
|
||||
up: async (queryInterface) => {
|
||||
up: async (queryInterface, Sequelize) => {
|
||||
let admin_hash = bcrypt.hashSync(config.admin_pass, config.bcrypt.saltRounds);
|
||||
let user_hash = bcrypt.hashSync(config.user_pass, config.bcrypt.saltRounds);
|
||||
|
||||
|
||||
@ -63,7 +63,7 @@ module.exports = {
|
||||
}
|
||||
|
||||
const entities = [
|
||||
"users","roles","permissions","schools","students","guardians","student_guardians","teachers","courses","grades","classes","subjects","enrollments","assessments","attendance","invoices","payments","employees","products","books","book_loans",
|
||||
"users","roles","permissions","schools","students","guardians","student_guardians","teachers","courses","grades","classes","subjects","enrollments","assessments","attendance","invoices","payments","employees","products","books","book_loans",,
|
||||
];
|
||||
await queryInterface.bulkInsert("permissions", entities.flatMap(createPermissions));
|
||||
await queryInterface.bulkInsert("permissions", [{ id: getId(`READ_API_DOCS`), createdAt, updatedAt, name: `READ_API_DOCS` }]);
|
||||
|
||||
@ -4450,7 +4450,7 @@ const BookLoansData = [
|
||||
|
||||
|
||||
module.exports = {
|
||||
up: async () => {
|
||||
up: async (queryInterface, Sequelize) => {
|
||||
|
||||
|
||||
|
||||
@ -4954,7 +4954,7 @@ module.exports = {
|
||||
|
||||
},
|
||||
|
||||
down: async (queryInterface) => {
|
||||
down: async (queryInterface, Sequelize) => {
|
||||
|
||||
|
||||
|
||||
|
||||
@ -8,7 +8,7 @@ module.exports = class Helpers {
|
||||
};
|
||||
}
|
||||
|
||||
static commonErrorHandler(error, req, res) {
|
||||
static commonErrorHandler(error, req, res, next) {
|
||||
if ([400, 403, 404].includes(error.code)) {
|
||||
return res.status(error.code).send(error.message);
|
||||
}
|
||||
@ -19,5 +19,5 @@ module.exports = class Helpers {
|
||||
|
||||
static jwtSign(data) {
|
||||
return jwt.sign(data, config.secret_key, {expiresIn: '6h'});
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
@ -6,6 +6,7 @@ const passport = require('passport');
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const bodyParser = require('body-parser');
|
||||
const db = require('./db/models');
|
||||
const config = require('./config');
|
||||
const swaggerUI = require('swagger-ui-express');
|
||||
const swaggerJsDoc = require('swagger-jsdoc');
|
||||
|
||||
@ -42,12 +42,9 @@ function checkPermissions(permission) {
|
||||
return async (req, res, next) => {
|
||||
const { currentUser } = req;
|
||||
|
||||
// 1. Check self-access bypass for reading the authenticated user's own record only.
|
||||
const isSelfAccess = currentUser && (
|
||||
currentUser.id === req.params.id || currentUser.id === req.body?.id
|
||||
);
|
||||
if (isSelfAccess && permission === 'READ_USERS') {
|
||||
return next();
|
||||
// 1. Check self-access bypass (only if the user is authenticated)
|
||||
if (currentUser && (currentUser.id === req.params.id || currentUser.id === req.body.id)) {
|
||||
return next(); // User has access to their own resource
|
||||
}
|
||||
|
||||
// 2. Check Custom Permissions (only if the user is authenticated)
|
||||
@ -138,10 +135,9 @@ const METHOD_MAP = {
|
||||
*/
|
||||
function checkCrudPermissions(name) {
|
||||
return (req, res, next) => {
|
||||
const methodAction = req.path.replace(/\/$/, '') === '/deleteByIds'
|
||||
? 'DELETE'
|
||||
: METHOD_MAP[req.method];
|
||||
const permissionName = `${methodAction}_${name.toUpperCase()}`;
|
||||
// Dynamically determine the permission name (e.g., 'READ_USERS')
|
||||
const permissionName = `${METHOD_MAP[req.method]}_${name.toUpperCase()}`;
|
||||
// Call the checkPermissions middleware with the determined permission
|
||||
checkPermissions(permissionName)(req, res, next);
|
||||
};
|
||||
}
|
||||
|
||||
@ -3,9 +3,10 @@ const express = require('express');
|
||||
|
||||
const AssessmentsService = require('../services/assessments');
|
||||
const AssessmentsDBApi = require('../db/api/assessments');
|
||||
const { getCurrentUserSchoolId } = require('../db/api/schoolScope');
|
||||
const wrapAsync = require('../helpers').wrapAsync;
|
||||
|
||||
const config = require('../config');
|
||||
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
@ -308,7 +309,6 @@ router.get('/', wrapAsync(async (req, res) => {
|
||||
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
throw err;
|
||||
}
|
||||
} else {
|
||||
res.status(200).send(payload);
|
||||
@ -384,14 +384,14 @@ router.get('/autocomplete', async (req, res) => {
|
||||
|
||||
const globalAccess = req.currentUser.app_role.globalAccess;
|
||||
|
||||
const schoolId = getCurrentUserSchoolId(req.currentUser);
|
||||
const organizationId = req.currentUser.organization?.id
|
||||
|
||||
|
||||
const payload = await AssessmentsDBApi.findAllAutocomplete(
|
||||
req.query.query,
|
||||
req.query.limit,
|
||||
req.query.offset,
|
||||
globalAccess, schoolId,
|
||||
globalAccess, organizationId,
|
||||
);
|
||||
|
||||
res.status(200).send(payload);
|
||||
@ -432,7 +432,6 @@ router.get('/autocomplete', async (req, res) => {
|
||||
router.get('/:id', wrapAsync(async (req, res) => {
|
||||
const payload = await AssessmentsDBApi.findBy(
|
||||
{ id: req.params.id },
|
||||
{ currentUser: req.currentUser },
|
||||
);
|
||||
|
||||
|
||||
|
||||
@ -3,9 +3,10 @@ const express = require('express');
|
||||
|
||||
const AttendanceService = require('../services/attendance');
|
||||
const AttendanceDBApi = require('../db/api/attendance');
|
||||
const { getCurrentUserSchoolId } = require('../db/api/schoolScope');
|
||||
const wrapAsync = require('../helpers').wrapAsync;
|
||||
|
||||
const config = require('../config');
|
||||
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
@ -304,7 +305,6 @@ router.get('/', wrapAsync(async (req, res) => {
|
||||
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
throw err;
|
||||
}
|
||||
} else {
|
||||
res.status(200).send(payload);
|
||||
@ -380,14 +380,14 @@ router.get('/autocomplete', async (req, res) => {
|
||||
|
||||
const globalAccess = req.currentUser.app_role.globalAccess;
|
||||
|
||||
const schoolId = getCurrentUserSchoolId(req.currentUser);
|
||||
const organizationId = req.currentUser.organization?.id
|
||||
|
||||
|
||||
const payload = await AttendanceDBApi.findAllAutocomplete(
|
||||
req.query.query,
|
||||
req.query.limit,
|
||||
req.query.offset,
|
||||
globalAccess, schoolId,
|
||||
globalAccess, organizationId,
|
||||
);
|
||||
|
||||
res.status(200).send(payload);
|
||||
@ -428,7 +428,6 @@ router.get('/autocomplete', async (req, res) => {
|
||||
router.get('/:id', wrapAsync(async (req, res) => {
|
||||
const payload = await AttendanceDBApi.findBy(
|
||||
{ id: req.params.id },
|
||||
{ currentUser: req.currentUser },
|
||||
);
|
||||
|
||||
|
||||
|
||||
@ -3,9 +3,10 @@ const express = require('express');
|
||||
|
||||
const Book_loansService = require('../services/book_loans');
|
||||
const Book_loansDBApi = require('../db/api/book_loans');
|
||||
const { getCurrentUserSchoolId } = require('../db/api/schoolScope');
|
||||
const wrapAsync = require('../helpers').wrapAsync;
|
||||
|
||||
const config = require('../config');
|
||||
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
@ -305,7 +306,6 @@ router.get('/', wrapAsync(async (req, res) => {
|
||||
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
throw err;
|
||||
}
|
||||
} else {
|
||||
res.status(200).send(payload);
|
||||
@ -381,14 +381,14 @@ router.get('/autocomplete', async (req, res) => {
|
||||
|
||||
const globalAccess = req.currentUser.app_role.globalAccess;
|
||||
|
||||
const schoolId = getCurrentUserSchoolId(req.currentUser);
|
||||
const organizationId = req.currentUser.organization?.id
|
||||
|
||||
|
||||
const payload = await Book_loansDBApi.findAllAutocomplete(
|
||||
req.query.query,
|
||||
req.query.limit,
|
||||
req.query.offset,
|
||||
globalAccess, schoolId,
|
||||
globalAccess, organizationId,
|
||||
);
|
||||
|
||||
res.status(200).send(payload);
|
||||
@ -429,7 +429,6 @@ router.get('/autocomplete', async (req, res) => {
|
||||
router.get('/:id', wrapAsync(async (req, res) => {
|
||||
const payload = await Book_loansDBApi.findBy(
|
||||
{ id: req.params.id },
|
||||
{ currentUser: req.currentUser },
|
||||
);
|
||||
|
||||
|
||||
|
||||
@ -3,9 +3,10 @@ const express = require('express');
|
||||
|
||||
const BooksService = require('../services/books');
|
||||
const BooksDBApi = require('../db/api/books');
|
||||
const { getCurrentUserSchoolId } = require('../db/api/schoolScope');
|
||||
const wrapAsync = require('../helpers').wrapAsync;
|
||||
|
||||
const config = require('../config');
|
||||
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
@ -317,7 +318,6 @@ router.get('/', wrapAsync(async (req, res) => {
|
||||
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
throw err;
|
||||
}
|
||||
} else {
|
||||
res.status(200).send(payload);
|
||||
@ -393,14 +393,14 @@ router.get('/autocomplete', async (req, res) => {
|
||||
|
||||
const globalAccess = req.currentUser.app_role.globalAccess;
|
||||
|
||||
const schoolId = getCurrentUserSchoolId(req.currentUser);
|
||||
const organizationId = req.currentUser.organization?.id
|
||||
|
||||
|
||||
const payload = await BooksDBApi.findAllAutocomplete(
|
||||
req.query.query,
|
||||
req.query.limit,
|
||||
req.query.offset,
|
||||
globalAccess, schoolId,
|
||||
globalAccess, organizationId,
|
||||
);
|
||||
|
||||
res.status(200).send(payload);
|
||||
@ -441,7 +441,6 @@ router.get('/autocomplete', async (req, res) => {
|
||||
router.get('/:id', wrapAsync(async (req, res) => {
|
||||
const payload = await BooksDBApi.findBy(
|
||||
{ id: req.params.id },
|
||||
{ currentUser: req.currentUser },
|
||||
);
|
||||
|
||||
|
||||
|
||||
@ -3,9 +3,10 @@ const express = require('express');
|
||||
|
||||
const ClassesService = require('../services/classes');
|
||||
const ClassesDBApi = require('../db/api/classes');
|
||||
const { getCurrentUserSchoolId } = require('../db/api/schoolScope');
|
||||
const wrapAsync = require('../helpers').wrapAsync;
|
||||
|
||||
const config = require('../config');
|
||||
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
@ -312,7 +313,6 @@ router.get('/', wrapAsync(async (req, res) => {
|
||||
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
throw err;
|
||||
}
|
||||
} else {
|
||||
res.status(200).send(payload);
|
||||
@ -388,14 +388,14 @@ router.get('/autocomplete', async (req, res) => {
|
||||
|
||||
const globalAccess = req.currentUser.app_role.globalAccess;
|
||||
|
||||
const schoolId = getCurrentUserSchoolId(req.currentUser);
|
||||
const organizationId = req.currentUser.organization?.id
|
||||
|
||||
|
||||
const payload = await ClassesDBApi.findAllAutocomplete(
|
||||
req.query.query,
|
||||
req.query.limit,
|
||||
req.query.offset,
|
||||
globalAccess, schoolId,
|
||||
globalAccess, organizationId,
|
||||
);
|
||||
|
||||
res.status(200).send(payload);
|
||||
@ -436,7 +436,6 @@ router.get('/autocomplete', async (req, res) => {
|
||||
router.get('/:id', wrapAsync(async (req, res) => {
|
||||
const payload = await ClassesDBApi.findBy(
|
||||
{ id: req.params.id },
|
||||
{ currentUser: req.currentUser },
|
||||
);
|
||||
|
||||
|
||||
|
||||
@ -3,9 +3,10 @@ const express = require('express');
|
||||
|
||||
const CoursesService = require('../services/courses');
|
||||
const CoursesDBApi = require('../db/api/courses');
|
||||
const { getCurrentUserSchoolId } = require('../db/api/schoolScope');
|
||||
const wrapAsync = require('../helpers').wrapAsync;
|
||||
|
||||
const config = require('../config');
|
||||
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
@ -308,7 +309,6 @@ router.get('/', wrapAsync(async (req, res) => {
|
||||
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
throw err;
|
||||
}
|
||||
} else {
|
||||
res.status(200).send(payload);
|
||||
@ -384,14 +384,14 @@ router.get('/autocomplete', async (req, res) => {
|
||||
|
||||
const globalAccess = req.currentUser.app_role.globalAccess;
|
||||
|
||||
const schoolId = getCurrentUserSchoolId(req.currentUser);
|
||||
const organizationId = req.currentUser.organization?.id
|
||||
|
||||
|
||||
const payload = await CoursesDBApi.findAllAutocomplete(
|
||||
req.query.query,
|
||||
req.query.limit,
|
||||
req.query.offset,
|
||||
globalAccess, schoolId,
|
||||
globalAccess, organizationId,
|
||||
);
|
||||
|
||||
res.status(200).send(payload);
|
||||
@ -432,7 +432,6 @@ router.get('/autocomplete', async (req, res) => {
|
||||
router.get('/:id', wrapAsync(async (req, res) => {
|
||||
const payload = await CoursesDBApi.findBy(
|
||||
{ id: req.params.id },
|
||||
{ currentUser: req.currentUser },
|
||||
);
|
||||
|
||||
|
||||
|
||||
@ -3,9 +3,10 @@ const express = require('express');
|
||||
|
||||
const EmployeesService = require('../services/employees');
|
||||
const EmployeesDBApi = require('../db/api/employees');
|
||||
const { getCurrentUserSchoolId } = require('../db/api/schoolScope');
|
||||
const wrapAsync = require('../helpers').wrapAsync;
|
||||
|
||||
const config = require('../config');
|
||||
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
@ -314,7 +315,6 @@ router.get('/', wrapAsync(async (req, res) => {
|
||||
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
throw err;
|
||||
}
|
||||
} else {
|
||||
res.status(200).send(payload);
|
||||
@ -390,14 +390,14 @@ router.get('/autocomplete', async (req, res) => {
|
||||
|
||||
const globalAccess = req.currentUser.app_role.globalAccess;
|
||||
|
||||
const schoolId = getCurrentUserSchoolId(req.currentUser);
|
||||
const organizationId = req.currentUser.organization?.id
|
||||
|
||||
|
||||
const payload = await EmployeesDBApi.findAllAutocomplete(
|
||||
req.query.query,
|
||||
req.query.limit,
|
||||
req.query.offset,
|
||||
globalAccess, schoolId,
|
||||
globalAccess, organizationId,
|
||||
);
|
||||
|
||||
res.status(200).send(payload);
|
||||
@ -438,7 +438,6 @@ router.get('/autocomplete', async (req, res) => {
|
||||
router.get('/:id', wrapAsync(async (req, res) => {
|
||||
const payload = await EmployeesDBApi.findBy(
|
||||
{ id: req.params.id },
|
||||
{ currentUser: req.currentUser },
|
||||
);
|
||||
|
||||
|
||||
|
||||
@ -3,9 +3,10 @@ const express = require('express');
|
||||
|
||||
const EnrollmentsService = require('../services/enrollments');
|
||||
const EnrollmentsDBApi = require('../db/api/enrollments');
|
||||
const { getCurrentUserSchoolId } = require('../db/api/schoolScope');
|
||||
const wrapAsync = require('../helpers').wrapAsync;
|
||||
|
||||
const config = require('../config');
|
||||
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
@ -305,7 +306,6 @@ router.get('/', wrapAsync(async (req, res) => {
|
||||
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
throw err;
|
||||
}
|
||||
} else {
|
||||
res.status(200).send(payload);
|
||||
@ -381,14 +381,14 @@ router.get('/autocomplete', async (req, res) => {
|
||||
|
||||
const globalAccess = req.currentUser.app_role.globalAccess;
|
||||
|
||||
const schoolId = getCurrentUserSchoolId(req.currentUser);
|
||||
const organizationId = req.currentUser.organization?.id
|
||||
|
||||
|
||||
const payload = await EnrollmentsDBApi.findAllAutocomplete(
|
||||
req.query.query,
|
||||
req.query.limit,
|
||||
req.query.offset,
|
||||
globalAccess, schoolId,
|
||||
globalAccess, organizationId,
|
||||
);
|
||||
|
||||
res.status(200).send(payload);
|
||||
@ -429,7 +429,6 @@ router.get('/autocomplete', async (req, res) => {
|
||||
router.get('/:id', wrapAsync(async (req, res) => {
|
||||
const payload = await EnrollmentsDBApi.findBy(
|
||||
{ id: req.params.id },
|
||||
{ currentUser: req.currentUser },
|
||||
);
|
||||
|
||||
|
||||
|
||||
@ -1,24 +1,21 @@
|
||||
const express = require('express');
|
||||
const config = require('../config');
|
||||
const path = require('path');
|
||||
const passport = require('passport');
|
||||
const services = require('../services/file');
|
||||
const wrapAsync = require('../helpers').wrapAsync;
|
||||
const router = express.Router();
|
||||
|
||||
router.get('/download', wrapAsync(async (req, res) => {
|
||||
router.get('/download', (req, res) => {
|
||||
if (process.env.NODE_ENV == "production" || process.env.NEXT_PUBLIC_BACK_API) {
|
||||
await services.downloadGCloud(req, res);
|
||||
services.downloadGCloud(req, res);
|
||||
}
|
||||
else {
|
||||
await services.downloadLocal(req, res);
|
||||
services.downloadLocal(req, res);
|
||||
}
|
||||
}));
|
||||
});
|
||||
|
||||
router.post('/upload/:table/:field', passport.authenticate('jwt', {session: false}), (req, res) => {
|
||||
const fileName = services.normalizeFolder(`${req.params.table}/${req.params.field}`);
|
||||
|
||||
if (!fileName) {
|
||||
return res.status(400).send({ message: 'Invalid upload path.' });
|
||||
}
|
||||
const fileName = `${req.params.table}/${req.params.field}`;
|
||||
|
||||
if (process.env.NODE_ENV == "production" || process.env.NEXT_PUBLIC_BACK_API) {
|
||||
services.uploadGCloud(fileName, req, res);
|
||||
@ -32,7 +29,4 @@ router.post('/upload/:table/:field', passport.authenticate('jwt', {session: fals
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
router.use('/', require('../helpers').commonErrorHandler);
|
||||
|
||||
module.exports = router;
|
||||
|
||||
@ -3,9 +3,10 @@ const express = require('express');
|
||||
|
||||
const GradesService = require('../services/grades');
|
||||
const GradesDBApi = require('../db/api/grades');
|
||||
const { getCurrentUserSchoolId } = require('../db/api/schoolScope');
|
||||
const wrapAsync = require('../helpers').wrapAsync;
|
||||
|
||||
const config = require('../config');
|
||||
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
@ -308,7 +309,6 @@ router.get('/', wrapAsync(async (req, res) => {
|
||||
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
throw err;
|
||||
}
|
||||
} else {
|
||||
res.status(200).send(payload);
|
||||
@ -384,14 +384,14 @@ router.get('/autocomplete', async (req, res) => {
|
||||
|
||||
const globalAccess = req.currentUser.app_role.globalAccess;
|
||||
|
||||
const schoolId = getCurrentUserSchoolId(req.currentUser);
|
||||
const organizationId = req.currentUser.organization?.id
|
||||
|
||||
|
||||
const payload = await GradesDBApi.findAllAutocomplete(
|
||||
req.query.query,
|
||||
req.query.limit,
|
||||
req.query.offset,
|
||||
globalAccess, schoolId,
|
||||
globalAccess, organizationId,
|
||||
);
|
||||
|
||||
res.status(200).send(payload);
|
||||
@ -432,7 +432,6 @@ router.get('/autocomplete', async (req, res) => {
|
||||
router.get('/:id', wrapAsync(async (req, res) => {
|
||||
const payload = await GradesDBApi.findBy(
|
||||
{ id: req.params.id },
|
||||
{ currentUser: req.currentUser },
|
||||
);
|
||||
|
||||
|
||||
|
||||
@ -3,9 +3,10 @@ const express = require('express');
|
||||
|
||||
const GuardiansService = require('../services/guardians');
|
||||
const GuardiansDBApi = require('../db/api/guardians');
|
||||
const { getCurrentUserSchoolId } = require('../db/api/schoolScope');
|
||||
const wrapAsync = require('../helpers').wrapAsync;
|
||||
|
||||
const config = require('../config');
|
||||
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
@ -311,7 +312,6 @@ router.get('/', wrapAsync(async (req, res) => {
|
||||
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
throw err;
|
||||
}
|
||||
} else {
|
||||
res.status(200).send(payload);
|
||||
@ -387,14 +387,14 @@ router.get('/autocomplete', async (req, res) => {
|
||||
|
||||
const globalAccess = req.currentUser.app_role.globalAccess;
|
||||
|
||||
const schoolId = getCurrentUserSchoolId(req.currentUser);
|
||||
const organizationId = req.currentUser.organization?.id
|
||||
|
||||
|
||||
const payload = await GuardiansDBApi.findAllAutocomplete(
|
||||
req.query.query,
|
||||
req.query.limit,
|
||||
req.query.offset,
|
||||
globalAccess, schoolId,
|
||||
globalAccess, organizationId,
|
||||
);
|
||||
|
||||
res.status(200).send(payload);
|
||||
@ -435,7 +435,6 @@ router.get('/autocomplete', async (req, res) => {
|
||||
router.get('/:id', wrapAsync(async (req, res) => {
|
||||
const payload = await GuardiansDBApi.findBy(
|
||||
{ id: req.params.id },
|
||||
{ currentUser: req.currentUser },
|
||||
);
|
||||
|
||||
|
||||
|
||||
@ -3,9 +3,10 @@ const express = require('express');
|
||||
|
||||
const InvoicesService = require('../services/invoices');
|
||||
const InvoicesDBApi = require('../db/api/invoices');
|
||||
const { getCurrentUserSchoolId } = require('../db/api/schoolScope');
|
||||
const wrapAsync = require('../helpers').wrapAsync;
|
||||
|
||||
const config = require('../config');
|
||||
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
@ -311,7 +312,6 @@ router.get('/', wrapAsync(async (req, res) => {
|
||||
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
throw err;
|
||||
}
|
||||
} else {
|
||||
res.status(200).send(payload);
|
||||
@ -387,14 +387,14 @@ router.get('/autocomplete', async (req, res) => {
|
||||
|
||||
const globalAccess = req.currentUser.app_role.globalAccess;
|
||||
|
||||
const schoolId = getCurrentUserSchoolId(req.currentUser);
|
||||
const organizationId = req.currentUser.organization?.id
|
||||
|
||||
|
||||
const payload = await InvoicesDBApi.findAllAutocomplete(
|
||||
req.query.query,
|
||||
req.query.limit,
|
||||
req.query.offset,
|
||||
globalAccess, schoolId,
|
||||
globalAccess, organizationId,
|
||||
);
|
||||
|
||||
res.status(200).send(payload);
|
||||
@ -435,7 +435,6 @@ router.get('/autocomplete', async (req, res) => {
|
||||
router.get('/:id', wrapAsync(async (req, res) => {
|
||||
const payload = await InvoicesDBApi.findBy(
|
||||
{ id: req.params.id },
|
||||
{ currentUser: req.currentUser },
|
||||
);
|
||||
|
||||
|
||||
|
||||
@ -5,109 +5,6 @@ const router = express.Router();
|
||||
const sjs = require('sequelize-json-schema');
|
||||
const { getWidget, askGpt } = require('../services/openai');
|
||||
const { LocalAIApi } = require('../ai/LocalAIApi');
|
||||
const ForbiddenError = require('../services/notifications/errors/forbidden');
|
||||
const { checkPermissions } = require('../middlewares/check-permissions');
|
||||
|
||||
|
||||
const WIDGET_CUSTOMIZATION_KEY = 'widgets';
|
||||
const UUID_REGEX = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
|
||||
|
||||
function badRequest(message) {
|
||||
const error = new Error(message);
|
||||
error.code = 400;
|
||||
return error;
|
||||
}
|
||||
|
||||
function normalizeWidgetKey(key) {
|
||||
if (key !== WIDGET_CUSTOMIZATION_KEY) {
|
||||
throw badRequest('Invalid role customization key.');
|
||||
}
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
function normalizeUuid(value, label) {
|
||||
if (typeof value !== 'string' || !UUID_REGEX.test(value)) {
|
||||
throw badRequest(`${label} must be a valid UUID.`);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
function normalizeText(value, label, maxLength) {
|
||||
if (typeof value !== 'string' || !value.trim()) {
|
||||
throw badRequest(`${label} is required.`);
|
||||
}
|
||||
|
||||
const normalized = value.trim();
|
||||
if (normalized.length > maxLength) {
|
||||
throw badRequest(`${label} is too long. Maximum length is ${maxLength} characters.`);
|
||||
}
|
||||
|
||||
return normalized;
|
||||
}
|
||||
|
||||
function normalizeNumberOption(value, defaultValue, minValue, maxValue) {
|
||||
const parsed = Number(value);
|
||||
if (!Number.isFinite(parsed)) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
return Math.min(Math.max(parsed, minValue), maxValue);
|
||||
}
|
||||
|
||||
function normalizeAiProxyOptions(options = {}) {
|
||||
return {
|
||||
poll_interval: normalizeNumberOption(options.poll_interval, 5, 1, 30),
|
||||
poll_timeout: normalizeNumberOption(options.poll_timeout, 300, 5, 300),
|
||||
timeout: normalizeNumberOption(options.timeout, 30, 5, 60),
|
||||
};
|
||||
}
|
||||
|
||||
function assertGlobalRoleAccess(currentUser) {
|
||||
if (!currentUser?.app_role?.globalAccess) {
|
||||
throw new ForbiddenError('auth.forbidden');
|
||||
}
|
||||
}
|
||||
|
||||
async function hasRolePermission(currentUser, permission) {
|
||||
if (!currentUser) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const customPermissions = Array.isArray(currentUser.custom_permissions)
|
||||
? currentUser.custom_permissions
|
||||
: [];
|
||||
|
||||
if (customPermissions.find((cp) => cp.name === permission)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!currentUser.app_role) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const permissions = typeof currentUser.app_role.getPermissions === 'function'
|
||||
? await currentUser.app_role.getPermissions()
|
||||
: currentUser.app_role.permissions || [];
|
||||
|
||||
return !!permissions.find((p) => p.name === permission);
|
||||
}
|
||||
|
||||
async function assertCanReadRoleWidgets(roleId, currentUser) {
|
||||
if (roleId === currentUser?.app_role?.id) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(await hasRolePermission(currentUser, 'READ_ROLES'))) {
|
||||
throw new ForbiddenError('auth.forbidden');
|
||||
}
|
||||
}
|
||||
|
||||
async function canGenerateRoleWidgets(currentUser) {
|
||||
return !!currentUser?.app_role?.globalAccess
|
||||
&& await hasRolePermission(currentUser, 'UPDATE_ROLES');
|
||||
}
|
||||
|
||||
const loadRolesModules = () => {
|
||||
try {
|
||||
@ -174,17 +71,12 @@ const loadRolesModules = () => {
|
||||
|
||||
router.delete(
|
||||
'/roles-info/:infoId',
|
||||
checkPermissions('UPDATE_ROLES'),
|
||||
wrapAsync(async (req, res) => {
|
||||
assertGlobalRoleAccess(req.currentUser);
|
||||
const { RolesService } = loadRolesModules();
|
||||
const key = normalizeWidgetKey(req.query.key);
|
||||
const infoId = normalizeUuid(req.query.infoId || req.params.infoId, 'Widget ID');
|
||||
const roleId = normalizeUuid(req.query.roleId, 'Role ID');
|
||||
const role = await RolesService.removeRoleInfoById(
|
||||
infoId,
|
||||
roleId,
|
||||
key,
|
||||
req.query.infoId,
|
||||
req.query.roleId,
|
||||
req.query.key,
|
||||
req.currentUser,
|
||||
);
|
||||
|
||||
@ -239,46 +131,33 @@ router.get(
|
||||
'/info-by-key',
|
||||
wrapAsync(async (req, res) => {
|
||||
const { RolesService, RolesDBApi } = loadRolesModules();
|
||||
const roleId = req.query.roleId;
|
||||
const key = req.query.key;
|
||||
const currentUser = req.currentUser;
|
||||
const key = normalizeWidgetKey(req.query.key);
|
||||
const roleId = normalizeUuid(req.query.roleId || currentUser?.app_role?.id, 'Role ID');
|
||||
|
||||
await assertCanReadRoleWidgets(roleId, currentUser);
|
||||
|
||||
const role = await RolesDBApi.findBy({ id: roleId });
|
||||
if (!role) {
|
||||
return res.status(404).send('Role not found');
|
||||
}
|
||||
|
||||
let info = await RolesService.getRoleInfoByKey(
|
||||
key,
|
||||
roleId,
|
||||
currentUser,
|
||||
);
|
||||
|
||||
if (!role.role_customization && await canGenerateRoleWidgets(currentUser)) {
|
||||
const schema = await sjs.getSequelizeSchema(db.sequelize, {});
|
||||
const widgetResults = await Promise.allSettled(['pie', 'bar'].map(async (chartType) => {
|
||||
const role = await RolesDBApi.findBy({ id: roleId });
|
||||
if (!role?.role_customization) {
|
||||
await Promise.all(["pie", "bar"].map(async (e) => {
|
||||
const schema = await sjs.getSequelizeSchema(db.sequelize, {});
|
||||
const payload = {
|
||||
description: `Create some cool ${chartType} chart`,
|
||||
description: `Create some cool ${e} chart`,
|
||||
modelDefinition: schema.definitions,
|
||||
};
|
||||
const widgetId = await getWidget(payload);
|
||||
if (typeof widgetId === 'string') {
|
||||
const widgetId = await getWidget(payload, currentUser?.id, roleId);
|
||||
if (widgetId) {
|
||||
await RolesService.addRoleInfo(
|
||||
roleId,
|
||||
currentUser?.id,
|
||||
WIDGET_CUSTOMIZATION_KEY,
|
||||
'widgets',
|
||||
widgetId,
|
||||
req.currentUser,
|
||||
);
|
||||
}
|
||||
}));
|
||||
|
||||
widgetResults
|
||||
.filter((result) => result.status === 'rejected')
|
||||
.forEach((result) => console.error('Default widget creation failed:', result.reason));
|
||||
|
||||
}))
|
||||
info = await RolesService.getRoleInfoByKey(
|
||||
key,
|
||||
roleId,
|
||||
@ -291,13 +170,9 @@ router.get(
|
||||
|
||||
router.post(
|
||||
'/create_widget',
|
||||
checkPermissions('UPDATE_ROLES'),
|
||||
wrapAsync(async (req, res) => {
|
||||
assertGlobalRoleAccess(req.currentUser);
|
||||
const { RolesService } = loadRolesModules();
|
||||
const { userId } = req.body || {};
|
||||
const description = normalizeText(req.body?.description, 'Description', 1000);
|
||||
const roleId = normalizeUuid(req.body?.roleId, 'Role ID');
|
||||
const { description, userId, roleId } = req.body;
|
||||
|
||||
const currentUser = req.currentUser;
|
||||
const schema = await sjs.getSequelizeSchema(db.sequelize, {});
|
||||
@ -306,13 +181,13 @@ router.post(
|
||||
modelDefinition: schema.definitions,
|
||||
};
|
||||
|
||||
const widgetId = await getWidget(payload);
|
||||
const widgetId = await getWidget(payload, userId, roleId);
|
||||
|
||||
if (typeof widgetId === 'string') {
|
||||
if (widgetId) {
|
||||
await RolesService.addRoleInfo(
|
||||
roleId,
|
||||
userId,
|
||||
WIDGET_CUSTOMIZATION_KEY,
|
||||
'widgets',
|
||||
widgetId,
|
||||
currentUser,
|
||||
);
|
||||
@ -372,10 +247,9 @@ router.post(
|
||||
'/response',
|
||||
wrapAsync(async (req, res) => {
|
||||
const body = req.body || {};
|
||||
const options = normalizeAiProxyOptions(body.options || {});
|
||||
const options = body.options || {};
|
||||
const payload = { ...body };
|
||||
delete payload.options;
|
||||
delete payload.project_uuid;
|
||||
|
||||
const response = await LocalAIApi.createResponse(payload, options);
|
||||
|
||||
@ -432,7 +306,13 @@ router.post(
|
||||
router.post(
|
||||
'/ask-gpt',
|
||||
wrapAsync(async (req, res) => {
|
||||
const prompt = normalizeText(req.body?.prompt, 'Prompt', 4000);
|
||||
const { prompt } = req.body;
|
||||
if (!prompt) {
|
||||
return res.status(400).send({
|
||||
success: false,
|
||||
error: 'Prompt is required',
|
||||
});
|
||||
}
|
||||
|
||||
const response = await askGpt(prompt);
|
||||
|
||||
@ -445,6 +325,4 @@ router.post(
|
||||
);
|
||||
|
||||
|
||||
router.use('/', require('../helpers').commonErrorHandler);
|
||||
|
||||
module.exports = router;
|
||||
|
||||
@ -3,9 +3,10 @@ const express = require('express');
|
||||
|
||||
const PaymentsService = require('../services/payments');
|
||||
const PaymentsDBApi = require('../db/api/payments');
|
||||
const { getCurrentUserSchoolId } = require('../db/api/schoolScope');
|
||||
const wrapAsync = require('../helpers').wrapAsync;
|
||||
|
||||
const config = require('../config');
|
||||
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
@ -308,7 +309,6 @@ router.get('/', wrapAsync(async (req, res) => {
|
||||
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
throw err;
|
||||
}
|
||||
} else {
|
||||
res.status(200).send(payload);
|
||||
@ -384,14 +384,14 @@ router.get('/autocomplete', async (req, res) => {
|
||||
|
||||
const globalAccess = req.currentUser.app_role.globalAccess;
|
||||
|
||||
const schoolId = getCurrentUserSchoolId(req.currentUser);
|
||||
const organizationId = req.currentUser.organization?.id
|
||||
|
||||
|
||||
const payload = await PaymentsDBApi.findAllAutocomplete(
|
||||
req.query.query,
|
||||
req.query.limit,
|
||||
req.query.offset,
|
||||
globalAccess, schoolId,
|
||||
globalAccess, organizationId,
|
||||
);
|
||||
|
||||
res.status(200).send(payload);
|
||||
@ -432,7 +432,6 @@ router.get('/autocomplete', async (req, res) => {
|
||||
router.get('/:id', wrapAsync(async (req, res) => {
|
||||
const payload = await PaymentsDBApi.findBy(
|
||||
{ id: req.params.id },
|
||||
{ currentUser: req.currentUser },
|
||||
);
|
||||
|
||||
|
||||
|
||||
@ -301,7 +301,6 @@ router.get('/', wrapAsync(async (req, res) => {
|
||||
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
throw err;
|
||||
}
|
||||
} else {
|
||||
res.status(200).send(payload);
|
||||
|
||||
@ -3,9 +3,10 @@ const express = require('express');
|
||||
|
||||
const ProductsService = require('../services/products');
|
||||
const ProductsDBApi = require('../db/api/products');
|
||||
const { getCurrentUserSchoolId } = require('../db/api/schoolScope');
|
||||
const wrapAsync = require('../helpers').wrapAsync;
|
||||
|
||||
const config = require('../config');
|
||||
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
@ -314,7 +315,6 @@ router.get('/', wrapAsync(async (req, res) => {
|
||||
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
throw err;
|
||||
}
|
||||
} else {
|
||||
res.status(200).send(payload);
|
||||
@ -390,14 +390,14 @@ router.get('/autocomplete', async (req, res) => {
|
||||
|
||||
const globalAccess = req.currentUser.app_role.globalAccess;
|
||||
|
||||
const schoolId = getCurrentUserSchoolId(req.currentUser);
|
||||
const organizationId = req.currentUser.organization?.id
|
||||
|
||||
|
||||
const payload = await ProductsDBApi.findAllAutocomplete(
|
||||
req.query.query,
|
||||
req.query.limit,
|
||||
req.query.offset,
|
||||
globalAccess, schoolId,
|
||||
globalAccess, organizationId,
|
||||
);
|
||||
|
||||
res.status(200).send(payload);
|
||||
@ -438,7 +438,6 @@ router.get('/autocomplete', async (req, res) => {
|
||||
router.get('/:id', wrapAsync(async (req, res) => {
|
||||
const payload = await ProductsDBApi.findBy(
|
||||
{ id: req.params.id },
|
||||
{ currentUser: req.currentUser },
|
||||
);
|
||||
|
||||
|
||||
|
||||
@ -5,6 +5,8 @@ const RolesService = require('../services/roles');
|
||||
const RolesDBApi = require('../db/api/roles');
|
||||
const wrapAsync = require('../helpers').wrapAsync;
|
||||
|
||||
const config = require('../config');
|
||||
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
@ -303,7 +305,6 @@ router.get('/', wrapAsync(async (req, res) => {
|
||||
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
throw err;
|
||||
}
|
||||
} else {
|
||||
res.status(200).send(payload);
|
||||
|
||||
@ -3,9 +3,10 @@ const express = require('express');
|
||||
|
||||
const SchoolsService = require('../services/schools');
|
||||
const SchoolsDBApi = require('../db/api/schools');
|
||||
const { getCurrentUserSchoolId } = require('../db/api/schoolScope');
|
||||
const wrapAsync = require('../helpers').wrapAsync;
|
||||
|
||||
const config = require('../config');
|
||||
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
@ -30,22 +31,9 @@ router.use(checkCrudPermissions('schools'));
|
||||
* name:
|
||||
* type: string
|
||||
* default: name
|
||||
* nif:
|
||||
* type: string
|
||||
* phone:
|
||||
* type: string
|
||||
* email:
|
||||
* type: string
|
||||
* province:
|
||||
* type: string
|
||||
* municipality:
|
||||
* type: string
|
||||
* address:
|
||||
* type: string
|
||||
* logoUrl:
|
||||
* type: string
|
||||
* status:
|
||||
* type: string
|
||||
|
||||
|
||||
|
||||
*/
|
||||
|
||||
/**
|
||||
@ -304,7 +292,11 @@ router.get('/', wrapAsync(async (req, res) => {
|
||||
req.query, globalAccess, { currentUser }
|
||||
);
|
||||
if (filetype && filetype === 'csv') {
|
||||
const fields = ['id','name','nif','phone','email','province','municipality','address','logoUrl','status'];
|
||||
const fields = ['id','name',
|
||||
|
||||
|
||||
|
||||
];
|
||||
const opts = { fields };
|
||||
try {
|
||||
const csv = parse(payload.rows, opts);
|
||||
@ -313,7 +305,6 @@ router.get('/', wrapAsync(async (req, res) => {
|
||||
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
throw err;
|
||||
}
|
||||
} else {
|
||||
res.status(200).send(payload);
|
||||
@ -389,14 +380,14 @@ router.get('/autocomplete', async (req, res) => {
|
||||
|
||||
const globalAccess = req.currentUser.app_role.globalAccess;
|
||||
|
||||
const schoolId = getCurrentUserSchoolId(req.currentUser);
|
||||
const organizationId = req.currentUser.organization?.id
|
||||
|
||||
|
||||
const payload = await SchoolsDBApi.findAllAutocomplete(
|
||||
req.query.query,
|
||||
req.query.limit,
|
||||
req.query.offset,
|
||||
globalAccess, schoolId,
|
||||
globalAccess, organizationId,
|
||||
);
|
||||
|
||||
res.status(200).send(payload);
|
||||
@ -437,7 +428,6 @@ router.get('/autocomplete', async (req, res) => {
|
||||
router.get('/:id', wrapAsync(async (req, res) => {
|
||||
const payload = await SchoolsDBApi.findBy(
|
||||
{ id: req.params.id },
|
||||
{ currentUser: req.currentUser },
|
||||
);
|
||||
|
||||
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
const express = require('express');
|
||||
const SearchService = require('../services/search');
|
||||
const wrapAsync = require('../helpers').wrapAsync;
|
||||
|
||||
const config = require('../config');
|
||||
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
@ -33,14 +35,22 @@ router.use(checkCrudPermissions('search'));
|
||||
* description: Internal server error
|
||||
*/
|
||||
|
||||
router.post('/', wrapAsync(async (req, res) => {
|
||||
const { searchQuery } = req.body || {};
|
||||
const globalAccess = !!req.currentUser?.app_role?.globalAccess;
|
||||
router.post('/', async (req, res) => {
|
||||
const { searchQuery , organizationId} = req.body;
|
||||
|
||||
const globalAccess = req.currentUser.app_role.globalAccess;
|
||||
|
||||
if (!searchQuery) {
|
||||
return res.status(400).json({ error: 'Please enter a search query' });
|
||||
}
|
||||
|
||||
try {
|
||||
const foundMatches = await SearchService.search(searchQuery, req.currentUser , organizationId, globalAccess,);
|
||||
res.json(foundMatches);
|
||||
} catch (error) {
|
||||
console.error('Internal Server Error', error);
|
||||
res.status(500).json({ error: 'Internal Server Error' });
|
||||
}
|
||||
});
|
||||
|
||||
const foundMatches = await SearchService.search(searchQuery, req.currentUser, globalAccess);
|
||||
res.json(foundMatches);
|
||||
}));
|
||||
|
||||
router.use('/', require('../helpers').commonErrorHandler);
|
||||
|
||||
module.exports = router;
|
||||
module.exports = router;
|
||||
@ -1,7 +1,6 @@
|
||||
const express = require('express');
|
||||
const db = require('../db/models');
|
||||
const wrapAsync = require('../helpers').wrapAsync;
|
||||
const ForbiddenError = require('../services/notifications/errors/forbidden');
|
||||
const { executeReadOnlySelect } = require('../services/sqlSafety');
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
@ -31,27 +30,32 @@ const router = express.Router();
|
||||
* description: Invalid SQL
|
||||
* 401:
|
||||
* $ref: "#/components/responses/UnauthorizedError"
|
||||
* 403:
|
||||
* description: Requires global access
|
||||
* 500:
|
||||
* description: Internal server error
|
||||
*/
|
||||
router.post(
|
||||
'/',
|
||||
wrapAsync(async (req, res) => {
|
||||
if (!req.currentUser?.app_role?.globalAccess) {
|
||||
throw new ForbiddenError('auth.forbidden');
|
||||
const { sql } = req.body;
|
||||
if (typeof sql !== 'string' || !sql.trim()) {
|
||||
return res.status(400).json({ error: 'SQL is required' });
|
||||
}
|
||||
|
||||
const body = req.body || {};
|
||||
const rows = await executeReadOnlySelect(body.sql, {
|
||||
limit: body.limit,
|
||||
const normalized = sql.trim().replace(/;+\s*$/, '');
|
||||
if (!/^select\b/i.test(normalized)) {
|
||||
return res.status(400).json({ error: 'Only SELECT statements are allowed' });
|
||||
}
|
||||
|
||||
if (normalized.includes(';')) {
|
||||
return res.status(400).json({ error: 'Only a single SELECT statement is allowed' });
|
||||
}
|
||||
|
||||
const rows = await db.sequelize.query(normalized, {
|
||||
type: db.Sequelize.QueryTypes.SELECT,
|
||||
});
|
||||
|
||||
return res.status(200).json({ rows });
|
||||
}),
|
||||
);
|
||||
|
||||
router.use('/', require('../helpers').commonErrorHandler);
|
||||
|
||||
module.exports = router;
|
||||
|
||||
@ -3,9 +3,10 @@ const express = require('express');
|
||||
|
||||
const Student_guardiansService = require('../services/student_guardians');
|
||||
const Student_guardiansDBApi = require('../db/api/student_guardians');
|
||||
const { getCurrentUserSchoolId } = require('../db/api/schoolScope');
|
||||
const wrapAsync = require('../helpers').wrapAsync;
|
||||
|
||||
const config = require('../config');
|
||||
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
@ -302,7 +303,6 @@ router.get('/', wrapAsync(async (req, res) => {
|
||||
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
throw err;
|
||||
}
|
||||
} else {
|
||||
res.status(200).send(payload);
|
||||
@ -378,14 +378,14 @@ router.get('/autocomplete', async (req, res) => {
|
||||
|
||||
const globalAccess = req.currentUser.app_role.globalAccess;
|
||||
|
||||
const schoolId = getCurrentUserSchoolId(req.currentUser);
|
||||
const organizationId = req.currentUser.organization?.id
|
||||
|
||||
|
||||
const payload = await Student_guardiansDBApi.findAllAutocomplete(
|
||||
req.query.query,
|
||||
req.query.limit,
|
||||
req.query.offset,
|
||||
globalAccess, schoolId,
|
||||
globalAccess, organizationId,
|
||||
);
|
||||
|
||||
res.status(200).send(payload);
|
||||
@ -426,7 +426,6 @@ router.get('/autocomplete', async (req, res) => {
|
||||
router.get('/:id', wrapAsync(async (req, res) => {
|
||||
const payload = await Student_guardiansDBApi.findBy(
|
||||
{ id: req.params.id },
|
||||
{ currentUser: req.currentUser },
|
||||
);
|
||||
|
||||
|
||||
|
||||
@ -3,9 +3,10 @@ const express = require('express');
|
||||
|
||||
const StudentsService = require('../services/students');
|
||||
const StudentsDBApi = require('../db/api/students');
|
||||
const { getCurrentUserSchoolId } = require('../db/api/schoolScope');
|
||||
const wrapAsync = require('../helpers').wrapAsync;
|
||||
|
||||
const config = require('../config');
|
||||
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
@ -321,7 +322,6 @@ router.get('/', wrapAsync(async (req, res) => {
|
||||
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
throw err;
|
||||
}
|
||||
} else {
|
||||
res.status(200).send(payload);
|
||||
@ -397,14 +397,14 @@ router.get('/autocomplete', async (req, res) => {
|
||||
|
||||
const globalAccess = req.currentUser.app_role.globalAccess;
|
||||
|
||||
const schoolId = getCurrentUserSchoolId(req.currentUser);
|
||||
const organizationId = req.currentUser.organization?.id
|
||||
|
||||
|
||||
const payload = await StudentsDBApi.findAllAutocomplete(
|
||||
req.query.query,
|
||||
req.query.limit,
|
||||
req.query.offset,
|
||||
globalAccess, schoolId,
|
||||
globalAccess, organizationId,
|
||||
);
|
||||
|
||||
res.status(200).send(payload);
|
||||
@ -445,7 +445,6 @@ router.get('/autocomplete', async (req, res) => {
|
||||
router.get('/:id', wrapAsync(async (req, res) => {
|
||||
const payload = await StudentsDBApi.findBy(
|
||||
{ id: req.params.id },
|
||||
{ currentUser: req.currentUser },
|
||||
);
|
||||
|
||||
|
||||
|
||||
@ -3,9 +3,10 @@ const express = require('express');
|
||||
|
||||
const SubjectsService = require('../services/subjects');
|
||||
const SubjectsDBApi = require('../db/api/subjects');
|
||||
const { getCurrentUserSchoolId } = require('../db/api/schoolScope');
|
||||
const wrapAsync = require('../helpers').wrapAsync;
|
||||
|
||||
const config = require('../config');
|
||||
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
@ -305,7 +306,6 @@ router.get('/', wrapAsync(async (req, res) => {
|
||||
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
throw err;
|
||||
}
|
||||
} else {
|
||||
res.status(200).send(payload);
|
||||
@ -381,14 +381,14 @@ router.get('/autocomplete', async (req, res) => {
|
||||
|
||||
const globalAccess = req.currentUser.app_role.globalAccess;
|
||||
|
||||
const schoolId = getCurrentUserSchoolId(req.currentUser);
|
||||
const organizationId = req.currentUser.organization?.id
|
||||
|
||||
|
||||
const payload = await SubjectsDBApi.findAllAutocomplete(
|
||||
req.query.query,
|
||||
req.query.limit,
|
||||
req.query.offset,
|
||||
globalAccess, schoolId,
|
||||
globalAccess, organizationId,
|
||||
);
|
||||
|
||||
res.status(200).send(payload);
|
||||
@ -429,7 +429,6 @@ router.get('/autocomplete', async (req, res) => {
|
||||
router.get('/:id', wrapAsync(async (req, res) => {
|
||||
const payload = await SubjectsDBApi.findBy(
|
||||
{ id: req.params.id },
|
||||
{ currentUser: req.currentUser },
|
||||
);
|
||||
|
||||
|
||||
|
||||
@ -3,9 +3,10 @@ const express = require('express');
|
||||
|
||||
const TeachersService = require('../services/teachers');
|
||||
const TeachersDBApi = require('../db/api/teachers');
|
||||
const { getCurrentUserSchoolId } = require('../db/api/schoolScope');
|
||||
const wrapAsync = require('../helpers').wrapAsync;
|
||||
|
||||
const config = require('../config');
|
||||
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
@ -314,7 +315,6 @@ router.get('/', wrapAsync(async (req, res) => {
|
||||
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
throw err;
|
||||
}
|
||||
} else {
|
||||
res.status(200).send(payload);
|
||||
@ -390,14 +390,14 @@ router.get('/autocomplete', async (req, res) => {
|
||||
|
||||
const globalAccess = req.currentUser.app_role.globalAccess;
|
||||
|
||||
const schoolId = getCurrentUserSchoolId(req.currentUser);
|
||||
const organizationId = req.currentUser.organization?.id
|
||||
|
||||
|
||||
const payload = await TeachersDBApi.findAllAutocomplete(
|
||||
req.query.query,
|
||||
req.query.limit,
|
||||
req.query.offset,
|
||||
globalAccess, schoolId,
|
||||
globalAccess, organizationId,
|
||||
);
|
||||
|
||||
res.status(200).send(payload);
|
||||
@ -438,7 +438,6 @@ router.get('/autocomplete', async (req, res) => {
|
||||
router.get('/:id', wrapAsync(async (req, res) => {
|
||||
const payload = await TeachersDBApi.findBy(
|
||||
{ id: req.params.id },
|
||||
{ currentUser: req.currentUser },
|
||||
);
|
||||
|
||||
|
||||
|
||||
@ -3,9 +3,10 @@ const express = require('express');
|
||||
|
||||
const UsersService = require('../services/users');
|
||||
const UsersDBApi = require('../db/api/users');
|
||||
const { getCurrentUserSchoolId } = require('../db/api/schoolScope');
|
||||
const wrapAsync = require('../helpers').wrapAsync;
|
||||
|
||||
const config = require('../config');
|
||||
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
@ -313,7 +314,6 @@ router.get('/', wrapAsync(async (req, res) => {
|
||||
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
throw err;
|
||||
}
|
||||
} else {
|
||||
res.status(200).send(payload);
|
||||
@ -389,14 +389,14 @@ router.get('/autocomplete', async (req, res) => {
|
||||
|
||||
const globalAccess = req.currentUser.app_role.globalAccess;
|
||||
|
||||
const schoolId = getCurrentUserSchoolId(req.currentUser);
|
||||
const organizationId = req.currentUser.organization?.id
|
||||
|
||||
|
||||
const payload = await UsersDBApi.findAllAutocomplete(
|
||||
req.query.query,
|
||||
req.query.limit,
|
||||
req.query.offset,
|
||||
globalAccess, schoolId,
|
||||
globalAccess, organizationId,
|
||||
);
|
||||
|
||||
res.status(200).send(payload);
|
||||
@ -437,7 +437,6 @@ router.get('/autocomplete', async (req, res) => {
|
||||
router.get('/:id', wrapAsync(async (req, res) => {
|
||||
const payload = await UsersDBApi.findBy(
|
||||
{ id: req.params.id },
|
||||
{ currentUser: req.currentUser },
|
||||
);
|
||||
|
||||
|
||||
|
||||
@ -1,7 +1,11 @@
|
||||
const db = require('../db/models');
|
||||
const AssessmentsDBApi = require('../db/api/assessments');
|
||||
const parseImportFile = require('./importFileParser');
|
||||
const processFile = require("../middlewares/upload");
|
||||
const ValidationError = require('./notifications/errors/validation');
|
||||
const csv = require('csv-parser');
|
||||
const axios = require('axios');
|
||||
const config = require('../config');
|
||||
const stream = require('stream');
|
||||
|
||||
|
||||
|
||||
@ -24,13 +28,28 @@ module.exports = class AssessmentsService {
|
||||
await transaction.rollback();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static async bulkImport(req, res) {
|
||||
static async bulkImport(req, res, sendInvitationEmails = true, host) {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
|
||||
try {
|
||||
const results = await parseImportFile(req, res);
|
||||
await processFile(req, res);
|
||||
const bufferStream = new stream.PassThrough();
|
||||
const results = [];
|
||||
|
||||
await bufferStream.end(Buffer.from(req.file.buffer, "utf-8")); // convert Buffer to Stream
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
bufferStream
|
||||
.pipe(csv())
|
||||
.on('data', (data) => results.push(data))
|
||||
.on('end', async () => {
|
||||
console.log('CSV results', results);
|
||||
resolve();
|
||||
})
|
||||
.on('error', (error) => reject(error));
|
||||
})
|
||||
|
||||
await AssessmentsDBApi.bulkImport(results, {
|
||||
transaction,
|
||||
@ -51,7 +70,7 @@ module.exports = class AssessmentsService {
|
||||
try {
|
||||
let assessments = await AssessmentsDBApi.findBy(
|
||||
{id},
|
||||
{ transaction, currentUser },
|
||||
{transaction},
|
||||
);
|
||||
|
||||
if (!assessments) {
|
||||
@ -76,7 +95,7 @@ module.exports = class AssessmentsService {
|
||||
await transaction.rollback();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static async deleteByIds(ids, currentUser) {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
|
||||
@ -1,7 +1,11 @@
|
||||
const db = require('../db/models');
|
||||
const AttendanceDBApi = require('../db/api/attendance');
|
||||
const parseImportFile = require('./importFileParser');
|
||||
const processFile = require("../middlewares/upload");
|
||||
const ValidationError = require('./notifications/errors/validation');
|
||||
const csv = require('csv-parser');
|
||||
const axios = require('axios');
|
||||
const config = require('../config');
|
||||
const stream = require('stream');
|
||||
|
||||
|
||||
|
||||
@ -24,13 +28,28 @@ module.exports = class AttendanceService {
|
||||
await transaction.rollback();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static async bulkImport(req, res) {
|
||||
static async bulkImport(req, res, sendInvitationEmails = true, host) {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
|
||||
try {
|
||||
const results = await parseImportFile(req, res);
|
||||
await processFile(req, res);
|
||||
const bufferStream = new stream.PassThrough();
|
||||
const results = [];
|
||||
|
||||
await bufferStream.end(Buffer.from(req.file.buffer, "utf-8")); // convert Buffer to Stream
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
bufferStream
|
||||
.pipe(csv())
|
||||
.on('data', (data) => results.push(data))
|
||||
.on('end', async () => {
|
||||
console.log('CSV results', results);
|
||||
resolve();
|
||||
})
|
||||
.on('error', (error) => reject(error));
|
||||
})
|
||||
|
||||
await AttendanceDBApi.bulkImport(results, {
|
||||
transaction,
|
||||
@ -51,7 +70,7 @@ module.exports = class AttendanceService {
|
||||
try {
|
||||
let attendance = await AttendanceDBApi.findBy(
|
||||
{id},
|
||||
{ transaction, currentUser },
|
||||
{transaction},
|
||||
);
|
||||
|
||||
if (!attendance) {
|
||||
@ -76,7 +95,7 @@ module.exports = class AttendanceService {
|
||||
await transaction.rollback();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static async deleteByIds(ids, currentUser) {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
const UsersDBApi = require('../db/api/users');
|
||||
const db = require('../db/models');
|
||||
const ValidationError = require('./notifications/errors/validation');
|
||||
const ForbiddenError = require('./notifications/errors/forbidden');
|
||||
const bcrypt = require('bcrypt');
|
||||
@ -84,7 +83,7 @@ class Auth {
|
||||
return helpers.jwtSign(data);
|
||||
}
|
||||
|
||||
static async signin(email, password) {
|
||||
static async signin(email, password, options = {}) {
|
||||
const user = await UsersDBApi.findBy({email});
|
||||
|
||||
if (!user) {
|
||||
@ -291,21 +290,12 @@ class Auth {
|
||||
try {
|
||||
await UsersDBApi.findBy(
|
||||
{id: currentUser.id},
|
||||
{ transaction, currentUser },
|
||||
{transaction},
|
||||
);
|
||||
|
||||
const safeProfile = {
|
||||
firstName: data?.firstName,
|
||||
lastName: data?.lastName,
|
||||
phoneNumber: data?.phoneNumber,
|
||||
password: data?.password,
|
||||
avatar: data?.avatar,
|
||||
};
|
||||
|
||||
await UsersDBApi.update(
|
||||
currentUser.id,
|
||||
safeProfile,
|
||||
currentUser.app_role?.globalAccess,
|
||||
data,
|
||||
{
|
||||
currentUser,
|
||||
transaction
|
||||
|
||||
@ -1,7 +1,11 @@
|
||||
const db = require('../db/models');
|
||||
const Book_loansDBApi = require('../db/api/book_loans');
|
||||
const parseImportFile = require('./importFileParser');
|
||||
const processFile = require("../middlewares/upload");
|
||||
const ValidationError = require('./notifications/errors/validation');
|
||||
const csv = require('csv-parser');
|
||||
const axios = require('axios');
|
||||
const config = require('../config');
|
||||
const stream = require('stream');
|
||||
|
||||
|
||||
|
||||
@ -24,13 +28,28 @@ module.exports = class Book_loansService {
|
||||
await transaction.rollback();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static async bulkImport(req, res) {
|
||||
static async bulkImport(req, res, sendInvitationEmails = true, host) {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
|
||||
try {
|
||||
const results = await parseImportFile(req, res);
|
||||
await processFile(req, res);
|
||||
const bufferStream = new stream.PassThrough();
|
||||
const results = [];
|
||||
|
||||
await bufferStream.end(Buffer.from(req.file.buffer, "utf-8")); // convert Buffer to Stream
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
bufferStream
|
||||
.pipe(csv())
|
||||
.on('data', (data) => results.push(data))
|
||||
.on('end', async () => {
|
||||
console.log('CSV results', results);
|
||||
resolve();
|
||||
})
|
||||
.on('error', (error) => reject(error));
|
||||
})
|
||||
|
||||
await Book_loansDBApi.bulkImport(results, {
|
||||
transaction,
|
||||
@ -51,7 +70,7 @@ module.exports = class Book_loansService {
|
||||
try {
|
||||
let book_loans = await Book_loansDBApi.findBy(
|
||||
{id},
|
||||
{ transaction, currentUser },
|
||||
{transaction},
|
||||
);
|
||||
|
||||
if (!book_loans) {
|
||||
@ -76,7 +95,7 @@ module.exports = class Book_loansService {
|
||||
await transaction.rollback();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static async deleteByIds(ids, currentUser) {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
|
||||
@ -1,7 +1,11 @@
|
||||
const db = require('../db/models');
|
||||
const BooksDBApi = require('../db/api/books');
|
||||
const parseImportFile = require('./importFileParser');
|
||||
const processFile = require("../middlewares/upload");
|
||||
const ValidationError = require('./notifications/errors/validation');
|
||||
const csv = require('csv-parser');
|
||||
const axios = require('axios');
|
||||
const config = require('../config');
|
||||
const stream = require('stream');
|
||||
|
||||
|
||||
|
||||
@ -24,13 +28,28 @@ module.exports = class BooksService {
|
||||
await transaction.rollback();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static async bulkImport(req, res) {
|
||||
static async bulkImport(req, res, sendInvitationEmails = true, host) {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
|
||||
try {
|
||||
const results = await parseImportFile(req, res);
|
||||
await processFile(req, res);
|
||||
const bufferStream = new stream.PassThrough();
|
||||
const results = [];
|
||||
|
||||
await bufferStream.end(Buffer.from(req.file.buffer, "utf-8")); // convert Buffer to Stream
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
bufferStream
|
||||
.pipe(csv())
|
||||
.on('data', (data) => results.push(data))
|
||||
.on('end', async () => {
|
||||
console.log('CSV results', results);
|
||||
resolve();
|
||||
})
|
||||
.on('error', (error) => reject(error));
|
||||
})
|
||||
|
||||
await BooksDBApi.bulkImport(results, {
|
||||
transaction,
|
||||
@ -51,7 +70,7 @@ module.exports = class BooksService {
|
||||
try {
|
||||
let books = await BooksDBApi.findBy(
|
||||
{id},
|
||||
{ transaction, currentUser },
|
||||
{transaction},
|
||||
);
|
||||
|
||||
if (!books) {
|
||||
@ -76,7 +95,7 @@ module.exports = class BooksService {
|
||||
await transaction.rollback();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static async deleteByIds(ids, currentUser) {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
|
||||
@ -1,7 +1,11 @@
|
||||
const db = require('../db/models');
|
||||
const ClassesDBApi = require('../db/api/classes');
|
||||
const parseImportFile = require('./importFileParser');
|
||||
const processFile = require("../middlewares/upload");
|
||||
const ValidationError = require('./notifications/errors/validation');
|
||||
const csv = require('csv-parser');
|
||||
const axios = require('axios');
|
||||
const config = require('../config');
|
||||
const stream = require('stream');
|
||||
|
||||
|
||||
|
||||
@ -24,13 +28,28 @@ module.exports = class ClassesService {
|
||||
await transaction.rollback();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static async bulkImport(req, res) {
|
||||
static async bulkImport(req, res, sendInvitationEmails = true, host) {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
|
||||
try {
|
||||
const results = await parseImportFile(req, res);
|
||||
await processFile(req, res);
|
||||
const bufferStream = new stream.PassThrough();
|
||||
const results = [];
|
||||
|
||||
await bufferStream.end(Buffer.from(req.file.buffer, "utf-8")); // convert Buffer to Stream
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
bufferStream
|
||||
.pipe(csv())
|
||||
.on('data', (data) => results.push(data))
|
||||
.on('end', async () => {
|
||||
console.log('CSV results', results);
|
||||
resolve();
|
||||
})
|
||||
.on('error', (error) => reject(error));
|
||||
})
|
||||
|
||||
await ClassesDBApi.bulkImport(results, {
|
||||
transaction,
|
||||
@ -51,7 +70,7 @@ module.exports = class ClassesService {
|
||||
try {
|
||||
let classes = await ClassesDBApi.findBy(
|
||||
{id},
|
||||
{ transaction, currentUser },
|
||||
{transaction},
|
||||
);
|
||||
|
||||
if (!classes) {
|
||||
@ -76,7 +95,7 @@ module.exports = class ClassesService {
|
||||
await transaction.rollback();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static async deleteByIds(ids, currentUser) {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
|
||||
@ -1,7 +1,11 @@
|
||||
const db = require('../db/models');
|
||||
const CoursesDBApi = require('../db/api/courses');
|
||||
const parseImportFile = require('./importFileParser');
|
||||
const processFile = require("../middlewares/upload");
|
||||
const ValidationError = require('./notifications/errors/validation');
|
||||
const csv = require('csv-parser');
|
||||
const axios = require('axios');
|
||||
const config = require('../config');
|
||||
const stream = require('stream');
|
||||
|
||||
|
||||
|
||||
@ -24,13 +28,28 @@ module.exports = class CoursesService {
|
||||
await transaction.rollback();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static async bulkImport(req, res) {
|
||||
static async bulkImport(req, res, sendInvitationEmails = true, host) {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
|
||||
try {
|
||||
const results = await parseImportFile(req, res);
|
||||
await processFile(req, res);
|
||||
const bufferStream = new stream.PassThrough();
|
||||
const results = [];
|
||||
|
||||
await bufferStream.end(Buffer.from(req.file.buffer, "utf-8")); // convert Buffer to Stream
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
bufferStream
|
||||
.pipe(csv())
|
||||
.on('data', (data) => results.push(data))
|
||||
.on('end', async () => {
|
||||
console.log('CSV results', results);
|
||||
resolve();
|
||||
})
|
||||
.on('error', (error) => reject(error));
|
||||
})
|
||||
|
||||
await CoursesDBApi.bulkImport(results, {
|
||||
transaction,
|
||||
@ -51,7 +70,7 @@ module.exports = class CoursesService {
|
||||
try {
|
||||
let courses = await CoursesDBApi.findBy(
|
||||
{id},
|
||||
{ transaction, currentUser },
|
||||
{transaction},
|
||||
);
|
||||
|
||||
if (!courses) {
|
||||
@ -76,7 +95,7 @@ module.exports = class CoursesService {
|
||||
await transaction.rollback();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static async deleteByIds(ids, currentUser) {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
|
||||
@ -1,7 +1,11 @@
|
||||
const db = require('../db/models');
|
||||
const EmployeesDBApi = require('../db/api/employees');
|
||||
const parseImportFile = require('./importFileParser');
|
||||
const processFile = require("../middlewares/upload");
|
||||
const ValidationError = require('./notifications/errors/validation');
|
||||
const csv = require('csv-parser');
|
||||
const axios = require('axios');
|
||||
const config = require('../config');
|
||||
const stream = require('stream');
|
||||
|
||||
|
||||
|
||||
@ -24,13 +28,28 @@ module.exports = class EmployeesService {
|
||||
await transaction.rollback();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static async bulkImport(req, res) {
|
||||
static async bulkImport(req, res, sendInvitationEmails = true, host) {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
|
||||
try {
|
||||
const results = await parseImportFile(req, res);
|
||||
await processFile(req, res);
|
||||
const bufferStream = new stream.PassThrough();
|
||||
const results = [];
|
||||
|
||||
await bufferStream.end(Buffer.from(req.file.buffer, "utf-8")); // convert Buffer to Stream
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
bufferStream
|
||||
.pipe(csv())
|
||||
.on('data', (data) => results.push(data))
|
||||
.on('end', async () => {
|
||||
console.log('CSV results', results);
|
||||
resolve();
|
||||
})
|
||||
.on('error', (error) => reject(error));
|
||||
})
|
||||
|
||||
await EmployeesDBApi.bulkImport(results, {
|
||||
transaction,
|
||||
@ -51,7 +70,7 @@ module.exports = class EmployeesService {
|
||||
try {
|
||||
let employees = await EmployeesDBApi.findBy(
|
||||
{id},
|
||||
{ transaction, currentUser },
|
||||
{transaction},
|
||||
);
|
||||
|
||||
if (!employees) {
|
||||
@ -76,7 +95,7 @@ module.exports = class EmployeesService {
|
||||
await transaction.rollback();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static async deleteByIds(ids, currentUser) {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
|
||||
@ -1,7 +1,11 @@
|
||||
const db = require('../db/models');
|
||||
const EnrollmentsDBApi = require('../db/api/enrollments');
|
||||
const parseImportFile = require('./importFileParser');
|
||||
const processFile = require("../middlewares/upload");
|
||||
const ValidationError = require('./notifications/errors/validation');
|
||||
const csv = require('csv-parser');
|
||||
const axios = require('axios');
|
||||
const config = require('../config');
|
||||
const stream = require('stream');
|
||||
|
||||
|
||||
|
||||
@ -24,13 +28,28 @@ module.exports = class EnrollmentsService {
|
||||
await transaction.rollback();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static async bulkImport(req, res) {
|
||||
static async bulkImport(req, res, sendInvitationEmails = true, host) {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
|
||||
try {
|
||||
const results = await parseImportFile(req, res);
|
||||
await processFile(req, res);
|
||||
const bufferStream = new stream.PassThrough();
|
||||
const results = [];
|
||||
|
||||
await bufferStream.end(Buffer.from(req.file.buffer, "utf-8")); // convert Buffer to Stream
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
bufferStream
|
||||
.pipe(csv())
|
||||
.on('data', (data) => results.push(data))
|
||||
.on('end', async () => {
|
||||
console.log('CSV results', results);
|
||||
resolve();
|
||||
})
|
||||
.on('error', (error) => reject(error));
|
||||
})
|
||||
|
||||
await EnrollmentsDBApi.bulkImport(results, {
|
||||
transaction,
|
||||
@ -51,7 +70,7 @@ module.exports = class EnrollmentsService {
|
||||
try {
|
||||
let enrollments = await EnrollmentsDBApi.findBy(
|
||||
{id},
|
||||
{ transaction, currentUser },
|
||||
{transaction},
|
||||
);
|
||||
|
||||
if (!enrollments) {
|
||||
@ -76,7 +95,7 @@ module.exports = class EnrollmentsService {
|
||||
await transaction.rollback();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static async deleteByIds(ids, currentUser) {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
|
||||
@ -2,172 +2,7 @@ const formidable = require('formidable');
|
||||
const fs = require('fs');
|
||||
const config = require('../config');
|
||||
const path = require('path');
|
||||
const crypto = require('crypto');
|
||||
const db = require('../db/models');
|
||||
|
||||
const POSIX_SEPARATOR = '/';
|
||||
|
||||
const getFirstValue = (value) => {
|
||||
if (Array.isArray(value)) {
|
||||
return value[0];
|
||||
}
|
||||
|
||||
return value;
|
||||
};
|
||||
|
||||
const normalizeStoragePath = (value) => {
|
||||
if (typeof value !== 'string') {
|
||||
return null;
|
||||
}
|
||||
|
||||
const trimmed = value.trim();
|
||||
|
||||
if (!trimmed || trimmed.includes('\0') || trimmed.includes('\\')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const normalized = path.posix.normalize(trimmed);
|
||||
|
||||
if (
|
||||
normalized === '.' ||
|
||||
path.posix.isAbsolute(normalized) ||
|
||||
normalized === '..' ||
|
||||
normalized.startsWith(`..${POSIX_SEPARATOR}`) ||
|
||||
normalized.split(POSIX_SEPARATOR).includes('..') ||
|
||||
normalized !== trimmed
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return normalized;
|
||||
};
|
||||
|
||||
const normalizeFolder = (folder) => normalizeStoragePath(folder);
|
||||
|
||||
const normalizePrivateUrl = (privateUrl) => normalizeStoragePath(privateUrl);
|
||||
|
||||
const normalizeFilename = (filename) => {
|
||||
if (typeof filename !== 'string') {
|
||||
return null;
|
||||
}
|
||||
|
||||
const trimmed = filename.trim();
|
||||
|
||||
if (
|
||||
!trimmed ||
|
||||
trimmed.includes('\0') ||
|
||||
trimmed.includes(POSIX_SEPARATOR) ||
|
||||
trimmed.includes('\\') ||
|
||||
trimmed === '.' ||
|
||||
trimmed === '..' ||
|
||||
path.basename(trimmed) !== trimmed
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return trimmed;
|
||||
};
|
||||
|
||||
const createDownloadToken = (privateUrl) => (
|
||||
crypto
|
||||
.createHmac('sha256', config.secret_key)
|
||||
.update(privateUrl)
|
||||
.digest('hex')
|
||||
);
|
||||
|
||||
const isValidDownloadToken = (privateUrl, token) => {
|
||||
if (typeof token !== 'string' || !token) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const expected = createDownloadToken(privateUrl);
|
||||
const tokenBuffer = Buffer.from(token, 'hex');
|
||||
const expectedBuffer = Buffer.from(expected, 'hex');
|
||||
|
||||
return (
|
||||
tokenBuffer.length === expectedBuffer.length &&
|
||||
crypto.timingSafeEqual(tokenBuffer, expectedBuffer)
|
||||
);
|
||||
};
|
||||
|
||||
const buildPublicDownloadUrl = (privateUrl) => (
|
||||
`/api/file/download?privateUrl=${encodeURIComponent(privateUrl)}&token=${createDownloadToken(privateUrl)}`
|
||||
);
|
||||
|
||||
const ensureKnownPrivateUrl = async (privateUrl) => {
|
||||
const file = await db.file.findOne({
|
||||
where: { privateUrl },
|
||||
attributes: ['id'],
|
||||
});
|
||||
|
||||
return !!file;
|
||||
};
|
||||
|
||||
const canDownloadPrivateUrl = async (privateUrl, token) => {
|
||||
if (isValidDownloadToken(privateUrl, token)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return ensureKnownPrivateUrl(privateUrl);
|
||||
};
|
||||
|
||||
const assertSafeLocalPath = (privateUrl) => {
|
||||
const baseDir = path.resolve(config.uploadDir);
|
||||
const targetPath = path.resolve(baseDir, privateUrl);
|
||||
|
||||
if (targetPath !== baseDir && !targetPath.startsWith(`${baseDir}${path.sep}`)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return targetPath;
|
||||
};
|
||||
|
||||
const validateFileMetadata = async (file, relation, options = {}) => {
|
||||
const privateUrl = normalizePrivateUrl(file && file.privateUrl);
|
||||
const expectedFolder = normalizeFolder(
|
||||
`${String(relation.belongsTo)}/${String(relation.belongsToColumn)}`,
|
||||
);
|
||||
|
||||
if (!privateUrl || !expectedFolder) {
|
||||
const error = new Error('Invalid file path.');
|
||||
error.code = 400;
|
||||
throw error;
|
||||
}
|
||||
|
||||
const expectedPrefix = `${expectedFolder}/`;
|
||||
|
||||
if (!privateUrl.startsWith(expectedPrefix)) {
|
||||
const error = new Error('Invalid file relation path.');
|
||||
error.code = 400;
|
||||
throw error;
|
||||
}
|
||||
|
||||
const filename = privateUrl.slice(expectedPrefix.length);
|
||||
|
||||
if (!normalizeFilename(filename)) {
|
||||
const error = new Error('Invalid file name.');
|
||||
error.code = 400;
|
||||
throw error;
|
||||
}
|
||||
|
||||
const existingFile = await db.file.findOne({
|
||||
where: { privateUrl },
|
||||
attributes: ['id'],
|
||||
transaction: options.transaction,
|
||||
});
|
||||
|
||||
if (existingFile) {
|
||||
const error = new Error('File already belongs to a record.');
|
||||
error.code = 403;
|
||||
throw error;
|
||||
}
|
||||
|
||||
return {
|
||||
...file,
|
||||
privateUrl,
|
||||
publicUrl: buildPublicDownloadUrl(privateUrl),
|
||||
};
|
||||
};
|
||||
const { format } = require("util");
|
||||
|
||||
const ensureDirectoryExistence = (filePath) => {
|
||||
const dirname = path.dirname(filePath);
|
||||
@ -201,29 +36,20 @@ const uploadLocal = (
|
||||
return;
|
||||
}
|
||||
|
||||
let uploadFolder = folder;
|
||||
|
||||
if (validations.folderIncludesAuthenticationUid) {
|
||||
uploadFolder = uploadFolder.replace(
|
||||
folder = folder.replace(
|
||||
':userId',
|
||||
req.currentUser.authenticationUid,
|
||||
);
|
||||
if (
|
||||
!req.currentUser.authenticationUid ||
|
||||
!uploadFolder.includes(req.currentUser.authenticationUid)
|
||||
!folder.includes(req.currentUser.authenticationUid)
|
||||
) {
|
||||
res.sendStatus(403);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
uploadFolder = normalizeFolder(uploadFolder);
|
||||
|
||||
if (!uploadFolder) {
|
||||
res.status(400).send({ message: 'Invalid upload path.' });
|
||||
return;
|
||||
}
|
||||
|
||||
const form = new formidable.IncomingForm();
|
||||
form.uploadDir = config.uploadDir;
|
||||
|
||||
@ -232,83 +58,44 @@ const uploadLocal = (
|
||||
}
|
||||
|
||||
form.parse(req, function (err, fields, files) {
|
||||
if (err) {
|
||||
console.error('Upload parse error:', err);
|
||||
res.status(500).send(err);
|
||||
return;
|
||||
}
|
||||
const filename = String(fields.filename);
|
||||
const fileTempUrl = files.file.path;
|
||||
|
||||
const filename = normalizeFilename(String(getFirstValue(fields.filename) || ''));
|
||||
const uploadedFile = getFirstValue(files.file);
|
||||
const fileTempUrl = uploadedFile && uploadedFile.path;
|
||||
|
||||
if (!filename || !fileTempUrl) {
|
||||
if (fileTempUrl && fs.existsSync(fileTempUrl)) {
|
||||
fs.unlinkSync(fileTempUrl);
|
||||
}
|
||||
res.status(400).send({ message: 'Invalid uploaded file.' });
|
||||
return;
|
||||
}
|
||||
|
||||
const privateUrl = assertSafeLocalPath(
|
||||
path.posix.join(uploadFolder, filename),
|
||||
);
|
||||
|
||||
if (!privateUrl) {
|
||||
if (!filename) {
|
||||
fs.unlinkSync(fileTempUrl);
|
||||
res.status(400).send({ message: 'Invalid upload path.' });
|
||||
res.sendStatus(500);
|
||||
return;
|
||||
}
|
||||
|
||||
const privateUrl = path.join(
|
||||
form.uploadDir,
|
||||
folder,
|
||||
filename,
|
||||
);
|
||||
ensureDirectoryExistence(privateUrl);
|
||||
fs.renameSync(fileTempUrl, privateUrl);
|
||||
res.status(200).send({
|
||||
url: buildPublicDownloadUrl(path.posix.join(uploadFolder, filename)),
|
||||
});
|
||||
res.sendStatus(200);
|
||||
});
|
||||
|
||||
form.on('error', function (err) {
|
||||
console.error('Upload form error:', err);
|
||||
res.status(500).send(err);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const downloadLocal = async (req, res) => {
|
||||
const privateUrl = normalizePrivateUrl(getFirstValue(req.query.privateUrl));
|
||||
const privateUrl = req.query.privateUrl;
|
||||
if (!privateUrl) {
|
||||
return res.sendStatus(404);
|
||||
}
|
||||
|
||||
const canDownload = await canDownloadPrivateUrl(
|
||||
privateUrl,
|
||||
getFirstValue(req.query.token),
|
||||
);
|
||||
if (!canDownload) {
|
||||
return res.sendStatus(404);
|
||||
}
|
||||
|
||||
const privatePath = assertSafeLocalPath(privateUrl);
|
||||
if (!privatePath) {
|
||||
return res.sendStatus(404);
|
||||
}
|
||||
|
||||
res.download(privatePath, (err) => {
|
||||
if (!err) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.error('Download error:', err);
|
||||
if (!res.headersSent) {
|
||||
res.sendStatus(404);
|
||||
}
|
||||
});
|
||||
res.download(path.join(config.uploadDir, privateUrl));
|
||||
}
|
||||
|
||||
const initGCloud = () => {
|
||||
const processFile = require("../middlewares/upload");
|
||||
const { Storage } = require("@google-cloud/storage");
|
||||
|
||||
const crypto = require('crypto')
|
||||
const hash = config.gcloud.hash
|
||||
|
||||
const privateKey = process.env.GC_PRIVATE_KEY.replace(/\\\n/g, "\n");
|
||||
@ -329,20 +116,14 @@ const uploadGCloud = async (folder, req, res) => {
|
||||
try {
|
||||
const {hash, bucket, processFile} = initGCloud();
|
||||
await processFile(req, res);
|
||||
const uploadFolder = normalizeFolder(folder);
|
||||
let buffer = await req.file.buffer;
|
||||
let filename = await req.body.filename;
|
||||
|
||||
if (!req.file) {
|
||||
return res.status(400).send({ message: "Please upload a file!" });
|
||||
}
|
||||
|
||||
let buffer = await req.file.buffer;
|
||||
let filename = normalizeFilename(await req.body.filename);
|
||||
|
||||
if (!uploadFolder || !filename) {
|
||||
return res.status(400).send({ message: 'Invalid upload path.' });
|
||||
}
|
||||
|
||||
let path = `${hash}/${uploadFolder}/${filename}`;
|
||||
let path = `${hash}/${folder}/${filename}`;
|
||||
let blob = bucket.file(path);
|
||||
|
||||
console.log(path);
|
||||
@ -359,9 +140,10 @@ const uploadGCloud = async (folder, req, res) => {
|
||||
|
||||
console.log(`https://storage.googleapis.com/${bucket.name}/${blob.name}`);
|
||||
|
||||
blobStream.on("finish", async () => {
|
||||
const privateUrl = path.posix.join(uploadFolder, filename);
|
||||
const publicUrl = buildPublicDownloadUrl(privateUrl);
|
||||
blobStream.on("finish", async (data) => {
|
||||
const publicUrl = format(
|
||||
`https://storage.googleapis.com/${bucket.name}/${blob.name}`
|
||||
);
|
||||
|
||||
res.status(200).send({
|
||||
message: "Uploaded the file successfully: " + path,
|
||||
@ -381,21 +163,9 @@ const uploadGCloud = async (folder, req, res) => {
|
||||
|
||||
const downloadGCloud = async (req, res) => {
|
||||
try {
|
||||
const {hash, bucket} = initGCloud();
|
||||
|
||||
const privateUrl = normalizePrivateUrl(getFirstValue(await req.query.privateUrl));
|
||||
if (!privateUrl) {
|
||||
return res.sendStatus(404);
|
||||
}
|
||||
|
||||
const canDownload = await canDownloadPrivateUrl(
|
||||
privateUrl,
|
||||
getFirstValue(req.query.token),
|
||||
);
|
||||
if (!canDownload) {
|
||||
return res.sendStatus(404);
|
||||
}
|
||||
const {hash, bucket, processFile} = initGCloud();
|
||||
|
||||
const privateUrl = await req.query.privateUrl;
|
||||
const filePath = `${hash}/${privateUrl}`;
|
||||
const file = bucket.file(filePath)
|
||||
const fileExists = await file.exists();
|
||||
@ -406,7 +176,7 @@ const downloadGCloud = async (req, res) => {
|
||||
}
|
||||
else {
|
||||
res.status(404).send({
|
||||
message: "Could not download the file.",
|
||||
message: "Could not download the file. " + err,
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
@ -418,13 +188,8 @@ const downloadGCloud = async (req, res) => {
|
||||
|
||||
const deleteGCloud = async (privateUrl) => {
|
||||
try {
|
||||
const normalizedPrivateUrl = normalizePrivateUrl(privateUrl);
|
||||
if (!normalizedPrivateUrl) {
|
||||
return;
|
||||
}
|
||||
|
||||
const {hash, bucket} = initGCloud();
|
||||
const filePath = `${hash}/${normalizedPrivateUrl}`;
|
||||
const {hash, bucket, processFile} = initGCloud();
|
||||
const filePath = `${hash}/${privateUrl}`;
|
||||
|
||||
const file = bucket.file(filePath)
|
||||
const fileExists = await file.exists();
|
||||
@ -443,13 +208,6 @@ module.exports = {
|
||||
downloadLocal,
|
||||
deleteGCloud,
|
||||
uploadGCloud,
|
||||
downloadGCloud,
|
||||
normalizePrivateUrl,
|
||||
normalizeFilename,
|
||||
normalizeFolder,
|
||||
buildPublicDownloadUrl,
|
||||
validateFileMetadata,
|
||||
createDownloadToken,
|
||||
isValidDownloadToken,
|
||||
downloadGCloud
|
||||
}
|
||||
|
||||
|
||||
@ -1,7 +1,11 @@
|
||||
const db = require('../db/models');
|
||||
const GradesDBApi = require('../db/api/grades');
|
||||
const parseImportFile = require('./importFileParser');
|
||||
const processFile = require("../middlewares/upload");
|
||||
const ValidationError = require('./notifications/errors/validation');
|
||||
const csv = require('csv-parser');
|
||||
const axios = require('axios');
|
||||
const config = require('../config');
|
||||
const stream = require('stream');
|
||||
|
||||
|
||||
|
||||
@ -24,13 +28,28 @@ module.exports = class GradesService {
|
||||
await transaction.rollback();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static async bulkImport(req, res) {
|
||||
static async bulkImport(req, res, sendInvitationEmails = true, host) {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
|
||||
try {
|
||||
const results = await parseImportFile(req, res);
|
||||
await processFile(req, res);
|
||||
const bufferStream = new stream.PassThrough();
|
||||
const results = [];
|
||||
|
||||
await bufferStream.end(Buffer.from(req.file.buffer, "utf-8")); // convert Buffer to Stream
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
bufferStream
|
||||
.pipe(csv())
|
||||
.on('data', (data) => results.push(data))
|
||||
.on('end', async () => {
|
||||
console.log('CSV results', results);
|
||||
resolve();
|
||||
})
|
||||
.on('error', (error) => reject(error));
|
||||
})
|
||||
|
||||
await GradesDBApi.bulkImport(results, {
|
||||
transaction,
|
||||
@ -51,7 +70,7 @@ module.exports = class GradesService {
|
||||
try {
|
||||
let grades = await GradesDBApi.findBy(
|
||||
{id},
|
||||
{ transaction, currentUser },
|
||||
{transaction},
|
||||
);
|
||||
|
||||
if (!grades) {
|
||||
@ -76,7 +95,7 @@ module.exports = class GradesService {
|
||||
await transaction.rollback();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static async deleteByIds(ids, currentUser) {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
|
||||
@ -1,7 +1,11 @@
|
||||
const db = require('../db/models');
|
||||
const GuardiansDBApi = require('../db/api/guardians');
|
||||
const parseImportFile = require('./importFileParser');
|
||||
const processFile = require("../middlewares/upload");
|
||||
const ValidationError = require('./notifications/errors/validation');
|
||||
const csv = require('csv-parser');
|
||||
const axios = require('axios');
|
||||
const config = require('../config');
|
||||
const stream = require('stream');
|
||||
|
||||
|
||||
|
||||
@ -24,13 +28,28 @@ module.exports = class GuardiansService {
|
||||
await transaction.rollback();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static async bulkImport(req, res) {
|
||||
static async bulkImport(req, res, sendInvitationEmails = true, host) {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
|
||||
try {
|
||||
const results = await parseImportFile(req, res);
|
||||
await processFile(req, res);
|
||||
const bufferStream = new stream.PassThrough();
|
||||
const results = [];
|
||||
|
||||
await bufferStream.end(Buffer.from(req.file.buffer, "utf-8")); // convert Buffer to Stream
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
bufferStream
|
||||
.pipe(csv())
|
||||
.on('data', (data) => results.push(data))
|
||||
.on('end', async () => {
|
||||
console.log('CSV results', results);
|
||||
resolve();
|
||||
})
|
||||
.on('error', (error) => reject(error));
|
||||
})
|
||||
|
||||
await GuardiansDBApi.bulkImport(results, {
|
||||
transaction,
|
||||
@ -51,7 +70,7 @@ module.exports = class GuardiansService {
|
||||
try {
|
||||
let guardians = await GuardiansDBApi.findBy(
|
||||
{id},
|
||||
{ transaction, currentUser },
|
||||
{transaction},
|
||||
);
|
||||
|
||||
if (!guardians) {
|
||||
@ -76,7 +95,7 @@ module.exports = class GuardiansService {
|
||||
await transaction.rollback();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static async deleteByIds(ids, currentUser) {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
|
||||
@ -1,380 +0,0 @@
|
||||
const csv = require('csv-parser');
|
||||
const stream = require('stream');
|
||||
const zlib = require('zlib');
|
||||
const processFile = require('../middlewares/upload');
|
||||
|
||||
function createBadRequest(message) {
|
||||
const error = new Error(message);
|
||||
error.code = 400;
|
||||
return error;
|
||||
}
|
||||
|
||||
function getExtension(filename) {
|
||||
if (!filename) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const match = String(filename).match(/\.([^.]+)$/);
|
||||
return match ? match[1].toLowerCase() : '';
|
||||
}
|
||||
|
||||
async function parseCsvBuffer(buffer) {
|
||||
const bufferStream = new stream.PassThrough();
|
||||
const results = [];
|
||||
|
||||
bufferStream.end(Buffer.from(buffer, 'utf-8'));
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
bufferStream
|
||||
.pipe(csv())
|
||||
.on('data', (data) => results.push(data))
|
||||
.on('end', resolve)
|
||||
.on('error', reject);
|
||||
});
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
function findEndOfCentralDirectory(buffer) {
|
||||
const signature = 0x06054b50;
|
||||
const minOffset = Math.max(0, buffer.length - 0xffff - 22);
|
||||
|
||||
for (let offset = buffer.length - 22; offset >= minOffset; offset -= 1) {
|
||||
if (buffer.readUInt32LE(offset) === signature) {
|
||||
return offset;
|
||||
}
|
||||
}
|
||||
|
||||
throw createBadRequest('Invalid XLSX file.');
|
||||
}
|
||||
|
||||
function readZipEntries(buffer) {
|
||||
const centralDirectorySignature = 0x02014b50;
|
||||
const localFileSignature = 0x04034b50;
|
||||
const endOffset = findEndOfCentralDirectory(buffer);
|
||||
const entriesCount = buffer.readUInt16LE(endOffset + 10);
|
||||
let offset = buffer.readUInt32LE(endOffset + 16);
|
||||
const entries = {};
|
||||
|
||||
for (let index = 0; index < entriesCount; index += 1) {
|
||||
if (buffer.readUInt32LE(offset) !== centralDirectorySignature) {
|
||||
throw createBadRequest('Invalid XLSX file.');
|
||||
}
|
||||
|
||||
const compressionMethod = buffer.readUInt16LE(offset + 10);
|
||||
const compressedSize = buffer.readUInt32LE(offset + 20);
|
||||
const fileNameLength = buffer.readUInt16LE(offset + 28);
|
||||
const extraFieldLength = buffer.readUInt16LE(offset + 30);
|
||||
const fileCommentLength = buffer.readUInt16LE(offset + 32);
|
||||
const localHeaderOffset = buffer.readUInt32LE(offset + 42);
|
||||
const fileName = buffer.toString('utf8', offset + 46, offset + 46 + fileNameLength);
|
||||
|
||||
if (buffer.readUInt32LE(localHeaderOffset) !== localFileSignature) {
|
||||
throw createBadRequest('Invalid XLSX file.');
|
||||
}
|
||||
|
||||
const localFileNameLength = buffer.readUInt16LE(localHeaderOffset + 26);
|
||||
const localExtraFieldLength = buffer.readUInt16LE(localHeaderOffset + 28);
|
||||
const dataOffset = localHeaderOffset + 30 + localFileNameLength + localExtraFieldLength;
|
||||
const compressedData = buffer.slice(dataOffset, dataOffset + compressedSize);
|
||||
|
||||
let data;
|
||||
if (compressionMethod === 0) {
|
||||
data = compressedData;
|
||||
} else if (compressionMethod === 8) {
|
||||
data = zlib.inflateRawSync(compressedData);
|
||||
} else {
|
||||
throw createBadRequest('Unsupported XLSX compression method.');
|
||||
}
|
||||
|
||||
entries[fileName] = data;
|
||||
offset += 46 + fileNameLength + extraFieldLength + fileCommentLength;
|
||||
}
|
||||
|
||||
return entries;
|
||||
}
|
||||
|
||||
function xmlText(entries, filename) {
|
||||
return entries[filename] ? entries[filename].toString('utf8') : '';
|
||||
}
|
||||
|
||||
function decodeXml(value) {
|
||||
if (value === undefined || value === null) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return String(value)
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, "'")
|
||||
.replace(/&/g, '&');
|
||||
}
|
||||
|
||||
function getAttribute(source, name) {
|
||||
const escapedName = name.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&');
|
||||
const match = source.match(new RegExp(`(?:^|\\s)${escapedName}=(['"])(.*?)\\1`));
|
||||
return match ? decodeXml(match[2]) : null;
|
||||
}
|
||||
|
||||
function extractTextFromXmlFragment(fragment) {
|
||||
const parts = [];
|
||||
const textRegex = /<t\b[^>]*>([\s\S]*?)<\/t>/g;
|
||||
let textMatch;
|
||||
|
||||
while ((textMatch = textRegex.exec(fragment)) !== null) {
|
||||
parts.push(decodeXml(textMatch[1]));
|
||||
}
|
||||
|
||||
if (parts.length) {
|
||||
return parts.join('');
|
||||
}
|
||||
|
||||
return decodeXml(fragment.replace(/<[^>]+>/g, ''));
|
||||
}
|
||||
|
||||
function parseSharedStrings(sharedStringsXml) {
|
||||
const sharedStrings = [];
|
||||
const itemRegex = /<si\b[^>]*>([\s\S]*?)<\/si>/g;
|
||||
let itemMatch;
|
||||
|
||||
while ((itemMatch = itemRegex.exec(sharedStringsXml)) !== null) {
|
||||
sharedStrings.push(extractTextFromXmlFragment(itemMatch[1]));
|
||||
}
|
||||
|
||||
return sharedStrings;
|
||||
}
|
||||
|
||||
function parseDateStyleIndexes(stylesXml) {
|
||||
if (!stylesXml) {
|
||||
return new Set();
|
||||
}
|
||||
|
||||
const builtInDateFormats = new Set([
|
||||
14, 15, 16, 17, 18, 19, 20, 21, 22,
|
||||
27, 28, 29, 30, 31, 32, 33, 34, 35, 36,
|
||||
45, 46, 47, 50, 51, 52, 53, 54, 55, 56, 57, 58,
|
||||
]);
|
||||
const customDateFormats = new Set();
|
||||
const numberFormatRegex = /<numFmt\b([^>]*)\/>/g;
|
||||
let numberFormatMatch;
|
||||
|
||||
while ((numberFormatMatch = numberFormatRegex.exec(stylesXml)) !== null) {
|
||||
const numberFormatId = Number(getAttribute(numberFormatMatch[1], 'numFmtId'));
|
||||
const formatCode = getAttribute(numberFormatMatch[1], 'formatCode') || '';
|
||||
|
||||
if (Number.isFinite(numberFormatId) && /[dy]/i.test(formatCode)) {
|
||||
customDateFormats.add(numberFormatId);
|
||||
}
|
||||
}
|
||||
|
||||
const cellFormatsMatch = stylesXml.match(/<cellXfs\b[^>]*>([\s\S]*?)<\/cellXfs>/);
|
||||
if (!cellFormatsMatch) {
|
||||
return new Set();
|
||||
}
|
||||
|
||||
const styleIndexes = new Set();
|
||||
const formatRegex = /<xf\b([^>]*)\/?>(?:<\/xf>)?/g;
|
||||
let styleIndex = 0;
|
||||
let formatMatch;
|
||||
|
||||
while ((formatMatch = formatRegex.exec(cellFormatsMatch[1])) !== null) {
|
||||
const numberFormatId = Number(getAttribute(formatMatch[1], 'numFmtId'));
|
||||
|
||||
if (builtInDateFormats.has(numberFormatId) || customDateFormats.has(numberFormatId)) {
|
||||
styleIndexes.add(styleIndex);
|
||||
}
|
||||
|
||||
styleIndex += 1;
|
||||
}
|
||||
|
||||
return styleIndexes;
|
||||
}
|
||||
|
||||
function excelSerialDateToIsoDate(value) {
|
||||
const serial = Number(value);
|
||||
|
||||
if (!Number.isFinite(serial)) {
|
||||
return value;
|
||||
}
|
||||
|
||||
const milliseconds = Math.round((serial - 25569) * 86400 * 1000);
|
||||
const date = new Date(milliseconds);
|
||||
|
||||
if (Number.isNaN(date.getTime())) {
|
||||
return value;
|
||||
}
|
||||
|
||||
return date.toISOString().slice(0, 10);
|
||||
}
|
||||
|
||||
function columnNameToIndex(columnName) {
|
||||
return columnName
|
||||
.toUpperCase()
|
||||
.split('')
|
||||
.reduce((result, char) => result * 26 + char.charCodeAt(0) - 64, 0) - 1;
|
||||
}
|
||||
|
||||
function getFirstSheetPath(entries) {
|
||||
const workbookXml = xmlText(entries, 'xl/workbook.xml');
|
||||
const relationshipsXml = xmlText(entries, 'xl/_rels/workbook.xml.rels');
|
||||
|
||||
if (!workbookXml || !relationshipsXml) {
|
||||
return entries['xl/worksheets/sheet1.xml'] ? 'xl/worksheets/sheet1.xml' : null;
|
||||
}
|
||||
|
||||
const sheetMatch = workbookXml.match(/<sheet\b([^>]*)\/?>/);
|
||||
const relationshipId = sheetMatch ? getAttribute(sheetMatch[1], 'r:id') : null;
|
||||
|
||||
if (!relationshipId) {
|
||||
return entries['xl/worksheets/sheet1.xml'] ? 'xl/worksheets/sheet1.xml' : null;
|
||||
}
|
||||
|
||||
const relationshipRegex = /<Relationship\b([^>]*)\/?>/g;
|
||||
let relationshipMatch;
|
||||
|
||||
while ((relationshipMatch = relationshipRegex.exec(relationshipsXml)) !== null) {
|
||||
if (getAttribute(relationshipMatch[1], 'Id') === relationshipId) {
|
||||
const target = getAttribute(relationshipMatch[1], 'Target');
|
||||
|
||||
if (!target) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (target.startsWith('/')) {
|
||||
return target.replace(/^\//, '');
|
||||
}
|
||||
|
||||
return `xl/${target}`.replace(/\/\.\//g, '/');
|
||||
}
|
||||
}
|
||||
|
||||
return entries['xl/worksheets/sheet1.xml'] ? 'xl/worksheets/sheet1.xml' : null;
|
||||
}
|
||||
|
||||
function readCellValue(cellAttributes, cellBody, sharedStrings, dateStyleIndexes) {
|
||||
const cellType = getAttribute(cellAttributes, 't');
|
||||
const styleIndex = Number(getAttribute(cellAttributes, 's'));
|
||||
|
||||
if (cellType === 'inlineStr') {
|
||||
return extractTextFromXmlFragment(cellBody);
|
||||
}
|
||||
|
||||
const valueMatch = cellBody.match(/<v\b[^>]*>([\s\S]*?)<\/v>/);
|
||||
const rawValue = valueMatch ? decodeXml(valueMatch[1]) : '';
|
||||
|
||||
if (cellType === 's') {
|
||||
return sharedStrings[Number(rawValue)] || '';
|
||||
}
|
||||
|
||||
if (cellType === 'b') {
|
||||
return rawValue === '1' ? 'true' : 'false';
|
||||
}
|
||||
|
||||
if (dateStyleIndexes.has(styleIndex) && rawValue !== '') {
|
||||
return excelSerialDateToIsoDate(rawValue);
|
||||
}
|
||||
|
||||
return rawValue;
|
||||
}
|
||||
|
||||
function parseWorksheetRows(sheetXml, sharedStrings, dateStyleIndexes) {
|
||||
const rows = [];
|
||||
const rowRegex = /<row\b[^>]*>([\s\S]*?)<\/row>/g;
|
||||
let rowMatch;
|
||||
|
||||
while ((rowMatch = rowRegex.exec(sheetXml)) !== null) {
|
||||
const row = [];
|
||||
const cellRegex = /<c\b([^>]*?)>([\s\S]*?)<\/c>|<c\b([^>]*?)\/>/g;
|
||||
let cellMatch;
|
||||
let nextColumnIndex = 0;
|
||||
|
||||
while ((cellMatch = cellRegex.exec(rowMatch[1])) !== null) {
|
||||
const cellAttributes = cellMatch[1] || cellMatch[3] || '';
|
||||
const cellBody = cellMatch[2] || '';
|
||||
const cellReference = getAttribute(cellAttributes, 'r');
|
||||
const columnMatch = cellReference ? cellReference.match(/[A-Z]+/i) : null;
|
||||
const columnIndex = columnMatch ? columnNameToIndex(columnMatch[0]) : nextColumnIndex;
|
||||
|
||||
row[columnIndex] = readCellValue(cellAttributes, cellBody, sharedStrings, dateStyleIndexes);
|
||||
nextColumnIndex = columnIndex + 1;
|
||||
}
|
||||
|
||||
if (row.some((value) => value !== undefined && value !== '')) {
|
||||
rows.push(row);
|
||||
}
|
||||
}
|
||||
|
||||
return rows;
|
||||
}
|
||||
|
||||
function rowsToObjects(rows) {
|
||||
if (!rows.length) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const headers = rows[0].map((header) => String(header || '').replace(/^\uFEFF/, '').trim());
|
||||
|
||||
return rows.slice(1).reduce((items, row) => {
|
||||
const item = {};
|
||||
|
||||
headers.forEach((header, index) => {
|
||||
if (!header) {
|
||||
return;
|
||||
}
|
||||
|
||||
const value = row[index];
|
||||
item[header] = value === undefined ? '' : value;
|
||||
});
|
||||
|
||||
if (Object.values(item).some((value) => value !== '')) {
|
||||
items.push(item);
|
||||
}
|
||||
|
||||
return items;
|
||||
}, []);
|
||||
}
|
||||
|
||||
function parseXlsxBuffer(buffer) {
|
||||
const entries = readZipEntries(buffer);
|
||||
const sheetPath = getFirstSheetPath(entries);
|
||||
|
||||
if (!sheetPath || !entries[sheetPath]) {
|
||||
throw createBadRequest('XLSX file does not contain a worksheet.');
|
||||
}
|
||||
|
||||
const sharedStrings = parseSharedStrings(xmlText(entries, 'xl/sharedStrings.xml'));
|
||||
const dateStyleIndexes = parseDateStyleIndexes(xmlText(entries, 'xl/styles.xml'));
|
||||
const rows = parseWorksheetRows(xmlText(entries, sheetPath), sharedStrings, dateStyleIndexes);
|
||||
|
||||
return rowsToObjects(rows);
|
||||
}
|
||||
|
||||
async function parseImportFile(req, res) {
|
||||
await processFile(req, res);
|
||||
|
||||
if (!req.file || !req.file.buffer) {
|
||||
throw createBadRequest('No import file was uploaded.');
|
||||
}
|
||||
|
||||
const filename = req.file.originalname || req.body.filename || '';
|
||||
const extension = getExtension(filename);
|
||||
|
||||
if (extension === 'csv') {
|
||||
return parseCsvBuffer(req.file.buffer);
|
||||
}
|
||||
|
||||
if (extension === 'xlsx') {
|
||||
return parseXlsxBuffer(req.file.buffer);
|
||||
}
|
||||
|
||||
if (extension === 'xls') {
|
||||
throw createBadRequest('Legacy .xls imports are not supported yet. Please save the spreadsheet as .xlsx or .csv.');
|
||||
}
|
||||
|
||||
throw createBadRequest('Invalid import format. Allowed formats: .csv, .xlsx.');
|
||||
}
|
||||
|
||||
module.exports = parseImportFile;
|
||||
module.exports.parseCsvBuffer = parseCsvBuffer;
|
||||
module.exports.parseXlsxBuffer = parseXlsxBuffer;
|
||||
@ -1,7 +1,11 @@
|
||||
const db = require('../db/models');
|
||||
const InvoicesDBApi = require('../db/api/invoices');
|
||||
const parseImportFile = require('./importFileParser');
|
||||
const processFile = require("../middlewares/upload");
|
||||
const ValidationError = require('./notifications/errors/validation');
|
||||
const csv = require('csv-parser');
|
||||
const axios = require('axios');
|
||||
const config = require('../config');
|
||||
const stream = require('stream');
|
||||
|
||||
|
||||
|
||||
@ -24,13 +28,28 @@ module.exports = class InvoicesService {
|
||||
await transaction.rollback();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static async bulkImport(req, res) {
|
||||
static async bulkImport(req, res, sendInvitationEmails = true, host) {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
|
||||
try {
|
||||
const results = await parseImportFile(req, res);
|
||||
await processFile(req, res);
|
||||
const bufferStream = new stream.PassThrough();
|
||||
const results = [];
|
||||
|
||||
await bufferStream.end(Buffer.from(req.file.buffer, "utf-8")); // convert Buffer to Stream
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
bufferStream
|
||||
.pipe(csv())
|
||||
.on('data', (data) => results.push(data))
|
||||
.on('end', async () => {
|
||||
console.log('CSV results', results);
|
||||
resolve();
|
||||
})
|
||||
.on('error', (error) => reject(error));
|
||||
})
|
||||
|
||||
await InvoicesDBApi.bulkImport(results, {
|
||||
transaction,
|
||||
@ -51,7 +70,7 @@ module.exports = class InvoicesService {
|
||||
try {
|
||||
let invoices = await InvoicesDBApi.findBy(
|
||||
{id},
|
||||
{ transaction, currentUser },
|
||||
{transaction},
|
||||
);
|
||||
|
||||
if (!invoices) {
|
||||
@ -76,7 +95,7 @@ module.exports = class InvoicesService {
|
||||
await transaction.rollback();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static async deleteByIds(ids, currentUser) {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
|
||||
@ -2,10 +2,20 @@ const axios = require('axios');
|
||||
const config = require('../config');
|
||||
const { LocalAIApi } = require('../ai/LocalAIApi');
|
||||
|
||||
|
||||
const loadRoleService = () => {
|
||||
try {
|
||||
return require('./roles');
|
||||
} catch (error) {
|
||||
console.error('Role service is missing. Advanced roles are required for this operation.', error);
|
||||
const err = new Error('Role service is missing. Advanced roles are required for this operation.');
|
||||
err.originalError = error;
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = class OpenAiService {
|
||||
static async getWidget(payload) {
|
||||
static async getWidget(payload, userId, roleId) {
|
||||
const RoleService = loadRoleService();
|
||||
const response = await axios.post(
|
||||
`${config.flHost}/${config.project_uuid}/project_customization_widgets.json`,
|
||||
payload,
|
||||
@ -13,6 +23,7 @@ module.exports = class OpenAiService {
|
||||
|
||||
if (response.status >= 200 && response.status < 300) {
|
||||
const { widget_id } = await response.data;
|
||||
await RoleService.addRoleInfo(roleId, userId, 'widgets', widget_id);
|
||||
return widget_id;
|
||||
} else {
|
||||
console.error('=======error=======', response.data);
|
||||
|
||||
@ -1,7 +1,11 @@
|
||||
const db = require('../db/models');
|
||||
const PaymentsDBApi = require('../db/api/payments');
|
||||
const parseImportFile = require('./importFileParser');
|
||||
const processFile = require("../middlewares/upload");
|
||||
const ValidationError = require('./notifications/errors/validation');
|
||||
const csv = require('csv-parser');
|
||||
const axios = require('axios');
|
||||
const config = require('../config');
|
||||
const stream = require('stream');
|
||||
|
||||
|
||||
|
||||
@ -24,13 +28,28 @@ module.exports = class PaymentsService {
|
||||
await transaction.rollback();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static async bulkImport(req, res) {
|
||||
static async bulkImport(req, res, sendInvitationEmails = true, host) {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
|
||||
try {
|
||||
const results = await parseImportFile(req, res);
|
||||
await processFile(req, res);
|
||||
const bufferStream = new stream.PassThrough();
|
||||
const results = [];
|
||||
|
||||
await bufferStream.end(Buffer.from(req.file.buffer, "utf-8")); // convert Buffer to Stream
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
bufferStream
|
||||
.pipe(csv())
|
||||
.on('data', (data) => results.push(data))
|
||||
.on('end', async () => {
|
||||
console.log('CSV results', results);
|
||||
resolve();
|
||||
})
|
||||
.on('error', (error) => reject(error));
|
||||
})
|
||||
|
||||
await PaymentsDBApi.bulkImport(results, {
|
||||
transaction,
|
||||
@ -51,7 +70,7 @@ module.exports = class PaymentsService {
|
||||
try {
|
||||
let payments = await PaymentsDBApi.findBy(
|
||||
{id},
|
||||
{ transaction, currentUser },
|
||||
{transaction},
|
||||
);
|
||||
|
||||
if (!payments) {
|
||||
@ -76,7 +95,7 @@ module.exports = class PaymentsService {
|
||||
await transaction.rollback();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static async deleteByIds(ids, currentUser) {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
|
||||
@ -1,7 +1,11 @@
|
||||
const db = require('../db/models');
|
||||
const PermissionsDBApi = require('../db/api/permissions');
|
||||
const parseImportFile = require('./importFileParser');
|
||||
const processFile = require("../middlewares/upload");
|
||||
const ValidationError = require('./notifications/errors/validation');
|
||||
const csv = require('csv-parser');
|
||||
const axios = require('axios');
|
||||
const config = require('../config');
|
||||
const stream = require('stream');
|
||||
|
||||
|
||||
|
||||
@ -24,13 +28,28 @@ module.exports = class PermissionsService {
|
||||
await transaction.rollback();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static async bulkImport(req, res) {
|
||||
static async bulkImport(req, res, sendInvitationEmails = true, host) {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
|
||||
try {
|
||||
const results = await parseImportFile(req, res);
|
||||
await processFile(req, res);
|
||||
const bufferStream = new stream.PassThrough();
|
||||
const results = [];
|
||||
|
||||
await bufferStream.end(Buffer.from(req.file.buffer, "utf-8")); // convert Buffer to Stream
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
bufferStream
|
||||
.pipe(csv())
|
||||
.on('data', (data) => results.push(data))
|
||||
.on('end', async () => {
|
||||
console.log('CSV results', results);
|
||||
resolve();
|
||||
})
|
||||
.on('error', (error) => reject(error));
|
||||
})
|
||||
|
||||
await PermissionsDBApi.bulkImport(results, {
|
||||
transaction,
|
||||
@ -76,7 +95,7 @@ module.exports = class PermissionsService {
|
||||
await transaction.rollback();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static async deleteByIds(ids, currentUser) {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
|
||||
@ -1,7 +1,11 @@
|
||||
const db = require('../db/models');
|
||||
const ProductsDBApi = require('../db/api/products');
|
||||
const parseImportFile = require('./importFileParser');
|
||||
const processFile = require("../middlewares/upload");
|
||||
const ValidationError = require('./notifications/errors/validation');
|
||||
const csv = require('csv-parser');
|
||||
const axios = require('axios');
|
||||
const config = require('../config');
|
||||
const stream = require('stream');
|
||||
|
||||
|
||||
|
||||
@ -24,13 +28,28 @@ module.exports = class ProductsService {
|
||||
await transaction.rollback();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static async bulkImport(req, res) {
|
||||
static async bulkImport(req, res, sendInvitationEmails = true, host) {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
|
||||
try {
|
||||
const results = await parseImportFile(req, res);
|
||||
await processFile(req, res);
|
||||
const bufferStream = new stream.PassThrough();
|
||||
const results = [];
|
||||
|
||||
await bufferStream.end(Buffer.from(req.file.buffer, "utf-8")); // convert Buffer to Stream
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
bufferStream
|
||||
.pipe(csv())
|
||||
.on('data', (data) => results.push(data))
|
||||
.on('end', async () => {
|
||||
console.log('CSV results', results);
|
||||
resolve();
|
||||
})
|
||||
.on('error', (error) => reject(error));
|
||||
})
|
||||
|
||||
await ProductsDBApi.bulkImport(results, {
|
||||
transaction,
|
||||
@ -51,7 +70,7 @@ module.exports = class ProductsService {
|
||||
try {
|
||||
let products = await ProductsDBApi.findBy(
|
||||
{id},
|
||||
{ transaction, currentUser },
|
||||
{transaction},
|
||||
);
|
||||
|
||||
if (!products) {
|
||||
@ -76,7 +95,7 @@ module.exports = class ProductsService {
|
||||
await transaction.rollback();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static async deleteByIds(ids, currentUser) {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
|
||||
@ -1,41 +1,19 @@
|
||||
const db = require('../db/models');
|
||||
const RolesDBApi = require('../db/api/roles');
|
||||
const parseImportFile = require('./importFileParser');
|
||||
const processFile = require("../middlewares/upload");
|
||||
const ValidationError = require('./notifications/errors/validation');
|
||||
const csv = require('csv-parser');
|
||||
const axios = require('axios');
|
||||
const config = require('../config');
|
||||
const { getCurrentUserSchoolId } = require('../db/api/schoolScope');
|
||||
const { executeReadOnlySelect } = require('./sqlSafety');
|
||||
|
||||
const WIDGET_CUSTOMIZATION_KEY = 'widgets';
|
||||
const UUID_REGEX = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
|
||||
|
||||
function badRequest(message) {
|
||||
const error = new Error(message);
|
||||
error.code = 400;
|
||||
return error;
|
||||
}
|
||||
|
||||
function assertWidgetKey(key) {
|
||||
if (key !== WIDGET_CUSTOMIZATION_KEY) {
|
||||
throw badRequest('Invalid role customization key.');
|
||||
}
|
||||
}
|
||||
|
||||
function assertUuid(value, label) {
|
||||
if (typeof value !== 'string' || !UUID_REGEX.test(value)) {
|
||||
throw badRequest(`${label} must be a valid UUID.`);
|
||||
}
|
||||
}
|
||||
const stream = require('stream');
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
function buildWidgetResult(widget, rows, queryString) {
|
||||
if (Array.isArray(rows) && rows.length) {
|
||||
const key = Object.keys(rows[0])[0];
|
||||
const value = widget.widget_type === 'scalar' ? rows[0][key] : rows;
|
||||
function buildWidgetResult(widget, queryResult, queryString) {
|
||||
if (queryResult[0] && queryResult[0].length) {
|
||||
const key = Object.keys(queryResult[0][0])[0];
|
||||
const value = widget.widget_type === 'scalar' ? queryResult[0][0][key] : queryResult[0];
|
||||
const widgetData = JSON.parse(widget.data);
|
||||
return { ...widget, ...widgetData, value, query: queryString };
|
||||
} else {
|
||||
@ -43,16 +21,14 @@ function buildWidgetResult(widget, rows, queryString) {
|
||||
}
|
||||
}
|
||||
|
||||
async function executeQuery(queryString, replacements) {
|
||||
async function executeQuery(queryString, currentUser) {
|
||||
try {
|
||||
return await executeReadOnlySelect(queryString, {
|
||||
replacements,
|
||||
maxRows: 1000,
|
||||
timeoutMs: 5000,
|
||||
return await db.sequelize.query(queryString, {
|
||||
replacements: { organizationId: currentUser.organizationId },
|
||||
});
|
||||
} catch (e) {
|
||||
console.error('Widget SQL execution failed:', e);
|
||||
throw e;
|
||||
console.log(e);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
@ -73,36 +49,19 @@ function insertWhereConditions(queryString, whereConditions) {
|
||||
}
|
||||
|
||||
function constructWhereConditions(mainTable, currentUser, replacements) {
|
||||
const globalAccess = currentUser?.app_role?.globalAccess;
|
||||
const tablesWithoutSchoolScope = ['permissions', 'roles'];
|
||||
const model = db[mainTable];
|
||||
const rawAttributes = model?.rawAttributes || {};
|
||||
const conditions = [];
|
||||
const { organizationId, app_role: { globalAccess } } = currentUser;
|
||||
const tablesWithoutOrgId = ['permissions', 'roles'];
|
||||
let whereConditions = '';
|
||||
|
||||
if (!globalAccess && !tablesWithoutSchoolScope.includes(mainTable)) {
|
||||
const schoolId = getCurrentUserSchoolId(currentUser);
|
||||
|
||||
if (!schoolId) {
|
||||
conditions.push('1 = 0');
|
||||
} else if (mainTable === 'schools') {
|
||||
conditions.push(`"${mainTable}"."id" = :schoolId`);
|
||||
replacements.schoolId = schoolId;
|
||||
} else if (rawAttributes.schoolId) {
|
||||
conditions.push(`"${mainTable}"."schoolId" = :schoolId`);
|
||||
replacements.schoolId = schoolId;
|
||||
} else if (rawAttributes.schoolsId) {
|
||||
conditions.push(`"${mainTable}"."schoolsId" = :schoolId`);
|
||||
replacements.schoolId = schoolId;
|
||||
} else {
|
||||
conditions.push('1 = 0');
|
||||
}
|
||||
if (!globalAccess && !tablesWithoutOrgId.includes(mainTable)) {
|
||||
whereConditions += `"${mainTable}"."organizationId" = :organizationId`;
|
||||
replacements.organizationId = organizationId;
|
||||
}
|
||||
|
||||
if (rawAttributes.deletedAt) {
|
||||
conditions.push(`"${mainTable}"."deletedAt" IS NULL`);
|
||||
}
|
||||
whereConditions += whereConditions ? ' AND ' : '';
|
||||
whereConditions += `"${mainTable}"."deletedAt" IS NULL`;
|
||||
|
||||
return conditions.join(' AND ');
|
||||
return whereConditions;
|
||||
}
|
||||
|
||||
function extractTableName(queryString) {
|
||||
@ -111,22 +70,23 @@ function extractTableName(queryString) {
|
||||
return match ? match[2] : null;
|
||||
}
|
||||
|
||||
function buildQuery(widget, currentUser) {
|
||||
function buildQueryString(widget, currentUser) {
|
||||
let queryString = widget?.query || '';
|
||||
const tableName = extractTableName(queryString);
|
||||
const mainTable = JSON.parse(widget?.data)?.main_table || tableName;
|
||||
const replacements = {};
|
||||
const whereConditions = constructWhereConditions(mainTable, currentUser, replacements);
|
||||
queryString = insertWhereConditions(queryString, whereConditions);
|
||||
return { queryString, replacements };
|
||||
console.log(queryString, 'queryString');
|
||||
return queryString;
|
||||
}
|
||||
|
||||
async function constructWidgetsResults(widgets, currentUser) {
|
||||
const widgetsResults = [];
|
||||
for (const widget of widgets) {
|
||||
if (!widget) continue;
|
||||
const { queryString, replacements } = buildQuery(widget, currentUser);
|
||||
const queryResult = await executeQuery(queryString, replacements);
|
||||
const queryString = buildQueryString(widget, currentUser);
|
||||
const queryResult = await executeQuery(queryString, currentUser);
|
||||
widgetsResults.push(buildWidgetResult(widget, queryResult, queryString));
|
||||
}
|
||||
return widgetsResults;
|
||||
@ -137,10 +97,6 @@ async function fetchWidgetsData(widgets) {
|
||||
axios.get(`${config.flHost}/${config.project_uuid}/project_customization_widgets/${widgetId}.json`)
|
||||
);
|
||||
const widgetResults = widgetPromises ? await Promise.allSettled(widgetPromises) : [];
|
||||
widgetResults
|
||||
.filter(result => result.status === 'rejected')
|
||||
.forEach(result => console.error('Widget fetch failed:', result.reason));
|
||||
|
||||
return widgetResults
|
||||
.filter(result => result.status === 'fulfilled')
|
||||
.map(result => result.value.data);
|
||||
@ -160,7 +116,7 @@ function parseCustomization(role) {
|
||||
}
|
||||
}
|
||||
|
||||
async function findRole(roleId) {
|
||||
async function findRole(roleId, currentUser) {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
try {
|
||||
const role = roleId
|
||||
@ -192,13 +148,28 @@ module.exports = class RolesService {
|
||||
await transaction.rollback();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static async bulkImport(req, res) {
|
||||
static async bulkImport(req, res, sendInvitationEmails = true, host) {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
|
||||
try {
|
||||
const results = await parseImportFile(req, res);
|
||||
await processFile(req, res);
|
||||
const bufferStream = new stream.PassThrough();
|
||||
const results = [];
|
||||
|
||||
await bufferStream.end(Buffer.from(req.file.buffer, "utf-8")); // convert Buffer to Stream
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
bufferStream
|
||||
.pipe(csv())
|
||||
.on('data', (data) => results.push(data))
|
||||
.on('end', async () => {
|
||||
console.log('CSV results', results);
|
||||
resolve();
|
||||
})
|
||||
.on('error', (error) => reject(error));
|
||||
})
|
||||
|
||||
await RolesDBApi.bulkImport(results, {
|
||||
transaction,
|
||||
@ -244,7 +215,7 @@ module.exports = class RolesService {
|
||||
await transaction.rollback();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static async deleteByIds(ids, currentUser) {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
@ -283,22 +254,22 @@ module.exports = class RolesService {
|
||||
|
||||
|
||||
static async addRoleInfo(roleId, userId, key, widgetId, currentUser) {
|
||||
assertWidgetKey(key);
|
||||
assertUuid(widgetId, 'Widget ID');
|
||||
const regexExpForUuid = /^[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}$/gi;
|
||||
const widgetIdIsUUID = regexExpForUuid.test(widgetId);
|
||||
|
||||
const transaction = await db.sequelize.transaction();
|
||||
let role;
|
||||
if (roleId) {
|
||||
role = await RolesDBApi.findBy({ id: roleId }, { transaction });
|
||||
} else {
|
||||
role = await RolesDBApi.findBy({ name: 'User' }, { transaction });
|
||||
}
|
||||
|
||||
if (!role) {
|
||||
throw new ValidationError('rolesNotFound');
|
||||
}
|
||||
|
||||
try {
|
||||
let role;
|
||||
if (roleId) {
|
||||
role = await RolesDBApi.findBy({ id: roleId }, { transaction });
|
||||
} else {
|
||||
role = await RolesDBApi.findBy({ name: 'User' }, { transaction });
|
||||
}
|
||||
|
||||
if (!role) {
|
||||
throw new ValidationError('rolesNotFound');
|
||||
}
|
||||
|
||||
let customization = {};
|
||||
try {
|
||||
customization = JSON.parse(role.role_customization || '{}');
|
||||
@ -306,12 +277,12 @@ module.exports = class RolesService {
|
||||
console.log(e);
|
||||
}
|
||||
|
||||
if (Array.isArray(customization[key])) {
|
||||
if (widgetIdIsUUID && Array.isArray(customization[key])) {
|
||||
const el = customization[key].find((e) => e === widgetId);
|
||||
if (!el) {
|
||||
customization[key].unshift(widgetId);
|
||||
}
|
||||
} else {
|
||||
!el ? customization[key].unshift(widgetId) : null;
|
||||
}
|
||||
|
||||
if (widgetIdIsUUID && !customization[key]) {
|
||||
customization[key] = [widgetId];
|
||||
}
|
||||
|
||||
@ -339,37 +310,33 @@ module.exports = class RolesService {
|
||||
}
|
||||
|
||||
static async removeRoleInfoById(infoId, roleId, key, currentUser) {
|
||||
assertWidgetKey(key);
|
||||
assertUuid(infoId, 'Widget ID');
|
||||
|
||||
const transaction = await db.sequelize.transaction();
|
||||
|
||||
let role;
|
||||
if (roleId) {
|
||||
role = await RolesDBApi.findBy({ id: roleId }, { transaction });
|
||||
} else {
|
||||
role = await RolesDBApi.findBy({ name: 'User' }, { transaction });
|
||||
}
|
||||
if (!role) {
|
||||
await transaction.rollback();
|
||||
throw new ValidationError('rolesNotFound');
|
||||
}
|
||||
|
||||
let customization = {};
|
||||
try {
|
||||
let role;
|
||||
if (roleId) {
|
||||
role = await RolesDBApi.findBy({ id: roleId }, { transaction });
|
||||
} else {
|
||||
role = await RolesDBApi.findBy({ name: 'User' }, { transaction });
|
||||
}
|
||||
if (!role) {
|
||||
throw new ValidationError('rolesNotFound');
|
||||
}
|
||||
customization = JSON.parse(role.role_customization || '{}');
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
|
||||
let customization = {};
|
||||
try {
|
||||
customization = JSON.parse(role.role_customization || '{}');
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
customization[key] = customization[key].filter(
|
||||
(item) => item !== infoId,
|
||||
);
|
||||
|
||||
if (!Array.isArray(customization[key])) {
|
||||
throw badRequest('Widget customization not found.');
|
||||
}
|
||||
|
||||
customization[key] = customization[key].filter(
|
||||
(item) => item !== infoId,
|
||||
);
|
||||
|
||||
await axios.delete(`${config.flHost}/${config.project_uuid}/project_customization_widgets/${infoId}.json`);
|
||||
const response = await axios.delete(`${config.flHost}/${config.project_uuid}/project_customization_widgets/${infoId}.json`);
|
||||
const { status } = await response;
|
||||
try {
|
||||
const result = await RolesDBApi.update(
|
||||
role.id,
|
||||
{
|
||||
@ -395,8 +362,14 @@ module.exports = class RolesService {
|
||||
static async getRoleInfoByKey(key, roleId, currentUser) {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
|
||||
const organizationId = currentUser.organizationId;
|
||||
let globalAccess = currentUser.app_role?.globalAccess;
|
||||
let queryString = '';
|
||||
|
||||
|
||||
|
||||
try {
|
||||
const role = await findRole(roleId);
|
||||
const role = await findRole(roleId, currentUser);
|
||||
const customization = parseCustomization(role);
|
||||
|
||||
let result;
|
||||
@ -411,8 +384,13 @@ module.exports = class RolesService {
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
await transaction.rollback();
|
||||
throw error;
|
||||
} finally {
|
||||
if (transaction.finished !== 'commit') {
|
||||
await transaction.rollback();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -1,50 +1,22 @@
|
||||
const db = require('../db/models');
|
||||
const SchoolsDBApi = require('../db/api/schools');
|
||||
const parseImportFile = require('./importFileParser');
|
||||
const processFile = require("../middlewares/upload");
|
||||
const ValidationError = require('./notifications/errors/validation');
|
||||
const csv = require('csv-parser');
|
||||
const axios = require('axios');
|
||||
const config = require('../config');
|
||||
const stream = require('stream');
|
||||
|
||||
const SCHOOL_STATUSES = new Set(['active', 'setup', 'suspended']);
|
||||
|
||||
function cleanString(value) {
|
||||
if (value === undefined || value === null) return null;
|
||||
const text = String(value).trim();
|
||||
return text || null;
|
||||
}
|
||||
|
||||
function normalizeSchoolData(data = {}) {
|
||||
const normalized = {
|
||||
name: cleanString(data.name),
|
||||
nif: cleanString(data.nif),
|
||||
phone: cleanString(data.phone),
|
||||
email: cleanString(data.email),
|
||||
province: cleanString(data.province),
|
||||
municipality: cleanString(data.municipality),
|
||||
address: cleanString(data.address),
|
||||
logoUrl: cleanString(data.logoUrl),
|
||||
status: cleanString(data.status) || 'active',
|
||||
};
|
||||
|
||||
if (!normalized.name) {
|
||||
throw new ValidationError('schoolsNameRequired');
|
||||
}
|
||||
|
||||
if (normalized.email && !normalized.email.includes('@')) {
|
||||
throw new ValidationError('schoolsEmailInvalid');
|
||||
}
|
||||
|
||||
if (!SCHOOL_STATUSES.has(normalized.status)) {
|
||||
throw new ValidationError('schoolsStatusInvalid');
|
||||
}
|
||||
|
||||
return normalized;
|
||||
}
|
||||
|
||||
module.exports = class SchoolsService {
|
||||
static async create(data, currentUser) {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
try {
|
||||
await SchoolsDBApi.create(
|
||||
normalizeSchoolData(data),
|
||||
data,
|
||||
{
|
||||
currentUser,
|
||||
transaction,
|
||||
@ -56,13 +28,28 @@ module.exports = class SchoolsService {
|
||||
await transaction.rollback();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static async bulkImport(req, res) {
|
||||
static async bulkImport(req, res, sendInvitationEmails = true, host) {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
|
||||
try {
|
||||
const results = await parseImportFile(req, res);
|
||||
await processFile(req, res);
|
||||
const bufferStream = new stream.PassThrough();
|
||||
const results = [];
|
||||
|
||||
await bufferStream.end(Buffer.from(req.file.buffer, "utf-8")); // convert Buffer to Stream
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
bufferStream
|
||||
.pipe(csv())
|
||||
.on('data', (data) => results.push(data))
|
||||
.on('end', async () => {
|
||||
console.log('CSV results', results);
|
||||
resolve();
|
||||
})
|
||||
.on('error', (error) => reject(error));
|
||||
})
|
||||
|
||||
await SchoolsDBApi.bulkImport(results, {
|
||||
transaction,
|
||||
@ -83,7 +70,7 @@ module.exports = class SchoolsService {
|
||||
try {
|
||||
let schools = await SchoolsDBApi.findBy(
|
||||
{id},
|
||||
{ transaction, currentUser },
|
||||
{transaction},
|
||||
);
|
||||
|
||||
if (!schools) {
|
||||
@ -94,7 +81,7 @@ module.exports = class SchoolsService {
|
||||
|
||||
const updatedSchools = await SchoolsDBApi.update(
|
||||
id,
|
||||
normalizeSchoolData(data),
|
||||
data,
|
||||
{
|
||||
currentUser,
|
||||
transaction,
|
||||
@ -108,7 +95,7 @@ module.exports = class SchoolsService {
|
||||
await transaction.rollback();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static async deleteByIds(ids, currentUser) {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
|
||||
@ -3,35 +3,6 @@ const ValidationError = require('./notifications/errors/validation');
|
||||
|
||||
const Sequelize = db.Sequelize;
|
||||
const Op = Sequelize.Op;
|
||||
const { getCurrentUserSchoolId } = require('../db/api/schoolScope');
|
||||
|
||||
const MAX_SEARCH_QUERY_LENGTH = 100;
|
||||
const MAX_RESULTS_PER_TABLE = 10;
|
||||
const MAX_TOTAL_SEARCH_RESULTS = 100;
|
||||
|
||||
function normalizeSearchQuery(searchQuery) {
|
||||
if (typeof searchQuery !== 'string') {
|
||||
throw new ValidationError('iam.errors.searchQueryRequired');
|
||||
}
|
||||
|
||||
const normalized = searchQuery.trim();
|
||||
if (!normalized) {
|
||||
throw new ValidationError('iam.errors.searchQueryRequired');
|
||||
}
|
||||
|
||||
if (normalized.length > MAX_SEARCH_QUERY_LENGTH) {
|
||||
const error = new Error(`Search query is too long. Maximum length is ${MAX_SEARCH_QUERY_LENGTH} characters.`);
|
||||
error.code = 400;
|
||||
throw error;
|
||||
}
|
||||
|
||||
return normalized;
|
||||
}
|
||||
|
||||
function escapeLikePattern(value) {
|
||||
return value.replace(/[\\%_]/g, '\\$&');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param {string} permission
|
||||
@ -43,10 +14,7 @@ async function checkPermissions(permission, currentUser) {
|
||||
throw new ValidationError('auth.unauthorized');
|
||||
}
|
||||
|
||||
const customPermissions = Array.isArray(currentUser.custom_permissions)
|
||||
? currentUser.custom_permissions
|
||||
: [];
|
||||
const userPermission = customPermissions.find(
|
||||
const userPermission = currentUser.custom_permissions.find(
|
||||
(cp) => cp.name === permission,
|
||||
);
|
||||
|
||||
@ -54,21 +22,25 @@ async function checkPermissions(permission, currentUser) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!currentUser.app_role) {
|
||||
throw new ValidationError('auth.forbidden');
|
||||
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;
|
||||
}
|
||||
|
||||
const permissions = await currentUser.app_role.getPermissions();
|
||||
|
||||
return !!permissions.find((p) => p.name === permission);
|
||||
}
|
||||
|
||||
module.exports = class SearchService {
|
||||
static async search(searchQuery, currentUser, globalAccess) {
|
||||
const normalizedSearchQuery = normalizeSearchQuery(searchQuery);
|
||||
const likeSearchQuery = `%${escapeLikePattern(normalizedSearchQuery)}%`;
|
||||
const lowerSearchQuery = normalizedSearchQuery.toLowerCase();
|
||||
|
||||
static async search(searchQuery, currentUser , organizationId, globalAccess) {
|
||||
try {
|
||||
if (!searchQuery) {
|
||||
throw new ValidationError('iam.errors.searchQueryRequired');
|
||||
}
|
||||
const tableColumns = {
|
||||
|
||||
|
||||
@ -455,87 +427,72 @@ module.exports = class SearchService {
|
||||
|
||||
let allFoundRecords = [];
|
||||
|
||||
for (const [tableName, attributesToSearch] of Object.entries(tableColumns)) {
|
||||
const model = db[tableName];
|
||||
if (!model) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const attributesIntToSearch = columnsInt[tableName] || [];
|
||||
const whereCondition = {
|
||||
[Op.or]: [
|
||||
...attributesToSearch.map(attribute => ({
|
||||
[attribute]: {
|
||||
[Op.iLike]: likeSearchQuery,
|
||||
},
|
||||
})),
|
||||
...attributesIntToSearch.map(attribute => (
|
||||
Sequelize.where(
|
||||
Sequelize.cast(Sequelize.col(`${tableName}.${attribute}`), 'varchar'),
|
||||
{ [Op.iLike]: likeSearchQuery }
|
||||
)
|
||||
)),
|
||||
],
|
||||
};
|
||||
|
||||
if (!globalAccess) {
|
||||
const schoolId = getCurrentUserSchoolId(currentUser);
|
||||
const rawAttributes = model.rawAttributes || {};
|
||||
|
||||
if (!schoolId) {
|
||||
whereCondition.id = null;
|
||||
} else if (tableName === 'schools') {
|
||||
whereCondition.id = schoolId;
|
||||
} else if (rawAttributes.schoolId) {
|
||||
whereCondition.schoolId = schoolId;
|
||||
} else if (rawAttributes.schoolsId) {
|
||||
whereCondition.schoolsId = schoolId;
|
||||
} else {
|
||||
whereCondition.id = null;
|
||||
}
|
||||
}
|
||||
|
||||
const hasPermission = await checkPermissions(`READ_${tableName.toUpperCase()}`, currentUser);
|
||||
if (!hasPermission) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const foundRecords = await model.findAll({
|
||||
where: whereCondition,
|
||||
attributes: [...new Set([...attributesToSearch, 'id', ...attributesIntToSearch])],
|
||||
limit: MAX_RESULTS_PER_TABLE,
|
||||
});
|
||||
|
||||
const modifiedRecords = foundRecords.map((record) => {
|
||||
const matchAttribute = [];
|
||||
|
||||
for (const attribute of attributesToSearch) {
|
||||
const value = record[attribute];
|
||||
if (typeof value === 'string' && value.toLowerCase().includes(lowerSearchQuery)) {
|
||||
matchAttribute.push(attribute);
|
||||
}
|
||||
}
|
||||
|
||||
for (const attribute of attributesIntToSearch) {
|
||||
const castedValue = String(record[attribute] ?? '');
|
||||
if (castedValue.toLowerCase().includes(lowerSearchQuery)) {
|
||||
matchAttribute.push(attribute);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
...record.get(),
|
||||
matchAttribute,
|
||||
tableName,
|
||||
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}%` }
|
||||
)
|
||||
)),
|
||||
],
|
||||
};
|
||||
});
|
||||
|
||||
allFoundRecords = allFoundRecords.concat(modifiedRecords);
|
||||
if (allFoundRecords.length >= MAX_TOTAL_SEARCH_RESULTS) {
|
||||
break;
|
||||
|
||||
if (!globalAccess && tableName !== 'organizations' && organizationId) {
|
||||
whereCondition.organizationId = organizationId;
|
||||
}
|
||||
|
||||
|
||||
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.slice(0, MAX_TOTAL_SEARCH_RESULTS);
|
||||
return allFoundRecords;
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user