fixed project settings issue

This commit is contained in:
Dmitri 2026-03-31 12:45:22 +04:00
parent 34770304c5
commit 91c24165bf
8 changed files with 129 additions and 87 deletions

View File

@ -19,7 +19,11 @@ class AssetsDBApi extends GenericDBApi {
}
static get ENUM_FIELDS() {
return ['asset_type', 'type', 'is_public', 'projectId'];
return ['asset_type', 'type', 'is_public'];
}
static get UUID_FIELDS() {
return ['projectId'];
}
static get CSV_FIELDS() {

View File

@ -26,6 +26,17 @@ class GenericDBApi {
return [];
}
/**
* UUID fields that require validation before querying.
* These are typically foreign key fields like 'projectId'.
* Invalid UUIDs will return empty results instead of causing DB errors.
* Override in subclass to specify fields.
* Example: return ['projectId', 'userId'];
*/
static get UUID_FIELDS() {
return [];
}
static get RELATION_FILTERS() {
return [];
}
@ -265,13 +276,10 @@ class GenericDBApi {
let include = [...this.FIND_ALL_INCLUDES];
if (filter.id) {
const validId = Utils.uuid(filter.id);
if (validId) {
where.id = validId;
} else {
// Invalid UUID provided - return empty results immediately
if (!Utils.isValidUuid(filter.id)) {
return { rows: [], count: 0 };
}
where.id = filter.id;
}
for (const field of this.SEARCHABLE_FIELDS) {
@ -299,6 +307,16 @@ class GenericDBApi {
}
}
// Validate UUID fields - return empty results for invalid UUIDs
for (const field of this.UUID_FIELDS) {
if (filter[field] !== undefined) {
if (!Utils.isValidUuid(filter[field])) {
return { rows: [], count: 0 };
}
where[field] = filter[field];
}
}
if (filter.active !== undefined) {
where.active = filter.active === true || filter.active === 'true';
}
@ -316,11 +334,7 @@ class GenericDBApi {
for (const rel of this.RELATION_FILTERS) {
if (filter[rel.filterKey]) {
const searchTerms = filter[rel.filterKey].split('|');
// Filter out null UUIDs - only keep valid ones
const validUuids = searchTerms
.map((term) => Utils.uuid(term))
.filter((id) => id !== null);
const validUuids = Utils.filterValidUuids(searchTerms);
// Build OR conditions array
const orConditions = [];
@ -384,12 +398,15 @@ class GenericDBApi {
let where = {};
if (query) {
where = {
[Op.or]: [
{ id: Utils.uuid(query) },
Utils.ilike(this.TABLE_NAME, this.AUTOCOMPLETE_FIELD, query),
],
};
const orConditions = [
Utils.ilike(this.TABLE_NAME, this.AUTOCOMPLETE_FIELD, query),
];
if (Utils.isValidUuid(query)) {
orConditions.unshift({ id: query });
}
where = { [Op.or]: orConditions };
}
const records = await this.MODEL.findAll({

View File

@ -93,6 +93,9 @@ class Project_audio_tracksDBApi extends GenericDBApi {
let where = {};
const terms = filter.project ? filter.project.split('|') : [];
const validUuids = Utils.filterValidUuids(terms);
let include = [
{
model: db.projects,
@ -100,18 +103,12 @@ class Project_audio_tracksDBApi extends GenericDBApi {
where: filter.project
? {
[Op.or]: [
{
id: {
[Op.in]: filter.project
.split('|')
.map((term) => Utils.uuid(term)),
},
},
...(validUuids.length > 0
? [{ id: { [Op.in]: validUuids } }]
: []),
{
name: {
[Op.or]: filter.project
.split('|')
.map((term) => ({ [Op.iLike]: `%${term}%` })),
[Op.or]: terms.map((term) => ({ [Op.iLike]: `%${term}%` })),
},
},
],
@ -123,7 +120,10 @@ class Project_audio_tracksDBApi extends GenericDBApi {
include[0] = applyRuntimeProjectFilter(include[0], options);
if (filter.id) {
where.id = Utils.uuid(filter.id);
if (!Utils.isValidUuid(filter.id)) {
return { rows: [], count: 0 };
}
where.id = filter.id;
}
for (const field of this.SEARCHABLE_FIELDS) {

View File

@ -111,6 +111,8 @@ class Project_element_defaultsDBApi extends GenericDBApi {
// Support both 'project' and 'projectId' query params
const projectFilter = filter.project || filter.projectId;
const terms = projectFilter ? projectFilter.split('|') : [];
const validUuids = Utils.filterValidUuids(terms);
let include = [
{
@ -119,18 +121,12 @@ class Project_element_defaultsDBApi extends GenericDBApi {
where: projectFilter
? {
[Op.or]: [
{
id: {
[Op.in]: projectFilter
.split('|')
.map((term) => Utils.uuid(term)),
},
},
...(validUuids.length > 0
? [{ id: { [Op.in]: validUuids } }]
: []),
{
name: {
[Op.or]: projectFilter
.split('|')
.map((term) => ({ [Op.iLike]: `%${term}%` })),
[Op.or]: terms.map((term) => ({ [Op.iLike]: `%${term}%` })),
},
},
],
@ -145,7 +141,10 @@ class Project_element_defaultsDBApi extends GenericDBApi {
];
if (filter.id) {
where.id = Utils.uuid(filter.id);
if (!Utils.isValidUuid(filter.id)) {
return { rows: [], count: 0 };
}
where.id = filter.id;
}
for (const field of this.SEARCHABLE_FIELDS) {

View File

@ -136,7 +136,10 @@ class ProjectsDBApi extends GenericDBApi {
let include = [];
if (filter.id) {
where.id = Utils.uuid(filter.id);
if (!Utils.isValidUuid(filter.id)) {
return { rows: [], count: 0 };
}
where.id = filter.id;
}
for (const field of this.SEARCHABLE_FIELDS) {

View File

@ -125,6 +125,9 @@ class Tour_pagesDBApi extends GenericDBApi {
let where = {};
const terms = filter.project ? filter.project.split('|') : [];
const validUuids = Utils.filterValidUuids(terms);
let include = [
{
model: db.projects,
@ -132,18 +135,12 @@ class Tour_pagesDBApi extends GenericDBApi {
where: filter.project
? {
[Op.or]: [
{
id: {
[Op.in]: filter.project
.split('|')
.map((term) => Utils.uuid(term)),
},
},
...(validUuids.length > 0
? [{ id: { [Op.in]: validUuids } }]
: []),
{
name: {
[Op.or]: filter.project
.split('|')
.map((term) => ({ [Op.iLike]: `%${term}%` })),
[Op.or]: terms.map((term) => ({ [Op.iLike]: `%${term}%` })),
},
},
],
@ -155,7 +152,10 @@ class Tour_pagesDBApi extends GenericDBApi {
include[0] = applyRuntimeProjectFilter(include[0], options);
if (filter.id) {
where.id = Utils.uuid(filter.id);
if (!Utils.isValidUuid(filter.id)) {
return { rows: [], count: 0 };
}
where.id = filter.id;
}
for (const field of this.SEARCHABLE_FIELDS) {

View File

@ -298,6 +298,9 @@ module.exports = class UsersDBApi {
offset = currentPage * limit;
const appRoleTerms = filter.app_role ? filter.app_role.split('|') : [];
const appRoleValidUuids = Utils.filterValidUuids(appRoleTerms);
let include = [
{
model: db.roles,
@ -306,18 +309,14 @@ module.exports = class UsersDBApi {
where: filter.app_role
? {
[Op.or]: [
{
id: {
[Op.in]: filter.app_role
.split('|')
.map((term) => Utils.uuid(term)),
},
},
...(appRoleValidUuids.length > 0
? [{ id: { [Op.in]: appRoleValidUuids } }]
: []),
{
name: {
[Op.or]: filter.app_role
.split('|')
.map((term) => ({ [Op.iLike]: `%${term}%` })),
[Op.or]: appRoleTerms.map((term) => ({
[Op.iLike]: `%${term}%`,
})),
},
},
],
@ -339,10 +338,10 @@ module.exports = class UsersDBApi {
if (filter) {
if (filter.id) {
where = {
...where,
['id']: Utils.uuid(filter.id),
};
if (!Utils.isValidUuid(filter.id)) {
return { rows: [], count: 0 };
}
where = { ...where, id: filter.id };
}
if (filter.firstName) {
@ -480,6 +479,7 @@ module.exports = class UsersDBApi {
if (filter.custom_permissions) {
const searchTerms = filter.custom_permissions.split('|');
const permissionValidUuids = Utils.filterValidUuids(searchTerms);
include = [
{
@ -490,11 +490,9 @@ module.exports = class UsersDBApi {
searchTerms.length > 0
? {
[Op.or]: [
{
id: {
[Op.in]: searchTerms.map((term) => Utils.uuid(term)),
},
},
...(permissionValidUuids.length > 0
? [{ id: { [Op.in]: permissionValidUuids } }]
: []),
{
name: {
[Op.or]: searchTerms.map((term) => ({
@ -568,12 +566,13 @@ module.exports = class UsersDBApi {
let where = {};
if (query) {
where = {
[Op.or]: [
{ ['id']: Utils.uuid(query) },
Utils.ilike('users', 'firstName', query),
],
};
const orConditions = [Utils.ilike('users', 'firstName', query)];
if (Utils.isValidUuid(query)) {
orConditions.unshift({ id: query });
}
where = { [Op.or]: orConditions };
}
const records = await db.users.findAll({

View File

@ -1,25 +1,45 @@
const validator = require('validator');
const { v4: uuidv4 } = require('uuid');
const Sequelize = require('./models').Sequelize;
module.exports = class Utils {
/**
* Validates a UUID string.
* @param {*} value - The value to validate as UUID
* @returns {string|null} - The valid UUID string, or null if invalid
* Check if value is a valid UUID
* @param {*} value - The value to check
* @returns {boolean} - True if valid UUID, false otherwise
*/
static uuid(value) {
if (value && validator.isUUID(String(value))) {
return value;
}
return null;
static isValidUuid(value) {
return Boolean(value && validator.isUUID(String(value)));
}
/**
* Generate a new UUID v4
* @returns {string} - A new UUID v4 string
*/
static generateUuid() {
return uuidv4();
}
/**
* Filter array to only valid UUIDs
* @param {Array} values - Array of values to filter
* @returns {string[]} - Array containing only valid UUIDs
*/
static filterValidUuids(values) {
return values.filter((v) => this.isValidUuid(v));
}
/**
* Case-insensitive LIKE query
* @param {string} model - The model/table name
* @param {string} column - The column name
* @param {string} value - The value to search for
* @returns {Object} - Sequelize where clause
*/
static ilike(model, column, value) {
return Sequelize.where(
Sequelize.fn('lower', Sequelize.col(`${model}.${column}`)),
{
[Sequelize.Op.like]: `%${value}%`.toLowerCase(),
},
{ [Sequelize.Op.like]: `%${value}%`.toLowerCase() },
);
}
};