Compare commits

..

11 Commits

Author SHA1 Message Date
Dmitri
e890ccf2ed improved project structure (DB, BE, FE) 2026-03-19 07:12:29 +04:00
Dmitri
70e3c28f6a updated dependencies 2026-03-18 11:53:34 +04:00
Dmitri
5ec464e6ce gitignore updated 2026-03-18 11:00:22 +04:00
Flatlogic Bot
1b5c13c8ae constructor (UI elements basic) 2026-03-18 06:43:39 +00:00
Flatlogic Bot
b4fe0dde81 Autosave: 20260318-063526 2026-03-18 06:35:27 +00:00
Flatlogic Bot
06dd524cd0 Revert to version 4268405 2026-03-17 15:35:44 +00:00
Flatlogic Bot
2680417aae Autosave: 20260317-153203 2026-03-17 15:32:03 +00:00
Flatlogic Bot
42684051c3 Basic constructor 2026-03-17 15:26:24 +00:00
Flatlogic Bot
8a20fdbd9e Autosave: 20260317-145604 2026-03-17 14:56:05 +00:00
Flatlogic Bot
a8942e7c5d Autosave: 20260317-124943 2026-03-17 12:49:43 +00:00
Flatlogic Bot
d3b659f3bc Autosave: 20260317-121506 2026-03-17 12:15:06 +00:00
400 changed files with 39849 additions and 103062 deletions

3
.gitignore vendored
View File

@ -4,3 +4,6 @@ node_modules/
*/node_modules/ */node_modules/
**/node_modules/ **/node_modules/
*/build/ */build/
package-lock.json
CLAUDE.md
.claude/

18071
backend/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -11,47 +11,52 @@
"watch": "node watcher.js" "watch": "node watcher.js"
}, },
"dependencies": { "dependencies": {
"@aws-sdk/client-s3": "^3.1010.0", "@aws-sdk/client-s3": "^3.1011.0",
"@google-cloud/storage": "^5.18.2", "@google-cloud/storage": "^7.0.0",
"axios": "^1.6.7", "axios": "^1.13.0",
"bcrypt": "5.1.1", "bcrypt": "^6.0.0",
"chokidar": "^4.0.3", "chokidar": "^4.0.3",
"cors": "2.8.5", "cors": "^2.8.6",
"csv-parser": "^3.0.0", "csv-parser": "^3.2.0",
"dotenv": "^16.4.0",
"express": "4.18.2", "express": "4.18.2",
"express-validator": "^7.0.0",
"formidable": "1.2.2", "formidable": "1.2.2",
"helmet": "4.1.1", "helmet": "^8.0.0",
"joi": "^17.13.0",
"json2csv": "^5.0.7", "json2csv": "^5.0.7",
"jsonwebtoken": "8.5.1", "jsonwebtoken": "^9.0.0",
"lodash": "4.17.21", "lodash": "^4.17.23",
"moment": "2.30.1", "moment": "2.30.1",
"multer": "^1.4.4", "multer": "^2.0.0",
"mysql2": "2.2.5", "mysql2": "2.2.5",
"nodemailer": "6.9.9", "nodemailer": "6.9.9",
"passport": "^0.7.0", "passport": "^0.7.0",
"passport-google-oauth2": "^0.2.0", "passport-google-oauth2": "^0.2.0",
"passport-jwt": "^4.0.1", "passport-jwt": "^4.0.1",
"passport-microsoft": "^0.1.0", "passport-microsoft": "^2.0.0",
"pg": "8.4.1", "pg": "^8.20.0",
"pino": "^9.0.0",
"pino-pretty": "^11.0.0",
"pg-hstore": "2.3.4", "pg-hstore": "2.3.4",
"sequelize": "6.35.2", "sequelize": "^6.37.0",
"sequelize-json-schema": "^2.1.1", "sequelize-json-schema": "^2.1.1",
"sqlite": "4.0.15", "sqlite": "4.0.15",
"swagger-jsdoc": "^6.2.8", "swagger-jsdoc": "^6.2.8",
"swagger-ui-express": "^5.0.0", "swagger-ui-express": "^5.0.0",
"tedious": "^18.2.4" "tedious": "^18.6.0"
}, },
"engines": { "engines": {
"node": ">=18" "node": ">=18"
}, },
"private": true, "private": true,
"devDependencies": { "devDependencies": {
"cross-env": "7.0.3", "cross-env": "^7.0.3",
"eslint": "^8.23.1", "eslint": "^8.57.0",
"eslint-plugin-import": "^2.29.1", "eslint-plugin-import": "^2.29.1",
"mocha": "8.1.3", "mocha": "^10.0.0",
"node-mocks-http": "1.9.0", "node-mocks-http": "^1.17.0",
"nodemon": "2.0.5", "nodemon": "^3.0.0",
"sequelize-cli": "6.6.2" "sequelize-cli": "^6.6.5"
} }
} }

View File

@ -1,37 +1,10 @@
const os = require('os'); const os = require('os');
const fs = require('fs');
const path = require('path'); const path = require('path');
const envFilePath = path.resolve(__dirname, '../.env'); require('dotenv').config({ path: path.resolve(__dirname, '../.env') });
if (fs.existsSync(envFilePath)) { const { validateEnv } = require('./utils/env-validation');
const envContent = fs.readFileSync(envFilePath, 'utf8'); validateEnv();
envContent.split('\n').forEach((line) => {
const trimmedLine = line.trim();
if (!trimmedLine || trimmedLine.startsWith('#')) {
return;
}
const delimiterIndex = trimmedLine.indexOf('=');
if (delimiterIndex === -1) {
return;
}
const key = trimmedLine.slice(0, delimiterIndex).trim();
const rawValue = trimmedLine.slice(delimiterIndex + 1).trim();
if (!key || Object.prototype.hasOwnProperty.call(process.env, key)) {
return;
}
const unquotedValue = rawValue.replace(/^['"]|['"]$/g, '');
process.env[key] = unquotedValue;
});
}
const config = { const config = {
gcloud: { gcloud: {
@ -48,9 +21,9 @@ const config = {
bcrypt: { bcrypt: {
saltRounds: 12 saltRounds: 12
}, },
admin_pass: "88dbeaf8", admin_pass: process.env.ADMIN_PASS || "88dbeaf8",
user_pass: "c3baadeda5c6", user_pass: process.env.USER_PASS || "c3baadeda5c6",
admin_email: "admin@flatlogic.com", admin_email: process.env.ADMIN_EMAIL || "admin@flatlogic.com",
providers: { providers: {
LOCAL: 'local', LOCAL: 'local',
GOOGLE: 'google', GOOGLE: 'google',

View File

@ -1,297 +1,76 @@
const GenericDBApi = require('./base.api');
const db = require('../models'); const db = require('../models');
const Utils = require('../utils'); const Utils = require('../utils');
const Sequelize = db.Sequelize; const Sequelize = db.Sequelize;
const Op = Sequelize.Op; const Op = Sequelize.Op;
module.exports = class Access_logsDBApi { class Access_logsDBApi extends GenericDBApi {
static get MODEL() {
return db.access_logs;
}
static async create(data, options) {
const currentUser = (options && options.currentUser) || { id: null }; static get TABLE_NAME() {
const transaction = (options && options.transaction) || undefined; return 'access_logs';
}
const access_logs = await db.access_logs.create(
{ static get SEARCHABLE_FIELDS() {
id: data.id || undefined, return ['path', 'ip_address', 'user_agent'];
}
environment: data.environment
|| static get RANGE_FIELDS() {
null return ['accessed_at'];
, }
path: data.path static get ENUM_FIELDS() {
|| return ['environment'];
null }
,
static get CSV_FIELDS() {
ip_address: data.ip_address return ['id', 'environment', 'path', 'ip_address', 'user_agent', 'accessed_at', 'createdAt'];
|| }
null
, static get AUTOCOMPLETE_FIELD() {
return 'path';
user_agent: data.user_agent }
||
null static get ASSOCIATIONS() {
, return [
{ field: 'project', setter: 'setProject', isArray: false },
accessed_at: data.accessed_at { field: 'user', setter: 'setUser', isArray: false },
|| ];
null }
,
static get FIND_BY_INCLUDES() {
importHash: data.importHash || null, return [
createdById: currentUser.id, { association: 'project' },
updatedById: currentUser.id, { association: 'user' },
}, ];
{ transaction }, }
);
static getFieldMapping(data) {
return {
await access_logs.setProject( data.project || null, { id: data.id || undefined,
transaction, environment: data.environment || null,
}); path: data.path || null,
ip_address: data.ip_address || null,
await access_logs.setUser( data.user || null, { user_agent: data.user_agent || null,
transaction, accessed_at: data.accessed_at || null,
}); };
}
static async findAll(filter = {}, options = {}) {
filter = filter || {};
const limit = filter.limit || 0;
const currentPage = +filter.page || 0;
return access_logs; const offset = currentPage * limit;
}
let where = {};
static async bulkImport(data, options) {
const currentUser = (options && options.currentUser) || { id: null };
const transaction = (options && options.transaction) || undefined;
// Prepare data - wrapping individual data transformations in a map() method
const access_logsData = data.map((item, index) => ({
id: item.id || undefined,
environment: item.environment
||
null
,
path: item.path
||
null
,
ip_address: item.ip_address
||
null
,
user_agent: item.user_agent
||
null
,
accessed_at: item.accessed_at
||
null
,
importHash: item.importHash || null,
createdById: currentUser.id,
updatedById: currentUser.id,
createdAt: new Date(Date.now() + index * 1000),
}));
// Bulk create items
const access_logs = await db.access_logs.bulkCreate(access_logsData, { transaction });
// For each item created, replace relation files
return access_logs;
}
static async update(id, data, options) {
const currentUser = (options && options.currentUser) || {id: null};
const transaction = (options && options.transaction) || undefined;
const access_logs = await db.access_logs.findByPk(id, {transaction});
const updatePayload = {};
if (data.environment !== undefined) updatePayload.environment = data.environment;
if (data.path !== undefined) updatePayload.path = data.path;
if (data.ip_address !== undefined) updatePayload.ip_address = data.ip_address;
if (data.user_agent !== undefined) updatePayload.user_agent = data.user_agent;
if (data.accessed_at !== undefined) updatePayload.accessed_at = data.accessed_at;
updatePayload.updatedById = currentUser.id;
await access_logs.update(updatePayload, {transaction});
if (data.project !== undefined) {
await access_logs.setProject(
data.project,
{ transaction }
);
}
if (data.user !== undefined) {
await access_logs.setUser(
data.user,
{ transaction }
);
}
return access_logs;
}
static async deleteByIds(ids, options) {
const currentUser = (options && options.currentUser) || { id: null };
const transaction = (options && options.transaction) || undefined;
const access_logs = await db.access_logs.findAll({
where: {
id: {
[Op.in]: ids,
},
},
transaction,
});
await db.sequelize.transaction(async (transaction) => {
for (const record of access_logs) {
await record.update(
{deletedBy: currentUser.id},
{transaction}
);
}
for (const record of access_logs) {
await record.destroy({transaction});
}
});
return access_logs;
}
static async remove(id, options) {
const currentUser = (options && options.currentUser) || {id: null};
const transaction = (options && options.transaction) || undefined;
const access_logs = await db.access_logs.findByPk(id, options);
await access_logs.update({
deletedBy: currentUser.id
}, {
transaction,
});
await access_logs.destroy({
transaction
});
return access_logs;
}
static async findBy(where, options) {
const transaction = (options && options.transaction) || undefined;
const access_logs = await db.access_logs.findOne({
where,
transaction,
});
if (!access_logs) {
return access_logs;
}
const output = access_logs.get({plain: true});
output.project = await access_logs.getProject({
transaction
});
output.user = await access_logs.getUser({
transaction
});
return output;
}
static async findAll(
filter,
options
) {
const limit = filter.limit || 0;
let offset = 0;
let where = {};
const currentPage = +filter.page;
offset = currentPage * limit;
let include = [ let include = [
{ {
model: db.projects, model: db.projects,
as: 'project', as: 'project',
where: filter.project ? { where: filter.project ? {
[Op.or]: [ [Op.or]: [
{ id: { [Op.in]: filter.project.split('|').map(term => Utils.uuid(term)) } }, { id: { [Op.in]: filter.project.split('|').map(term => Utils.uuid(term)) } },
@ -302,13 +81,10 @@ module.exports = class Access_logsDBApi {
}, },
] ]
} : {}, } : {},
}, },
{ {
model: db.users, model: db.users,
as: 'user', as: 'user',
where: filter.user ? { where: filter.user ? {
[Op.or]: [ [Op.or]: [
{ id: { [Op.in]: filter.user.split('|').map(term => Utils.uuid(term)) } }, { id: { [Op.in]: filter.user.split('|').map(term => Utils.uuid(term)) } },
@ -319,196 +95,78 @@ module.exports = class Access_logsDBApi {
}, },
] ]
} : {}, } : {},
}, },
]; ];
if (filter) { if (filter.id) {
if (filter.id) { where.id = Utils.uuid(filter.id);
where = {
...where,
['id']: Utils.uuid(filter.id),
};
}
if (filter.path) {
where = {
...where,
[Op.and]: Utils.ilike(
'access_logs',
'path',
filter.path,
),
};
}
if (filter.ip_address) {
where = {
...where,
[Op.and]: Utils.ilike(
'access_logs',
'ip_address',
filter.ip_address,
),
};
}
if (filter.user_agent) {
where = {
...where,
[Op.and]: Utils.ilike(
'access_logs',
'user_agent',
filter.user_agent,
),
};
}
if (filter.accessed_atRange) {
const [start, end] = filter.accessed_atRange;
if (start !== undefined && start !== null && start !== '') {
where = {
...where,
accessed_at: {
...where.accessed_at,
[Op.gte]: start,
},
};
}
if (end !== undefined && end !== null && end !== '') {
where = {
...where,
accessed_at: {
...where.accessed_at,
[Op.lte]: end,
},
};
}
}
if (filter.active !== undefined) {
where = {
...where,
active: filter.active === true || filter.active === 'true'
};
}
if (filter.environment) {
where = {
...where,
environment: filter.environment,
};
}
if (filter.createdAtRange) {
const [start, end] = filter.createdAtRange;
if (start !== undefined && start !== null && start !== '') {
where = {
...where,
['createdAt']: {
...where.createdAt,
[Op.gte]: start,
},
};
}
if (end !== undefined && end !== null && end !== '') {
where = {
...where,
['createdAt']: {
...where.createdAt,
[Op.lte]: end,
},
};
}
}
}
const queryOptions = {
where,
include,
distinct: true,
order: filter.field && filter.sort
? [[filter.field, filter.sort]]
: [['createdAt', 'desc']],
transaction: options?.transaction,
logging: console.log
};
if (!options?.countOnly) {
queryOptions.limit = limit ? Number(limit) : undefined;
queryOptions.offset = offset ? Number(offset) : undefined;
}
try {
const { rows, count } = await db.access_logs.findAndCountAll(queryOptions);
return {
rows: options?.countOnly ? [] : rows,
count: count
};
} catch (error) {
console.error('Error executing query:', error);
throw error;
}
} }
static async findAllAutocomplete(query, limit, offset, ) { for (const field of this.SEARCHABLE_FIELDS) {
let where = {}; if (filter[field]) {
where[Op.and] = Utils.ilike(this.TABLE_NAME, field, filter[field]);
}
if (query) {
where = {
[Op.or]: [
{ ['id']: Utils.uuid(query) },
Utils.ilike(
'access_logs',
'path',
query,
),
],
};
}
const records = await db.access_logs.findAll({
attributes: [ 'id', 'path' ],
where,
limit: limit ? Number(limit) : undefined,
offset: offset ? Number(offset) : undefined,
orderBy: [['path', 'ASC']],
});
return records.map((record) => ({
id: record.id,
label: record.path,
}));
} }
for (const field of this.RANGE_FIELDS) {
const rangeKey = `${field}Range`;
if (filter[rangeKey]) {
const [start, end] = filter[rangeKey];
if (start !== undefined && start !== null && start !== '') {
where[field] = { ...where[field], [Op.gte]: start };
}
if (end !== undefined && end !== null && end !== '') {
where[field] = { ...where[field], [Op.lte]: end };
}
}
}
}; for (const field of this.ENUM_FIELDS) {
if (filter[field] !== undefined) {
where[field] = filter[field];
}
}
if (filter.active !== undefined) {
where.active = filter.active === true || filter.active === 'true';
}
if (filter.createdAtRange) {
const [start, end] = filter.createdAtRange;
if (start !== undefined && start !== null && start !== '') {
where.createdAt = { ...where.createdAt, [Op.gte]: start };
}
if (end !== undefined && end !== null && end !== '') {
where.createdAt = { ...where.createdAt, [Op.lte]: end };
}
}
const queryOptions = {
where,
include,
distinct: true,
order: filter.field && filter.sort
? [[filter.field, filter.sort]]
: [['createdAt', 'desc']],
transaction: options.transaction,
};
if (!options.countOnly) {
queryOptions.limit = limit ? Number(limit) : undefined;
queryOptions.offset = offset ? Number(offset) : undefined;
}
try {
const { rows, count } = await this.MODEL.findAndCountAll(queryOptions);
return {
rows: options.countOnly ? [] : rows,
count,
};
} catch (error) {
console.error('Error executing query:', error);
throw error;
}
}
}
module.exports = Access_logsDBApi;

View File

@ -1,279 +1,72 @@
const GenericDBApi = require('./base.api');
const db = require('../models'); const db = require('../models');
const Utils = require('../utils'); const Utils = require('../utils');
const Sequelize = db.Sequelize; const Sequelize = db.Sequelize;
const Op = Sequelize.Op; const Op = Sequelize.Op;
module.exports = class Asset_variantsDBApi { class Asset_variantsDBApi extends GenericDBApi {
static get MODEL() {
return db.asset_variants;
}
static async create(data, options) {
const currentUser = (options && options.currentUser) || { id: null }; static get TABLE_NAME() {
const transaction = (options && options.transaction) || undefined; return 'asset_variants';
}
const asset_variants = await db.asset_variants.create(
{ static get SEARCHABLE_FIELDS() {
id: data.id || undefined, return ['cdn_url'];
}
variant_type: data.variant_type
|| static get RANGE_FIELDS() {
null return ['width_px', 'height_px', 'size_mb'];
, }
cdn_url: data.cdn_url static get ENUM_FIELDS() {
|| return ['variant_type'];
null }
,
static get CSV_FIELDS() {
width_px: data.width_px return ['id', 'variant_type', 'cdn_url', 'width_px', 'height_px', 'size_mb', 'createdAt'];
|| }
null
, static get AUTOCOMPLETE_FIELD() {
return 'variant_type';
height_px: data.height_px }
||
null static get ASSOCIATIONS() {
, return [
{ field: 'asset', setter: 'setAsset', isArray: false },
size_mb: data.size_mb ];
|| }
null
, static get FIND_BY_INCLUDES() {
return [{ association: 'asset' }];
importHash: data.importHash || null, }
createdById: currentUser.id,
updatedById: currentUser.id, static getFieldMapping(data) {
}, return {
{ transaction }, id: data.id || undefined,
); variant_type: data.variant_type || null,
cdn_url: data.cdn_url || null,
width_px: data.width_px || null,
await asset_variants.setAsset( data.asset || null, { height_px: data.height_px || null,
transaction, size_mb: data.size_mb || null,
}); };
}
static async findAll(filter = {}, options = {}) {
filter = filter || {};
const limit = filter.limit || 0;
const currentPage = +filter.page || 0;
return asset_variants; const offset = currentPage * limit;
}
let where = {};
static async bulkImport(data, options) {
const currentUser = (options && options.currentUser) || { id: null };
const transaction = (options && options.transaction) || undefined;
// Prepare data - wrapping individual data transformations in a map() method
const asset_variantsData = data.map((item, index) => ({
id: item.id || undefined,
variant_type: item.variant_type
||
null
,
cdn_url: item.cdn_url
||
null
,
width_px: item.width_px
||
null
,
height_px: item.height_px
||
null
,
size_mb: item.size_mb
||
null
,
importHash: item.importHash || null,
createdById: currentUser.id,
updatedById: currentUser.id,
createdAt: new Date(Date.now() + index * 1000),
}));
// Bulk create items
const asset_variants = await db.asset_variants.bulkCreate(asset_variantsData, { transaction });
// For each item created, replace relation files
return asset_variants;
}
static async update(id, data, options) {
const currentUser = (options && options.currentUser) || {id: null};
const transaction = (options && options.transaction) || undefined;
const asset_variants = await db.asset_variants.findByPk(id, {transaction});
const updatePayload = {};
if (data.variant_type !== undefined) updatePayload.variant_type = data.variant_type;
if (data.cdn_url !== undefined) updatePayload.cdn_url = data.cdn_url;
if (data.width_px !== undefined) updatePayload.width_px = data.width_px;
if (data.height_px !== undefined) updatePayload.height_px = data.height_px;
if (data.size_mb !== undefined) updatePayload.size_mb = data.size_mb;
updatePayload.updatedById = currentUser.id;
await asset_variants.update(updatePayload, {transaction});
if (data.asset !== undefined) {
await asset_variants.setAsset(
data.asset,
{ transaction }
);
}
return asset_variants;
}
static async deleteByIds(ids, options) {
const currentUser = (options && options.currentUser) || { id: null };
const transaction = (options && options.transaction) || undefined;
const asset_variants = await db.asset_variants.findAll({
where: {
id: {
[Op.in]: ids,
},
},
transaction,
});
await db.sequelize.transaction(async (transaction) => {
for (const record of asset_variants) {
await record.update(
{deletedBy: currentUser.id},
{transaction}
);
}
for (const record of asset_variants) {
await record.destroy({transaction});
}
});
return asset_variants;
}
static async remove(id, options) {
const currentUser = (options && options.currentUser) || {id: null};
const transaction = (options && options.transaction) || undefined;
const asset_variants = await db.asset_variants.findByPk(id, options);
await asset_variants.update({
deletedBy: currentUser.id
}, {
transaction,
});
await asset_variants.destroy({
transaction
});
return asset_variants;
}
static async findBy(where, options) {
const transaction = (options && options.transaction) || undefined;
const asset_variants = await db.asset_variants.findOne({
where,
transaction,
});
if (!asset_variants) {
return asset_variants;
}
const output = asset_variants.get({plain: true});
output.asset = await asset_variants.getAsset({
transaction
});
return output;
}
static async findAll(
filter,
options
) {
const limit = filter.limit || 0;
let offset = 0;
let where = {};
const currentPage = +filter.page;
offset = currentPage * limit;
let include = [ let include = [
{ {
model: db.assets, model: db.assets,
as: 'asset', as: 'asset',
where: filter.asset ? { where: filter.asset ? {
[Op.or]: [ [Op.or]: [
{ id: { [Op.in]: filter.asset.split('|').map(term => Utils.uuid(term)) } }, { id: { [Op.in]: filter.asset.split('|').map(term => Utils.uuid(term)) } },
@ -284,220 +77,78 @@ module.exports = class Asset_variantsDBApi {
}, },
] ]
} : {}, } : {},
}, },
]; ];
if (filter) { if (filter.id) {
if (filter.id) { where.id = Utils.uuid(filter.id);
where = {
...where,
['id']: Utils.uuid(filter.id),
};
}
if (filter.cdn_url) {
where = {
...where,
[Op.and]: Utils.ilike(
'asset_variants',
'cdn_url',
filter.cdn_url,
),
};
}
if (filter.width_pxRange) {
const [start, end] = filter.width_pxRange;
if (start !== undefined && start !== null && start !== '') {
where = {
...where,
width_px: {
...where.width_px,
[Op.gte]: start,
},
};
}
if (end !== undefined && end !== null && end !== '') {
where = {
...where,
width_px: {
...where.width_px,
[Op.lte]: end,
},
};
}
}
if (filter.height_pxRange) {
const [start, end] = filter.height_pxRange;
if (start !== undefined && start !== null && start !== '') {
where = {
...where,
height_px: {
...where.height_px,
[Op.gte]: start,
},
};
}
if (end !== undefined && end !== null && end !== '') {
where = {
...where,
height_px: {
...where.height_px,
[Op.lte]: end,
},
};
}
}
if (filter.size_mbRange) {
const [start, end] = filter.size_mbRange;
if (start !== undefined && start !== null && start !== '') {
where = {
...where,
size_mb: {
...where.size_mb,
[Op.gte]: start,
},
};
}
if (end !== undefined && end !== null && end !== '') {
where = {
...where,
size_mb: {
...where.size_mb,
[Op.lte]: end,
},
};
}
}
if (filter.active !== undefined) {
where = {
...where,
active: filter.active === true || filter.active === 'true'
};
}
if (filter.variant_type) {
where = {
...where,
variant_type: filter.variant_type,
};
}
if (filter.createdAtRange) {
const [start, end] = filter.createdAtRange;
if (start !== undefined && start !== null && start !== '') {
where = {
...where,
['createdAt']: {
...where.createdAt,
[Op.gte]: start,
},
};
}
if (end !== undefined && end !== null && end !== '') {
where = {
...where,
['createdAt']: {
...where.createdAt,
[Op.lte]: end,
},
};
}
}
}
const queryOptions = {
where,
include,
distinct: true,
order: filter.field && filter.sort
? [[filter.field, filter.sort]]
: [['createdAt', 'desc']],
transaction: options?.transaction,
logging: console.log
};
if (!options?.countOnly) {
queryOptions.limit = limit ? Number(limit) : undefined;
queryOptions.offset = offset ? Number(offset) : undefined;
}
try {
const { rows, count } = await db.asset_variants.findAndCountAll(queryOptions);
return {
rows: options?.countOnly ? [] : rows,
count: count
};
} catch (error) {
console.error('Error executing query:', error);
throw error;
}
} }
static async findAllAutocomplete(query, limit, offset, ) { for (const field of this.SEARCHABLE_FIELDS) {
let where = {}; if (filter[field]) {
where[Op.and] = Utils.ilike(this.TABLE_NAME, field, filter[field]);
}
if (query) {
where = {
[Op.or]: [
{ ['id']: Utils.uuid(query) },
Utils.ilike(
'asset_variants',
'variant_type',
query,
),
],
};
}
const records = await db.asset_variants.findAll({
attributes: [ 'id', 'variant_type' ],
where,
limit: limit ? Number(limit) : undefined,
offset: offset ? Number(offset) : undefined,
orderBy: [['variant_type', 'ASC']],
});
return records.map((record) => ({
id: record.id,
label: record.variant_type,
}));
} }
for (const field of this.RANGE_FIELDS) {
const rangeKey = `${field}Range`;
if (filter[rangeKey]) {
const [start, end] = filter[rangeKey];
if (start !== undefined && start !== null && start !== '') {
where[field] = { ...where[field], [Op.gte]: start };
}
if (end !== undefined && end !== null && end !== '') {
where[field] = { ...where[field], [Op.lte]: end };
}
}
}
}; for (const field of this.ENUM_FIELDS) {
if (filter[field] !== undefined) {
where[field] = filter[field];
}
}
if (filter.active !== undefined) {
where.active = filter.active === true || filter.active === 'true';
}
if (filter.createdAtRange) {
const [start, end] = filter.createdAtRange;
if (start !== undefined && start !== null && start !== '') {
where.createdAt = { ...where.createdAt, [Op.gte]: start };
}
if (end !== undefined && end !== null && end !== '') {
where.createdAt = { ...where.createdAt, [Op.lte]: end };
}
}
const queryOptions = {
where,
include,
distinct: true,
order: filter.field && filter.sort
? [[filter.field, filter.sort]]
: [['createdAt', 'desc']],
transaction: options.transaction,
};
if (!options.countOnly) {
queryOptions.limit = limit ? Number(limit) : undefined;
queryOptions.offset = offset ? Number(offset) : undefined;
}
try {
const { rows, count } = await this.MODEL.findAndCountAll(queryOptions);
return {
rows: options.countOnly ? [] : rows,
count,
};
} catch (error) {
console.error('Error executing query:', error);
throw error;
}
}
}
module.exports = Asset_variantsDBApi;

View File

@ -1,391 +1,90 @@
const GenericDBApi = require('./base.api');
const db = require('../models'); const db = require('../models');
const Utils = require('../utils'); const Utils = require('../utils');
const Sequelize = db.Sequelize; const Sequelize = db.Sequelize;
const Op = Sequelize.Op; const Op = Sequelize.Op;
module.exports = class AssetsDBApi { class AssetsDBApi extends GenericDBApi {
static get MODEL() {
return db.assets;
}
static async create(data, options) {
const currentUser = (options && options.currentUser) || { id: null }; static get TABLE_NAME() {
const transaction = (options && options.transaction) || undefined; return 'assets';
}
const assets = await db.assets.create(
{ static get SEARCHABLE_FIELDS() {
id: data.id || undefined, return ['name', 'cdn_url', 'storage_key', 'mime_type', 'checksum'];
}
name: data.name
|| static get RANGE_FIELDS() {
null return ['size_mb', 'width_px', 'height_px', 'duration_sec', 'deleted_at_time'];
, }
asset_type: data.asset_type static get ENUM_FIELDS() {
|| return ['asset_type', 'type', 'is_public', 'is_deleted'];
null }
,
static get CSV_FIELDS() {
cdn_url: data.cdn_url return ['id', 'name', 'asset_type', 'type', 'cdn_url', 'storage_key', 'mime_type', 'size_mb', 'createdAt'];
|| }
null
, static get AUTOCOMPLETE_FIELD() {
return 'name';
storage_key: data.storage_key }
||
null static get ASSOCIATIONS() {
, return [
{ field: 'project', setter: 'setProject', isArray: false },
mime_type: data.mime_type ];
|| }
null
, static get FIND_BY_INCLUDES() {
return [
size_mb: data.size_mb { association: 'asset_variants_asset' },
|| { association: 'project' },
null ];
, }
width_px: data.width_px static get RELATION_FILTERS() {
|| return [
null { filterKey: 'project', model: db.projects, as: 'project', searchField: 'name' },
, ];
}
height_px: data.height_px
|| static getFieldMapping(data) {
null return {
, id: data.id || undefined,
name: data.name || null,
duration_sec: data.duration_sec asset_type: data.asset_type || null,
|| type: data.type || 'general',
null cdn_url: data.cdn_url || null,
, storage_key: data.storage_key || null,
mime_type: data.mime_type || null,
checksum: data.checksum size_mb: data.size_mb || null,
|| width_px: data.width_px || null,
null height_px: data.height_px || null,
, duration_sec: data.duration_sec || null,
checksum: data.checksum || null,
is_public: data.is_public is_public: data.is_public || false,
|| is_deleted: data.is_deleted || false,
false deleted_at_time: data.deleted_at_time || null,
};
, }
is_deleted: data.is_deleted static async findAll(filter = {}, options = {}) {
|| filter = filter || {};
false const limit = filter.limit || 0;
const currentPage = +filter.page || 0;
, const offset = currentPage * limit;
deleted_at_time: data.deleted_at_time let where = {};
||
null
,
importHash: data.importHash || null,
createdById: currentUser.id,
updatedById: currentUser.id,
},
{ transaction },
);
await assets.setProject( data.project || null, {
transaction,
});
return assets;
}
static async bulkImport(data, options) {
const currentUser = (options && options.currentUser) || { id: null };
const transaction = (options && options.transaction) || undefined;
// Prepare data - wrapping individual data transformations in a map() method
const assetsData = data.map((item, index) => ({
id: item.id || undefined,
name: item.name
||
null
,
asset_type: item.asset_type
||
null
,
cdn_url: item.cdn_url
||
null
,
storage_key: item.storage_key
||
null
,
mime_type: item.mime_type
||
null
,
size_mb: item.size_mb
||
null
,
width_px: item.width_px
||
null
,
height_px: item.height_px
||
null
,
duration_sec: item.duration_sec
||
null
,
checksum: item.checksum
||
null
,
is_public: item.is_public
||
false
,
is_deleted: item.is_deleted
||
false
,
deleted_at_time: item.deleted_at_time
||
null
,
importHash: item.importHash || null,
createdById: currentUser.id,
updatedById: currentUser.id,
createdAt: new Date(Date.now() + index * 1000),
}));
// Bulk create items
const assets = await db.assets.bulkCreate(assetsData, { transaction });
// For each item created, replace relation files
return assets;
}
static async update(id, data, options) {
const currentUser = (options && options.currentUser) || {id: null};
const transaction = (options && options.transaction) || undefined;
const assets = await db.assets.findByPk(id, {transaction});
const updatePayload = {};
if (data.name !== undefined) updatePayload.name = data.name;
if (data.asset_type !== undefined) updatePayload.asset_type = data.asset_type;
if (data.cdn_url !== undefined) updatePayload.cdn_url = data.cdn_url;
if (data.storage_key !== undefined) updatePayload.storage_key = data.storage_key;
if (data.mime_type !== undefined) updatePayload.mime_type = data.mime_type;
if (data.size_mb !== undefined) updatePayload.size_mb = data.size_mb;
if (data.width_px !== undefined) updatePayload.width_px = data.width_px;
if (data.height_px !== undefined) updatePayload.height_px = data.height_px;
if (data.duration_sec !== undefined) updatePayload.duration_sec = data.duration_sec;
if (data.checksum !== undefined) updatePayload.checksum = data.checksum;
if (data.is_public !== undefined) updatePayload.is_public = data.is_public;
if (data.is_deleted !== undefined) updatePayload.is_deleted = data.is_deleted;
if (data.deleted_at_time !== undefined) updatePayload.deleted_at_time = data.deleted_at_time;
updatePayload.updatedById = currentUser.id;
await assets.update(updatePayload, {transaction});
if (data.project !== undefined) {
await assets.setProject(
data.project,
{ transaction }
);
}
return assets;
}
static async deleteByIds(ids, options) {
const currentUser = (options && options.currentUser) || { id: null };
const transaction = (options && options.transaction) || undefined;
const assets = await db.assets.findAll({
where: {
id: {
[Op.in]: ids,
},
},
transaction,
});
await db.sequelize.transaction(async (transaction) => {
for (const record of assets) {
await record.update(
{deletedBy: currentUser.id},
{transaction}
);
}
for (const record of assets) {
await record.destroy({transaction});
}
});
return assets;
}
static async remove(id, options) {
const currentUser = (options && options.currentUser) || {id: null};
const transaction = (options && options.transaction) || undefined;
const assets = await db.assets.findByPk(id, options);
await assets.update({
deletedBy: currentUser.id
}, {
transaction,
});
await assets.destroy({
transaction
});
return assets;
}
static async findBy(where, options) {
const transaction = (options && options.transaction) || undefined;
const assets = await db.assets.findOne({
where,
transaction,
});
if (!assets) {
return assets;
}
const output = assets.get({plain: true});
output.asset_variants_asset = await assets.getAsset_variants_asset({
transaction
});
output.project = await assets.getProject({
transaction
});
return output;
}
static async findAll(
filter,
options
) {
const limit = filter.limit || 0;
let offset = 0;
let where = {};
const currentPage = +filter.page;
offset = currentPage * limit;
let include = [ let include = [
{ {
model: db.projects, model: db.projects,
as: 'project', as: 'project',
where: filter.project ? { where: filter.project ? {
[Op.or]: [ [Op.or]: [
{ id: { [Op.in]: filter.project.split('|').map(term => Utils.uuid(term)) } }, { id: { [Op.in]: filter.project.split('|').map(term => Utils.uuid(term)) } },
@ -396,326 +95,78 @@ module.exports = class AssetsDBApi {
}, },
] ]
} : {}, } : {},
}, },
]; ];
if (filter) { if (filter.id) {
if (filter.id) { where.id = Utils.uuid(filter.id);
where = {
...where,
['id']: Utils.uuid(filter.id),
};
}
if (filter.name) {
where = {
...where,
[Op.and]: Utils.ilike(
'assets',
'name',
filter.name,
),
};
}
if (filter.cdn_url) {
where = {
...where,
[Op.and]: Utils.ilike(
'assets',
'cdn_url',
filter.cdn_url,
),
};
}
if (filter.storage_key) {
where = {
...where,
[Op.and]: Utils.ilike(
'assets',
'storage_key',
filter.storage_key,
),
};
}
if (filter.mime_type) {
where = {
...where,
[Op.and]: Utils.ilike(
'assets',
'mime_type',
filter.mime_type,
),
};
}
if (filter.checksum) {
where = {
...where,
[Op.and]: Utils.ilike(
'assets',
'checksum',
filter.checksum,
),
};
}
if (filter.size_mbRange) {
const [start, end] = filter.size_mbRange;
if (start !== undefined && start !== null && start !== '') {
where = {
...where,
size_mb: {
...where.size_mb,
[Op.gte]: start,
},
};
}
if (end !== undefined && end !== null && end !== '') {
where = {
...where,
size_mb: {
...where.size_mb,
[Op.lte]: end,
},
};
}
}
if (filter.width_pxRange) {
const [start, end] = filter.width_pxRange;
if (start !== undefined && start !== null && start !== '') {
where = {
...where,
width_px: {
...where.width_px,
[Op.gte]: start,
},
};
}
if (end !== undefined && end !== null && end !== '') {
where = {
...where,
width_px: {
...where.width_px,
[Op.lte]: end,
},
};
}
}
if (filter.height_pxRange) {
const [start, end] = filter.height_pxRange;
if (start !== undefined && start !== null && start !== '') {
where = {
...where,
height_px: {
...where.height_px,
[Op.gte]: start,
},
};
}
if (end !== undefined && end !== null && end !== '') {
where = {
...where,
height_px: {
...where.height_px,
[Op.lte]: end,
},
};
}
}
if (filter.duration_secRange) {
const [start, end] = filter.duration_secRange;
if (start !== undefined && start !== null && start !== '') {
where = {
...where,
duration_sec: {
...where.duration_sec,
[Op.gte]: start,
},
};
}
if (end !== undefined && end !== null && end !== '') {
where = {
...where,
duration_sec: {
...where.duration_sec,
[Op.lte]: end,
},
};
}
}
if (filter.deleted_at_timeRange) {
const [start, end] = filter.deleted_at_timeRange;
if (start !== undefined && start !== null && start !== '') {
where = {
...where,
deleted_at_time: {
...where.deleted_at_time,
[Op.gte]: start,
},
};
}
if (end !== undefined && end !== null && end !== '') {
where = {
...where,
deleted_at_time: {
...where.deleted_at_time,
[Op.lte]: end,
},
};
}
}
if (filter.active !== undefined) {
where = {
...where,
active: filter.active === true || filter.active === 'true'
};
}
if (filter.asset_type) {
where = {
...where,
asset_type: filter.asset_type,
};
}
if (filter.is_public) {
where = {
...where,
is_public: filter.is_public,
};
}
if (filter.is_deleted) {
where = {
...where,
is_deleted: filter.is_deleted,
};
}
if (filter.createdAtRange) {
const [start, end] = filter.createdAtRange;
if (start !== undefined && start !== null && start !== '') {
where = {
...where,
['createdAt']: {
...where.createdAt,
[Op.gte]: start,
},
};
}
if (end !== undefined && end !== null && end !== '') {
where = {
...where,
['createdAt']: {
...where.createdAt,
[Op.lte]: end,
},
};
}
}
}
const queryOptions = {
where,
include,
distinct: true,
order: filter.field && filter.sort
? [[filter.field, filter.sort]]
: [['createdAt', 'desc']],
transaction: options?.transaction,
logging: console.log
};
if (!options?.countOnly) {
queryOptions.limit = limit ? Number(limit) : undefined;
queryOptions.offset = offset ? Number(offset) : undefined;
}
try {
const { rows, count } = await db.assets.findAndCountAll(queryOptions);
return {
rows: options?.countOnly ? [] : rows,
count: count
};
} catch (error) {
console.error('Error executing query:', error);
throw error;
}
} }
static async findAllAutocomplete(query, limit, offset, ) { for (const field of this.SEARCHABLE_FIELDS) {
let where = {}; if (filter[field]) {
where[Op.and] = Utils.ilike(this.TABLE_NAME, field, filter[field]);
}
if (query) {
where = {
[Op.or]: [
{ ['id']: Utils.uuid(query) },
Utils.ilike(
'assets',
'name',
query,
),
],
};
}
const records = await db.assets.findAll({
attributes: [ 'id', 'name' ],
where,
limit: limit ? Number(limit) : undefined,
offset: offset ? Number(offset) : undefined,
orderBy: [['name', 'ASC']],
});
return records.map((record) => ({
id: record.id,
label: record.name,
}));
} }
for (const field of this.RANGE_FIELDS) {
const rangeKey = `${field}Range`;
if (filter[rangeKey]) {
const [start, end] = filter[rangeKey];
if (start !== undefined && start !== null && start !== '') {
where[field] = { ...where[field], [Op.gte]: start };
}
if (end !== undefined && end !== null && end !== '') {
where[field] = { ...where[field], [Op.lte]: end };
}
}
}
}; for (const field of this.ENUM_FIELDS) {
if (filter[field] !== undefined) {
where[field] = filter[field];
}
}
if (filter.active !== undefined) {
where.active = filter.active === true || filter.active === 'true';
}
if (filter.createdAtRange) {
const [start, end] = filter.createdAtRange;
if (start !== undefined && start !== null && start !== '') {
where.createdAt = { ...where.createdAt, [Op.gte]: start };
}
if (end !== undefined && end !== null && end !== '') {
where.createdAt = { ...where.createdAt, [Op.lte]: end };
}
}
const queryOptions = {
where,
include,
distinct: true,
order: filter.field && filter.sort
? [[filter.field, filter.sort]]
: [['createdAt', 'desc']],
transaction: options.transaction,
};
if (!options.countOnly) {
queryOptions.limit = limit ? Number(limit) : undefined;
queryOptions.offset = offset ? Number(offset) : undefined;
}
try {
const { rows, count } = await this.MODEL.findAndCountAll(queryOptions);
return {
rows: options.countOnly ? [] : rows,
count,
};
} catch (error) {
console.error('Error executing query:', error);
throw error;
}
}
}
module.exports = AssetsDBApi;

View File

@ -0,0 +1,313 @@
const db = require('../models');
const Utils = require('../utils');
const { parse } = require('json2csv');
const Sequelize = db.Sequelize;
const Op = Sequelize.Op;
class GenericDBApi {
static get MODEL() {
throw new Error('MODEL must be defined in subclass');
}
static get TABLE_NAME() {
return this.MODEL.getTableName();
}
static get SEARCHABLE_FIELDS() {
return [];
}
static get RANGE_FIELDS() {
return [];
}
static get ENUM_FIELDS() {
return [];
}
static get RELATION_FILTERS() {
return [];
}
static get CSV_FIELDS() {
return ['id', 'createdAt'];
}
static get AUTOCOMPLETE_FIELD() {
return 'name';
}
static get ASSOCIATIONS() {
return [];
}
static get FIND_BY_INCLUDES() {
return [];
}
static get FIND_ALL_INCLUDES() {
return [];
}
static getFieldMapping(data) {
return data;
}
static async create(data, options = {}) {
const currentUser = options.currentUser || { id: null };
const transaction = options.transaction;
const mappedData = this.getFieldMapping(data);
const record = await this.MODEL.create(
{
...mappedData,
importHash: data.importHash || null,
createdById: currentUser.id,
updatedById: currentUser.id,
},
{ transaction }
);
for (const assoc of this.ASSOCIATIONS) {
if (data[assoc.field] !== undefined) {
await record[assoc.setter](data[assoc.field] || (assoc.isArray ? [] : null), { transaction });
}
}
return record;
}
static async bulkImport(data, options = {}) {
const currentUser = options.currentUser || { id: null };
const transaction = options.transaction;
const recordsData = data.map((item, index) => ({
...this.getFieldMapping(item),
importHash: item.importHash || null,
createdById: currentUser.id,
updatedById: currentUser.id,
createdAt: new Date(Date.now() + index * 1000),
}));
return this.MODEL.bulkCreate(recordsData, { transaction });
}
static async update(id, data, options = {}) {
const currentUser = options.currentUser || { id: null };
const transaction = options.transaction;
const record = await this.MODEL.findByPk(id, { transaction });
if (!record) {
throw { status: 404, message: `${this.TABLE_NAME} not found` };
}
const updatePayload = { updatedById: currentUser.id };
const mappedData = this.getFieldMapping(data);
for (const [key, value] of Object.entries(mappedData)) {
if (value !== undefined) {
updatePayload[key] = value;
}
}
await record.update(updatePayload, { transaction });
for (const assoc of this.ASSOCIATIONS) {
if (data[assoc.field] !== undefined) {
await record[assoc.setter](data[assoc.field], { transaction });
}
}
return record;
}
static async deleteByIds(ids, options = {}) {
const currentUser = options.currentUser || { id: null };
const transaction = options.transaction;
const records = await this.MODEL.findAll({
where: { id: { [Op.in]: ids } },
transaction,
});
await db.sequelize.transaction(async (tx) => {
for (const record of records) {
await record.update({ deletedBy: currentUser.id }, { transaction: tx });
}
for (const record of records) {
await record.destroy({ transaction: tx });
}
});
return records;
}
static async remove(id, options = {}) {
const currentUser = options.currentUser || { id: null };
const transaction = options.transaction;
const record = await this.MODEL.findByPk(id, { transaction });
if (!record) {
throw { status: 404, message: `${this.TABLE_NAME} not found` };
}
await record.update({ deletedBy: currentUser.id }, { transaction });
await record.destroy({ transaction });
return record;
}
static async findBy(where, options = {}) {
const transaction = options.transaction;
const record = await this.MODEL.findOne({
where,
transaction,
include: this.FIND_BY_INCLUDES,
});
if (!record) {
return null;
}
return record.get({ plain: true });
}
static async findAll(filter = {}, options = {}) {
filter = filter || {};
const limit = filter.limit || 0;
const currentPage = +filter.page || 0;
const offset = currentPage * limit;
let where = {};
let include = [...this.FIND_ALL_INCLUDES];
if (filter.id) {
where.id = Utils.uuid(filter.id);
}
for (const field of this.SEARCHABLE_FIELDS) {
if (filter[field]) {
where[Op.and] = Utils.ilike(this.TABLE_NAME, field, filter[field]);
}
}
for (const field of this.RANGE_FIELDS) {
const rangeKey = `${field}Range`;
if (filter[rangeKey]) {
const [start, end] = filter[rangeKey];
if (start !== undefined && start !== null && start !== '') {
where[field] = { ...where[field], [Op.gte]: start };
}
if (end !== undefined && end !== null && end !== '') {
where[field] = { ...where[field], [Op.lte]: end };
}
}
}
for (const field of this.ENUM_FIELDS) {
if (filter[field] !== undefined) {
where[field] = filter[field];
}
}
if (filter.active !== undefined) {
where.active = filter.active === true || filter.active === 'true';
}
if (filter.createdAtRange) {
const [start, end] = filter.createdAtRange;
if (start !== undefined && start !== null && start !== '') {
where.createdAt = { ...where.createdAt, [Op.gte]: start };
}
if (end !== undefined && end !== null && end !== '') {
where.createdAt = { ...where.createdAt, [Op.lte]: end };
}
}
for (const rel of this.RELATION_FILTERS) {
if (filter[rel.filterKey]) {
const searchTerms = filter[rel.filterKey].split('|');
const relInclude = {
model: rel.model,
as: rel.as,
required: searchTerms.length > 0,
where: searchTerms.length > 0 ? {
[Op.or]: [
{ id: { [Op.in]: searchTerms.map(term => Utils.uuid(term)) } },
rel.searchField ? {
[rel.searchField]: {
[Op.or]: searchTerms.map(term => ({ [Op.iLike]: `%${term}%` }))
}
} : {}
]
} : undefined
};
include = [relInclude, ...include];
}
}
const queryOptions = {
where,
include,
distinct: true,
order: filter.field && filter.sort
? [[filter.field, filter.sort]]
: [['createdAt', 'desc']],
transaction: options.transaction,
};
if (!options.countOnly) {
queryOptions.limit = limit ? Number(limit) : undefined;
queryOptions.offset = offset ? Number(offset) : undefined;
}
try {
const { rows, count } = await this.MODEL.findAndCountAll(queryOptions);
return {
rows: options.countOnly ? [] : rows,
count,
};
} catch (error) {
console.error('Error executing query:', error);
throw error;
}
}
static async findAllAutocomplete(query, limit, offset) {
let where = {};
if (query) {
where = {
[Op.or]: [
{ id: Utils.uuid(query) },
Utils.ilike(this.TABLE_NAME, this.AUTOCOMPLETE_FIELD, query),
],
};
}
const records = await this.MODEL.findAll({
attributes: ['id', this.AUTOCOMPLETE_FIELD],
where,
limit: limit ? Number(limit) : undefined,
offset: offset ? Number(offset) : undefined,
order: [[this.AUTOCOMPLETE_FIELD, 'ASC']],
});
return records.map((record) => ({
id: record.id,
label: record[this.AUTOCOMPLETE_FIELD],
}));
}
static toCSV(rows) {
const opts = { fields: this.CSV_FIELDS };
return parse(rows, opts);
}
}
module.exports = GenericDBApi;

View File

@ -1,4 +1,4 @@
const GenericDBApi = require('./base.api');
const db = require('../models'); const db = require('../models');
const Utils = require('../utils'); const Utils = require('../utils');
const { const {
@ -6,306 +6,102 @@ const {
getRuntimeProjectSlug, getRuntimeProjectSlug,
} = require('./runtime-context'); } = require('./runtime-context');
const Sequelize = db.Sequelize; const Sequelize = db.Sequelize;
const Op = Sequelize.Op; const Op = Sequelize.Op;
module.exports = class Page_elementsDBApi { class Page_elementsDBApi extends GenericDBApi {
static get MODEL() {
return db.page_elements;
}
static async create(data, options) {
const currentUser = (options && options.currentUser) || { id: null }; static get TABLE_NAME() {
const transaction = (options && options.transaction) || undefined; return 'page_elements';
}
const page_elements = await db.page_elements.create(
{ static get SEARCHABLE_FIELDS() {
id: data.id || undefined, return ['name', 'style_json', 'content_json'];
}
element_type: data.element_type ?? null,
static get RANGE_FIELDS() {
name: data.name ?? null, return ['sort_order', 'x_percent', 'y_percent', 'width_percent', 'height_percent', 'rotation_deg'];
}
sort_order: data.sort_order ?? 0,
static get ENUM_FIELDS() {
is_visible: data.is_visible ?? false, return ['element_type', 'is_visible'];
}
x_percent: data.x_percent ?? null,
static get CSV_FIELDS() {
y_percent: data.y_percent ?? null, return ['id', 'element_type', 'name', 'sort_order', 'is_visible', 'x_percent', 'y_percent', 'createdAt'];
}
width_percent: data.width_percent ?? null,
static get AUTOCOMPLETE_FIELD() {
height_percent: data.height_percent ?? null, return 'name';
}
rotation_deg: data.rotation_deg ?? null,
static get ASSOCIATIONS() {
style_json: data.style_json ?? null, return [
{ field: 'page', setter: 'setPage', isArray: false },
content_json: data.content_json ?? null, ];
}
importHash: data.importHash || null,
createdById: currentUser.id, static getFieldMapping(data) {
updatedById: currentUser.id, return {
}, id: data.id || undefined,
{ transaction }, element_type: data.element_type ?? null,
); name: data.name ?? null,
sort_order: data.sort_order ?? 0,
is_visible: data.is_visible ?? false,
await page_elements.setPage( data.page || null, { x_percent: data.x_percent ?? null,
transaction, y_percent: data.y_percent ?? null,
}); width_percent: data.width_percent ?? null,
height_percent: data.height_percent ?? null,
rotation_deg: data.rotation_deg ?? null,
style_json: data.style_json ?? null,
content_json: data.content_json ?? null,
};
}
return page_elements;
} static async findBy(where, options = {}) {
const transaction = options.transaction;
const runtimeEnvironment = getRuntimeEnvironment(options);
static async bulkImport(data, options) { const runtimeProjectSlug = getRuntimeProjectSlug(options);
const currentUser = (options && options.currentUser) || { id: null }; const pageInclude = {
const transaction = (options && options.transaction) || undefined; model: db.tour_pages,
as: 'page',
// Prepare data - wrapping individual data transformations in a map() method required: Boolean(runtimeEnvironment || runtimeProjectSlug),
const page_elementsData = data.map((item, index) => ({ where: runtimeEnvironment ? { environment: runtimeEnvironment } : {},
id: item.id || undefined, include: runtimeProjectSlug
? [{
element_type: item.element_type ?? null, model: db.projects,
as: 'project',
name: item.name ?? null, required: true,
where: { slug: runtimeProjectSlug },
sort_order: item.sort_order ?? 0, }]
: [],
is_visible: item.is_visible ?? false, };
x_percent: item.x_percent ?? null, const record = await this.MODEL.findOne({
where,
y_percent: item.y_percent ?? null, transaction,
include: [pageInclude],
width_percent: item.width_percent ?? null, });
height_percent: item.height_percent ?? null, if (!record) return null;
return record.get({ plain: true });
rotation_deg: item.rotation_deg ?? null, }
style_json: item.style_json ?? null, static async findAll(filter = {}, options = {}) {
filter = filter || {};
content_json: item.content_json ?? null, const limit = filter.limit || 0;
const currentPage = +filter.page || 0;
importHash: item.importHash || null, const offset = currentPage * limit;
createdById: currentUser.id,
updatedById: currentUser.id, let where = {};
createdAt: new Date(Date.now() + index * 1000),
}));
// Bulk create items
const page_elements = await db.page_elements.bulkCreate(page_elementsData, { transaction });
// For each item created, replace relation files
return page_elements;
}
static async update(id, data, options) {
const currentUser = (options && options.currentUser) || {id: null};
const transaction = (options && options.transaction) || undefined;
const page_elements = await db.page_elements.findByPk(id, {transaction});
const updatePayload = {};
if (data.element_type !== undefined) updatePayload.element_type = data.element_type;
if (data.name !== undefined) updatePayload.name = data.name;
if (data.sort_order !== undefined) updatePayload.sort_order = data.sort_order;
if (data.is_visible !== undefined) updatePayload.is_visible = data.is_visible;
if (data.x_percent !== undefined) updatePayload.x_percent = data.x_percent;
if (data.y_percent !== undefined) updatePayload.y_percent = data.y_percent;
if (data.width_percent !== undefined) updatePayload.width_percent = data.width_percent;
if (data.height_percent !== undefined) updatePayload.height_percent = data.height_percent;
if (data.rotation_deg !== undefined) updatePayload.rotation_deg = data.rotation_deg;
if (data.style_json !== undefined) updatePayload.style_json = data.style_json;
if (data.content_json !== undefined) updatePayload.content_json = data.content_json;
updatePayload.updatedById = currentUser.id;
await page_elements.update(updatePayload, {transaction});
if (data.page !== undefined) {
await page_elements.setPage(
data.page,
{ transaction }
);
}
return page_elements;
}
static async deleteByIds(ids, options) {
const currentUser = (options && options.currentUser) || { id: null };
const transaction = (options && options.transaction) || undefined;
const page_elements = await db.page_elements.findAll({
where: {
id: {
[Op.in]: ids,
},
},
transaction,
});
await db.sequelize.transaction(async (transaction) => {
for (const record of page_elements) {
await record.update(
{deletedBy: currentUser.id},
{transaction}
);
}
for (const record of page_elements) {
await record.destroy({transaction});
}
});
return page_elements;
}
static async remove(id, options) {
const currentUser = (options && options.currentUser) || {id: null};
const transaction = (options && options.transaction) || undefined;
const page_elements = await db.page_elements.findByPk(id, options);
await page_elements.update({
deletedBy: currentUser.id
}, {
transaction,
});
await page_elements.destroy({
transaction
});
return page_elements;
}
static async findBy(where, options) {
const transaction = (options && options.transaction) || undefined;
const runtimeEnvironment = getRuntimeEnvironment(options);
const runtimeProjectSlug = getRuntimeProjectSlug(options);
const pageInclude = {
model: db.tour_pages,
as: 'page',
required: Boolean(runtimeEnvironment || runtimeProjectSlug),
where: runtimeEnvironment ? { environment: runtimeEnvironment } : {},
include: runtimeProjectSlug
? [{
model: db.projects,
as: 'project',
required: true,
where: { slug: runtimeProjectSlug },
}]
: [],
};
const page_elements = await db.page_elements.findOne(
{ where, include: [pageInclude], transaction },
);
if (!page_elements) {
return page_elements;
}
const output = page_elements.get({plain: true});
output.page = await page_elements.getPage({
transaction
});
return output;
}
static async findAll(
filter,
options
) {
filter = filter || {};
const limit = filter.limit || 0;
let offset = 0;
let where = {};
const currentPage = +filter.page;
offset = currentPage * limit;
let include = [ let include = [
{ {
model: db.tour_pages, model: db.tour_pages,
as: 'page', as: 'page',
where: filter.page ? { where: filter.page ? {
[Op.or]: [ [Op.or]: [
{ id: { [Op.in]: filter.page.split('|').map(term => Utils.uuid(term)) } }, { id: { [Op.in]: filter.page.split('|').map(term => Utils.uuid(term)) } },
@ -316,341 +112,99 @@ module.exports = class Page_elementsDBApi {
}, },
] ]
} : {}, } : {},
}, },
]; ];
const runtimeEnvironment = getRuntimeEnvironment(options);
const runtimeProjectSlug = getRuntimeProjectSlug(options);
if (runtimeEnvironment) { const runtimeEnvironment = getRuntimeEnvironment(options);
include[0].where = { const runtimeProjectSlug = getRuntimeProjectSlug(options);
...(include[0].where || {}),
environment: runtimeEnvironment,
};
include[0].required = true;
}
if (runtimeProjectSlug) { if (runtimeEnvironment) {
include[0].include = [{ include[0].where = {
model: db.projects, ...(include[0].where || {}),
as: 'project', environment: runtimeEnvironment,
required: true, };
where: { slug: runtimeProjectSlug }, include[0].required = true;
}];
include[0].required = true;
}
if (filter) {
if (filter.id) {
where = {
...where,
['id']: Utils.uuid(filter.id),
};
}
if (filter.name) {
where = {
...where,
[Op.and]: Utils.ilike(
'page_elements',
'name',
filter.name,
),
};
}
if (filter.style_json) {
where = {
...where,
[Op.and]: Utils.ilike(
'page_elements',
'style_json',
filter.style_json,
),
};
}
if (filter.content_json) {
where = {
...where,
[Op.and]: Utils.ilike(
'page_elements',
'content_json',
filter.content_json,
),
};
}
if (filter.sort_orderRange) {
const [start, end] = filter.sort_orderRange;
if (start !== undefined && start !== null && start !== '') {
where = {
...where,
sort_order: {
...where.sort_order,
[Op.gte]: start,
},
};
}
if (end !== undefined && end !== null && end !== '') {
where = {
...where,
sort_order: {
...where.sort_order,
[Op.lte]: end,
},
};
}
}
if (filter.x_percentRange) {
const [start, end] = filter.x_percentRange;
if (start !== undefined && start !== null && start !== '') {
where = {
...where,
x_percent: {
...where.x_percent,
[Op.gte]: start,
},
};
}
if (end !== undefined && end !== null && end !== '') {
where = {
...where,
x_percent: {
...where.x_percent,
[Op.lte]: end,
},
};
}
}
if (filter.y_percentRange) {
const [start, end] = filter.y_percentRange;
if (start !== undefined && start !== null && start !== '') {
where = {
...where,
y_percent: {
...where.y_percent,
[Op.gte]: start,
},
};
}
if (end !== undefined && end !== null && end !== '') {
where = {
...where,
y_percent: {
...where.y_percent,
[Op.lte]: end,
},
};
}
}
if (filter.width_percentRange) {
const [start, end] = filter.width_percentRange;
if (start !== undefined && start !== null && start !== '') {
where = {
...where,
width_percent: {
...where.width_percent,
[Op.gte]: start,
},
};
}
if (end !== undefined && end !== null && end !== '') {
where = {
...where,
width_percent: {
...where.width_percent,
[Op.lte]: end,
},
};
}
}
if (filter.height_percentRange) {
const [start, end] = filter.height_percentRange;
if (start !== undefined && start !== null && start !== '') {
where = {
...where,
height_percent: {
...where.height_percent,
[Op.gte]: start,
},
};
}
if (end !== undefined && end !== null && end !== '') {
where = {
...where,
height_percent: {
...where.height_percent,
[Op.lte]: end,
},
};
}
}
if (filter.rotation_degRange) {
const [start, end] = filter.rotation_degRange;
if (start !== undefined && start !== null && start !== '') {
where = {
...where,
rotation_deg: {
...where.rotation_deg,
[Op.gte]: start,
},
};
}
if (end !== undefined && end !== null && end !== '') {
where = {
...where,
rotation_deg: {
...where.rotation_deg,
[Op.lte]: end,
},
};
}
}
if (filter.active !== undefined) {
where = {
...where,
active: filter.active === true || filter.active === 'true'
};
}
if (filter.element_type) {
where = {
...where,
element_type: filter.element_type,
};
}
if (filter.is_visible) {
where = {
...where,
is_visible: filter.is_visible,
};
}
if (filter.createdAtRange) {
const [start, end] = filter.createdAtRange;
if (start !== undefined && start !== null && start !== '') {
where = {
...where,
['createdAt']: {
...where.createdAt,
[Op.gte]: start,
},
};
}
if (end !== undefined && end !== null && end !== '') {
where = {
...where,
['createdAt']: {
...where.createdAt,
[Op.lte]: end,
},
};
}
}
}
const queryOptions = {
where,
include,
distinct: true,
order: filter.field && filter.sort
? [[filter.field, filter.sort]]
: [['createdAt', 'desc']],
transaction: options?.transaction,
logging: console.log
};
if (!options?.countOnly) {
queryOptions.limit = limit ? Number(limit) : undefined;
queryOptions.offset = offset ? Number(offset) : undefined;
}
try {
const { rows, count } = await db.page_elements.findAndCountAll(queryOptions);
return {
rows: options?.countOnly ? [] : rows,
count: count
};
} catch (error) {
console.error('Error executing query:', error);
throw error;
}
} }
static async findAllAutocomplete(query, limit, offset, ) { if (runtimeProjectSlug) {
let where = {}; include[0].include = [{
model: db.projects,
as: 'project',
required: true,
if (query) { where: { slug: runtimeProjectSlug },
where = { }];
[Op.or]: [ include[0].required = true;
{ ['id']: Utils.uuid(query) },
Utils.ilike(
'page_elements',
'name',
query,
),
],
};
}
const records = await db.page_elements.findAll({
attributes: [ 'id', 'name' ],
where,
limit: limit ? Number(limit) : undefined,
offset: offset ? Number(offset) : undefined,
orderBy: [['name', 'ASC']],
});
return records.map((record) => ({
id: record.id,
label: record.name,
}));
} }
if (filter.id) {
where.id = Utils.uuid(filter.id);
}
}; for (const field of this.SEARCHABLE_FIELDS) {
if (filter[field]) {
where[Op.and] = Utils.ilike(this.TABLE_NAME, field, filter[field]);
}
}
for (const field of this.RANGE_FIELDS) {
const rangeKey = `${field}Range`;
if (filter[rangeKey]) {
const [start, end] = filter[rangeKey];
if (start !== undefined && start !== null && start !== '') {
where[field] = { ...where[field], [Op.gte]: start };
}
if (end !== undefined && end !== null && end !== '') {
where[field] = { ...where[field], [Op.lte]: end };
}
}
}
for (const field of this.ENUM_FIELDS) {
if (filter[field] !== undefined) {
where[field] = filter[field];
}
}
if (filter.active !== undefined) {
where.active = filter.active === true || filter.active === 'true';
}
if (filter.createdAtRange) {
const [start, end] = filter.createdAtRange;
if (start !== undefined && start !== null && start !== '') {
where.createdAt = { ...where.createdAt, [Op.gte]: start };
}
if (end !== undefined && end !== null && end !== '') {
where.createdAt = { ...where.createdAt, [Op.lte]: end };
}
}
const queryOptions = {
where,
include,
distinct: true,
order: filter.field && filter.sort
? [[filter.field, filter.sort]]
: [['createdAt', 'desc']],
transaction: options.transaction,
};
if (!options.countOnly) {
queryOptions.limit = limit ? Number(limit) : undefined;
queryOptions.offset = offset ? Number(offset) : undefined;
}
try {
const { rows, count } = await this.MODEL.findAndCountAll(queryOptions);
return {
rows: options.countOnly ? [] : rows,
count,
};
} catch (error) {
console.error('Error executing query:', error);
throw error;
}
}
}
module.exports = Page_elementsDBApi;

View File

@ -1,4 +1,4 @@
const GenericDBApi = require('./base.api');
const db = require('../models'); const db = require('../models');
const Utils = require('../utils'); const Utils = require('../utils');
const { const {
@ -6,334 +6,115 @@ const {
getRuntimeProjectSlug, getRuntimeProjectSlug,
} = require('./runtime-context'); } = require('./runtime-context');
const Sequelize = db.Sequelize; const Sequelize = db.Sequelize;
const Op = Sequelize.Op; const Op = Sequelize.Op;
module.exports = class Page_linksDBApi { class Page_linksDBApi extends GenericDBApi {
static get MODEL() {
return db.page_links;
}
static get TABLE_NAME() {
return 'page_links';
}
static get SEARCHABLE_FIELDS() {
return ['external_url', 'trigger_selector'];
}
static async create(data, options) { static get RANGE_FIELDS() {
const currentUser = (options && options.currentUser) || { id: null }; return [];
const transaction = (options && options.transaction) || undefined; }
const page_links = await db.page_links.create( static get ENUM_FIELDS() {
{ return ['direction', 'is_active'];
id: data.id || undefined, }
direction: data.direction static get CSV_FIELDS() {
|| return ['id', 'direction', 'external_url', 'is_active', 'trigger_selector', 'createdAt'];
null }
,
external_url: data.external_url static get AUTOCOMPLETE_FIELD() {
|| return 'direction';
null }
,
is_active: data.is_active static get ASSOCIATIONS() {
|| return [
false { field: 'from_page', setter: 'setFrom_page', isArray: false },
{ field: 'to_page', setter: 'setTo_page', isArray: false },
{ field: 'transition', setter: 'setTransition', isArray: false },
];
}
, static getFieldMapping(data) {
return {
id: data.id || undefined,
direction: data.direction || null,
external_url: data.external_url || null,
is_active: data.is_active || false,
trigger_selector: data.trigger_selector || null,
};
}
trigger_selector: data.trigger_selector static async findBy(where, options = {}) {
|| const transaction = options.transaction;
null const runtimeEnvironment = getRuntimeEnvironment(options);
, const runtimeProjectSlug = getRuntimeProjectSlug(options);
const buildProjectInclude = () => (
importHash: data.importHash || null, runtimeProjectSlug
createdById: currentUser.id, ? [{
updatedById: currentUser.id, model: db.projects,
}, as: 'project',
{ transaction }, required: true,
where: { slug: runtimeProjectSlug },
}]
: []
); );
const record = await this.MODEL.findOne({
await page_links.setFrom_page( data.from_page || null, { where,
transaction, transaction,
}); include: [
{
await page_links.setTo_page( data.to_page || null, { model: db.tour_pages,
transaction, as: 'from_page',
}); required: Boolean(runtimeEnvironment || runtimeProjectSlug),
where: runtimeEnvironment ? { environment: runtimeEnvironment } : {},
await page_links.setTransition( data.transition || null, { include: buildProjectInclude(),
transaction, },
}); {
model: db.tour_pages,
as: 'to_page',
required: false,
where: runtimeEnvironment ? { environment: runtimeEnvironment } : {},
include: buildProjectInclude(),
},
return page_links; {
} model: db.transitions,
as: 'transition',
required: false,
static async bulkImport(data, options) { where: runtimeEnvironment ? { environment: runtimeEnvironment } : {},
const currentUser = (options && options.currentUser) || { id: null }; include: buildProjectInclude(),
const transaction = (options && options.transaction) || undefined; },
],
// Prepare data - wrapping individual data transformations in a map() method });
const page_linksData = data.map((item, index) => ({
id: item.id || undefined, if (!record) return null;
return record.get({ plain: true });
direction: item.direction }
||
null static async findAll(filter = {}, options = {}) {
, filter = filter || {};
const limit = filter.limit || 0;
external_url: item.external_url const currentPage = +filter.page || 0;
|| const offset = currentPage * limit;
null
, let where = {};
is_active: item.is_active
||
false
,
trigger_selector: item.trigger_selector
||
null
,
importHash: item.importHash || null,
createdById: currentUser.id,
updatedById: currentUser.id,
createdAt: new Date(Date.now() + index * 1000),
}));
// Bulk create items
const page_links = await db.page_links.bulkCreate(page_linksData, { transaction });
// For each item created, replace relation files
return page_links;
}
static async update(id, data, options) {
const currentUser = (options && options.currentUser) || {id: null};
const transaction = (options && options.transaction) || undefined;
const page_links = await db.page_links.findByPk(id, {transaction});
const updatePayload = {};
if (data.direction !== undefined) updatePayload.direction = data.direction;
if (data.external_url !== undefined) updatePayload.external_url = data.external_url;
if (data.is_active !== undefined) updatePayload.is_active = data.is_active;
if (data.trigger_selector !== undefined) updatePayload.trigger_selector = data.trigger_selector;
updatePayload.updatedById = currentUser.id;
await page_links.update(updatePayload, {transaction});
if (data.from_page !== undefined) {
await page_links.setFrom_page(
data.from_page,
{ transaction }
);
}
if (data.to_page !== undefined) {
await page_links.setTo_page(
data.to_page,
{ transaction }
);
}
if (data.transition !== undefined) {
await page_links.setTransition(
data.transition,
{ transaction }
);
}
return page_links;
}
static async deleteByIds(ids, options) {
const currentUser = (options && options.currentUser) || { id: null };
const transaction = (options && options.transaction) || undefined;
const page_links = await db.page_links.findAll({
where: {
id: {
[Op.in]: ids,
},
},
transaction,
});
await db.sequelize.transaction(async (transaction) => {
for (const record of page_links) {
await record.update(
{deletedBy: currentUser.id},
{transaction}
);
}
for (const record of page_links) {
await record.destroy({transaction});
}
});
return page_links;
}
static async remove(id, options) {
const currentUser = (options && options.currentUser) || {id: null};
const transaction = (options && options.transaction) || undefined;
const page_links = await db.page_links.findByPk(id, options);
await page_links.update({
deletedBy: currentUser.id
}, {
transaction,
});
await page_links.destroy({
transaction
});
return page_links;
}
static async findBy(where, options) {
const transaction = (options && options.transaction) || undefined;
const runtimeEnvironment = getRuntimeEnvironment(options);
const runtimeProjectSlug = getRuntimeProjectSlug(options);
const buildProjectInclude = () => (
runtimeProjectSlug
? [{
model: db.projects,
as: 'project',
required: true,
where: { slug: runtimeProjectSlug },
}]
: []
);
const page_links = await db.page_links.findOne(
{
where,
include: [
{
model: db.tour_pages,
as: 'from_page',
required: Boolean(runtimeEnvironment || runtimeProjectSlug),
where: runtimeEnvironment ? { environment: runtimeEnvironment } : {},
include: buildProjectInclude(),
},
{
model: db.transitions,
as: 'transition',
required: false,
where: runtimeEnvironment ? { environment: runtimeEnvironment } : {},
include: buildProjectInclude(),
},
],
transaction,
},
);
if (!page_links) {
return page_links;
}
const output = page_links.get({plain: true});
output.from_page = await page_links.getFrom_page({
transaction
});
output.to_page = await page_links.getTo_page({
transaction
});
output.transition = await page_links.getTransition({
transaction
});
return output;
}
static async findAll(
filter,
options
) {
filter = filter || {};
const limit = filter.limit || 0;
let offset = 0;
let where = {};
const currentPage = +filter.page;
offset = currentPage * limit;
let include = [ let include = [
{ {
model: db.tour_pages, model: db.tour_pages,
as: 'from_page', as: 'from_page',
where: filter.from_page ? { where: filter.from_page ? {
[Op.or]: [ [Op.or]: [
{ id: { [Op.in]: filter.from_page.split('|').map(term => Utils.uuid(term)) } }, { id: { [Op.in]: filter.from_page.split('|').map(term => Utils.uuid(term)) } },
@ -344,13 +125,10 @@ module.exports = class Page_linksDBApi {
}, },
] ]
} : {}, } : {},
}, },
{ {
model: db.tour_pages, model: db.tour_pages,
as: 'to_page', as: 'to_page',
where: filter.to_page ? { where: filter.to_page ? {
[Op.or]: [ [Op.or]: [
{ id: { [Op.in]: filter.to_page.split('|').map(term => Utils.uuid(term)) } }, { id: { [Op.in]: filter.to_page.split('|').map(term => Utils.uuid(term)) } },
@ -361,13 +139,10 @@ module.exports = class Page_linksDBApi {
}, },
] ]
} : {}, } : {},
}, },
{ {
model: db.transitions, model: db.transitions,
as: 'transition', as: 'transition',
where: filter.transition ? { where: filter.transition ? {
[Op.or]: [ [Op.or]: [
{ id: { [Op.in]: filter.transition.split('|').map(term => Utils.uuid(term)) } }, { id: { [Op.in]: filter.transition.split('|').map(term => Utils.uuid(term)) } },
@ -378,212 +153,90 @@ module.exports = class Page_linksDBApi {
}, },
] ]
} : {}, } : {},
}, },
]; ];
const runtimeEnvironment = getRuntimeEnvironment(options);
const runtimeProjectSlug = getRuntimeProjectSlug(options);
if (runtimeEnvironment) { const runtimeEnvironment = getRuntimeEnvironment(options);
include[0].where = { const runtimeProjectSlug = getRuntimeProjectSlug(options);
...(include[0].where || {}),
environment: runtimeEnvironment,
};
include[0].required = true;
include[1].where = {
...(include[1].where || {}),
environment: runtimeEnvironment,
};
include[2].where = {
...(include[2].where || {}),
environment: runtimeEnvironment,
};
include[2].required = false;
}
if (runtimeProjectSlug) { if (runtimeEnvironment) {
include[0].include = [{ include[0].where = { ...(include[0].where || {}), environment: runtimeEnvironment };
model: db.projects, include[0].required = true;
as: 'project', include[1].where = { ...(include[1].where || {}), environment: runtimeEnvironment };
required: true, include[2].where = { ...(include[2].where || {}), environment: runtimeEnvironment };
where: { slug: runtimeProjectSlug }, include[2].required = false;
}];
include[0].required = true;
include[1].include = [{
model: db.projects,
as: 'project',
required: true,
where: { slug: runtimeProjectSlug },
}];
include[2].include = [{
model: db.projects,
as: 'project',
required: true,
where: { slug: runtimeProjectSlug },
}];
include[2].required = false;
}
if (filter) {
if (filter.id) {
where = {
...where,
['id']: Utils.uuid(filter.id),
};
}
if (filter.external_url) {
where = {
...where,
[Op.and]: Utils.ilike(
'page_links',
'external_url',
filter.external_url,
),
};
}
if (filter.trigger_selector) {
where = {
...where,
[Op.and]: Utils.ilike(
'page_links',
'trigger_selector',
filter.trigger_selector,
),
};
}
if (filter.active !== undefined) {
where = {
...where,
active: filter.active === true || filter.active === 'true'
};
}
if (filter.direction) {
where = {
...where,
direction: filter.direction,
};
}
if (filter.is_active) {
where = {
...where,
is_active: filter.is_active,
};
}
if (filter.createdAtRange) {
const [start, end] = filter.createdAtRange;
if (start !== undefined && start !== null && start !== '') {
where = {
...where,
['createdAt']: {
...where.createdAt,
[Op.gte]: start,
},
};
}
if (end !== undefined && end !== null && end !== '') {
where = {
...where,
['createdAt']: {
...where.createdAt,
[Op.lte]: end,
},
};
}
}
}
const queryOptions = {
where,
include,
distinct: true,
order: filter.field && filter.sort
? [[filter.field, filter.sort]]
: [['createdAt', 'desc']],
transaction: options?.transaction,
logging: console.log
};
if (!options?.countOnly) {
queryOptions.limit = limit ? Number(limit) : undefined;
queryOptions.offset = offset ? Number(offset) : undefined;
}
try {
const { rows, count } = await db.page_links.findAndCountAll(queryOptions);
return {
rows: options?.countOnly ? [] : rows,
count: count
};
} catch (error) {
console.error('Error executing query:', error);
throw error;
}
} }
static async findAllAutocomplete(query, limit, offset, ) { if (runtimeProjectSlug) {
let where = {}; const projectInclude = [{
model: db.projects,
as: 'project',
required: true,
if (query) { where: { slug: runtimeProjectSlug },
where = { }];
[Op.or]: [ include[0].include = projectInclude;
{ ['id']: Utils.uuid(query) }, include[0].required = true;
Utils.ilike( include[1].include = projectInclude;
'page_links', include[2].include = projectInclude;
'direction', include[2].required = false;
query,
),
],
};
}
const records = await db.page_links.findAll({
attributes: [ 'id', 'direction' ],
where,
limit: limit ? Number(limit) : undefined,
offset: offset ? Number(offset) : undefined,
orderBy: [['direction', 'ASC']],
});
return records.map((record) => ({
id: record.id,
label: record.direction,
}));
} }
if (filter.id) {
where.id = Utils.uuid(filter.id);
}
}; for (const field of this.SEARCHABLE_FIELDS) {
if (filter[field]) {
where[Op.and] = Utils.ilike(this.TABLE_NAME, field, filter[field]);
}
}
for (const field of this.ENUM_FIELDS) {
if (filter[field] !== undefined) {
where[field] = filter[field];
}
}
if (filter.active !== undefined) {
where.active = filter.active === true || filter.active === 'true';
}
if (filter.createdAtRange) {
const [start, end] = filter.createdAtRange;
if (start !== undefined && start !== null && start !== '') {
where.createdAt = { ...where.createdAt, [Op.gte]: start };
}
if (end !== undefined && end !== null && end !== '') {
where.createdAt = { ...where.createdAt, [Op.lte]: end };
}
}
const queryOptions = {
where,
include,
distinct: true,
order: filter.field && filter.sort
? [[filter.field, filter.sort]]
: [['createdAt', 'desc']],
transaction: options.transaction,
};
if (!options.countOnly) {
queryOptions.limit = limit ? Number(limit) : undefined;
queryOptions.offset = offset ? Number(offset) : undefined;
}
try {
const { rows, count } = await this.MODEL.findAndCountAll(queryOptions);
return {
rows: options.countOnly ? [] : rows,
count,
};
} catch (error) {
console.error('Error executing query:', error);
throw error;
}
}
}
module.exports = Page_linksDBApi;

View File

@ -1,335 +1,53 @@
const GenericDBApi = require('./base.api');
const db = require('../models'); const db = require('../models');
const Utils = require('../utils');
class PermissionsDBApi extends GenericDBApi {
static get MODEL() {
return db.permissions;
}
static get TABLE_NAME() {
return 'permissions';
}
const Sequelize = db.Sequelize; static get SEARCHABLE_FIELDS() {
const Op = Sequelize.Op; return ['name'];
}
module.exports = class PermissionsDBApi { static get RANGE_FIELDS() {
return [];
}
static get ENUM_FIELDS() {
return [];
}
static get CSV_FIELDS() {
return ['id', 'name', 'createdAt'];
}
static async create(data, options) { static get AUTOCOMPLETE_FIELD() {
const currentUser = (options && options.currentUser) || { id: null }; return 'name';
const transaction = (options && options.transaction) || undefined; }
const permissions = await db.permissions.create( static get ASSOCIATIONS() {
{ return [];
id: data.id || undefined, }
name: data.name static get FIND_BY_INCLUDES() {
|| return [];
null }
,
importHash: data.importHash || null, static get FIND_ALL_INCLUDES() {
createdById: currentUser.id, return [];
updatedById: currentUser.id, }
},
{ transaction },
);
static getFieldMapping(data) {
return {
id: data.id || undefined,
name: data.name || null,
};
}
}
module.exports = PermissionsDBApi;
return permissions;
}
static async bulkImport(data, options) {
const currentUser = (options && options.currentUser) || { id: null };
const transaction = (options && options.transaction) || undefined;
// Prepare data - wrapping individual data transformations in a map() method
const permissionsData = data.map((item, index) => ({
id: item.id || undefined,
name: item.name
||
null
,
importHash: item.importHash || null,
createdById: currentUser.id,
updatedById: currentUser.id,
createdAt: new Date(Date.now() + index * 1000),
}));
// Bulk create items
const permissions = await db.permissions.bulkCreate(permissionsData, { transaction });
// For each item created, replace relation files
return permissions;
}
static async update(id, data, options) {
const currentUser = (options && options.currentUser) || {id: null};
const transaction = (options && options.transaction) || undefined;
const permissions = await db.permissions.findByPk(id, {transaction});
const updatePayload = {};
if (data.name !== undefined) updatePayload.name = data.name;
updatePayload.updatedById = currentUser.id;
await permissions.update(updatePayload, {transaction});
return permissions;
}
static async deleteByIds(ids, options) {
const currentUser = (options && options.currentUser) || { id: null };
const transaction = (options && options.transaction) || undefined;
const permissions = await db.permissions.findAll({
where: {
id: {
[Op.in]: ids,
},
},
transaction,
});
await db.sequelize.transaction(async (transaction) => {
for (const record of permissions) {
await record.update(
{deletedBy: currentUser.id},
{transaction}
);
}
for (const record of permissions) {
await record.destroy({transaction});
}
});
return permissions;
}
static async remove(id, options) {
const currentUser = (options && options.currentUser) || {id: null};
const transaction = (options && options.transaction) || undefined;
const permissions = await db.permissions.findByPk(id, options);
await permissions.update({
deletedBy: currentUser.id
}, {
transaction,
});
await permissions.destroy({
transaction
});
return permissions;
}
static async findBy(where, options) {
const transaction = (options && options.transaction) || undefined;
const permissions = await db.permissions.findOne({
where,
transaction,
});
if (!permissions) {
return permissions;
}
const output = permissions.get({plain: true});
return output;
}
static async findAll(
filter,
options
) {
const limit = filter.limit || 0;
let offset = 0;
let where = {};
const currentPage = +filter.page;
offset = currentPage * limit;
let include = [
];
if (filter) {
if (filter.id) {
where = {
...where,
['id']: Utils.uuid(filter.id),
};
}
if (filter.name) {
where = {
...where,
[Op.and]: Utils.ilike(
'permissions',
'name',
filter.name,
),
};
}
if (filter.active !== undefined) {
where = {
...where,
active: filter.active === true || filter.active === 'true'
};
}
if (filter.createdAtRange) {
const [start, end] = filter.createdAtRange;
if (start !== undefined && start !== null && start !== '') {
where = {
...where,
['createdAt']: {
...where.createdAt,
[Op.gte]: start,
},
};
}
if (end !== undefined && end !== null && end !== '') {
where = {
...where,
['createdAt']: {
...where.createdAt,
[Op.lte]: end,
},
};
}
}
}
const queryOptions = {
where,
include,
distinct: true,
order: filter.field && filter.sort
? [[filter.field, filter.sort]]
: [['createdAt', 'desc']],
transaction: options?.transaction,
logging: console.log
};
if (!options?.countOnly) {
queryOptions.limit = limit ? Number(limit) : undefined;
queryOptions.offset = offset ? Number(offset) : undefined;
}
try {
const { rows, count } = await db.permissions.findAndCountAll(queryOptions);
return {
rows: options?.countOnly ? [] : rows,
count: count
};
} catch (error) {
console.error('Error executing query:', error);
throw error;
}
}
static async findAllAutocomplete(query, limit, offset, ) {
let where = {};
if (query) {
where = {
[Op.or]: [
{ ['id']: Utils.uuid(query) },
Utils.ilike(
'permissions',
'name',
query,
),
],
};
}
const records = await db.permissions.findAll({
attributes: [ 'id', 'name' ],
where,
limit: limit ? Number(limit) : undefined,
offset: offset ? Number(offset) : undefined,
orderBy: [['name', 'ASC']],
});
return records.map((record) => ({
id: record.id,
label: record.name,
}));
}
};

View File

@ -1,323 +1,78 @@
const GenericDBApi = require('./base.api');
const db = require('../models'); const db = require('../models');
const Utils = require('../utils'); const Utils = require('../utils');
const Sequelize = db.Sequelize; const Sequelize = db.Sequelize;
const Op = Sequelize.Op; const Op = Sequelize.Op;
module.exports = class Presigned_url_requestsDBApi { class Presigned_url_requestsDBApi extends GenericDBApi {
static get MODEL() {
return db.presigned_url_requests;
}
static async create(data, options) {
const currentUser = (options && options.currentUser) || { id: null }; static get TABLE_NAME() {
const transaction = (options && options.transaction) || undefined; return 'presigned_url_requests';
}
const presigned_url_requests = await db.presigned_url_requests.create(
{ static get SEARCHABLE_FIELDS() {
id: data.id || undefined, return ['requested_key', 'mime_type', 'status'];
}
purpose: data.purpose
|| static get RANGE_FIELDS() {
null return ['requested_size_mb', 'expires_at'];
, }
asset_type: data.asset_type static get ENUM_FIELDS() {
|| return ['purpose', 'asset_type'];
null }
,
static get CSV_FIELDS() {
requested_key: data.requested_key return ['id', 'purpose', 'asset_type', 'requested_key', 'mime_type', 'status', 'createdAt'];
|| }
null
, static get AUTOCOMPLETE_FIELD() {
return 'requested_key';
mime_type: data.mime_type }
||
null static get ASSOCIATIONS() {
, return [
{ field: 'project', setter: 'setProject', isArray: false },
requested_size_mb: data.requested_size_mb { field: 'user', setter: 'setUser', isArray: false },
|| ];
null }
,
static get FIND_BY_INCLUDES() {
expires_at: data.expires_at return [
|| { association: 'project' },
null { association: 'user' },
, ];
}
status: data.status
|| static getFieldMapping(data) {
null return {
, id: data.id || undefined,
purpose: data.purpose || null,
importHash: data.importHash || null, asset_type: data.asset_type || null,
createdById: currentUser.id, requested_key: data.requested_key || null,
updatedById: currentUser.id, mime_type: data.mime_type || null,
}, requested_size_mb: data.requested_size_mb || null,
{ transaction }, expires_at: data.expires_at || null,
); status: data.status || null,
};
}
await presigned_url_requests.setProject( data.project || null, {
transaction, static async findAll(filter = {}, options = {}) {
}); filter = filter || {};
const limit = filter.limit || 0;
await presigned_url_requests.setUser( data.user || null, { const currentPage = +filter.page || 0;
transaction, const offset = currentPage * limit;
});
let where = {};
return presigned_url_requests;
}
static async bulkImport(data, options) {
const currentUser = (options && options.currentUser) || { id: null };
const transaction = (options && options.transaction) || undefined;
// Prepare data - wrapping individual data transformations in a map() method
const presigned_url_requestsData = data.map((item, index) => ({
id: item.id || undefined,
purpose: item.purpose
||
null
,
asset_type: item.asset_type
||
null
,
requested_key: item.requested_key
||
null
,
mime_type: item.mime_type
||
null
,
requested_size_mb: item.requested_size_mb
||
null
,
expires_at: item.expires_at
||
null
,
status: item.status
||
null
,
importHash: item.importHash || null,
createdById: currentUser.id,
updatedById: currentUser.id,
createdAt: new Date(Date.now() + index * 1000),
}));
// Bulk create items
const presigned_url_requests = await db.presigned_url_requests.bulkCreate(presigned_url_requestsData, { transaction });
// For each item created, replace relation files
return presigned_url_requests;
}
static async update(id, data, options) {
const currentUser = (options && options.currentUser) || {id: null};
const transaction = (options && options.transaction) || undefined;
const presigned_url_requests = await db.presigned_url_requests.findByPk(id, {transaction});
const updatePayload = {};
if (data.purpose !== undefined) updatePayload.purpose = data.purpose;
if (data.asset_type !== undefined) updatePayload.asset_type = data.asset_type;
if (data.requested_key !== undefined) updatePayload.requested_key = data.requested_key;
if (data.mime_type !== undefined) updatePayload.mime_type = data.mime_type;
if (data.requested_size_mb !== undefined) updatePayload.requested_size_mb = data.requested_size_mb;
if (data.expires_at !== undefined) updatePayload.expires_at = data.expires_at;
if (data.status !== undefined) updatePayload.status = data.status;
updatePayload.updatedById = currentUser.id;
await presigned_url_requests.update(updatePayload, {transaction});
if (data.project !== undefined) {
await presigned_url_requests.setProject(
data.project,
{ transaction }
);
}
if (data.user !== undefined) {
await presigned_url_requests.setUser(
data.user,
{ transaction }
);
}
return presigned_url_requests;
}
static async deleteByIds(ids, options) {
const currentUser = (options && options.currentUser) || { id: null };
const transaction = (options && options.transaction) || undefined;
const presigned_url_requests = await db.presigned_url_requests.findAll({
where: {
id: {
[Op.in]: ids,
},
},
transaction,
});
await db.sequelize.transaction(async (transaction) => {
for (const record of presigned_url_requests) {
await record.update(
{deletedBy: currentUser.id},
{transaction}
);
}
for (const record of presigned_url_requests) {
await record.destroy({transaction});
}
});
return presigned_url_requests;
}
static async remove(id, options) {
const currentUser = (options && options.currentUser) || {id: null};
const transaction = (options && options.transaction) || undefined;
const presigned_url_requests = await db.presigned_url_requests.findByPk(id, options);
await presigned_url_requests.update({
deletedBy: currentUser.id
}, {
transaction,
});
await presigned_url_requests.destroy({
transaction
});
return presigned_url_requests;
}
static async findBy(where, options) {
const transaction = (options && options.transaction) || undefined;
const presigned_url_requests = await db.presigned_url_requests.findOne({
where,
transaction,
});
if (!presigned_url_requests) {
return presigned_url_requests;
}
const output = presigned_url_requests.get({plain: true});
output.project = await presigned_url_requests.getProject({
transaction
});
output.user = await presigned_url_requests.getUser({
transaction
});
return output;
}
static async findAll(
filter,
options
) {
const limit = filter.limit || 0;
let offset = 0;
let where = {};
const currentPage = +filter.page;
offset = currentPage * limit;
let include = [ let include = [
{ {
model: db.projects, model: db.projects,
as: 'project', as: 'project',
where: filter.project ? { where: filter.project ? {
[Op.or]: [ [Op.or]: [
{ id: { [Op.in]: filter.project.split('|').map(term => Utils.uuid(term)) } }, { id: { [Op.in]: filter.project.split('|').map(term => Utils.uuid(term)) } },
@ -328,13 +83,10 @@ module.exports = class Presigned_url_requestsDBApi {
}, },
] ]
} : {}, } : {},
}, },
{ {
model: db.users, model: db.users,
as: 'user', as: 'user',
where: filter.user ? { where: filter.user ? {
[Op.or]: [ [Op.or]: [
{ id: { [Op.in]: filter.user.split('|').map(term => Utils.uuid(term)) } }, { id: { [Op.in]: filter.user.split('|').map(term => Utils.uuid(term)) } },
@ -345,227 +97,78 @@ module.exports = class Presigned_url_requestsDBApi {
}, },
] ]
} : {}, } : {},
}, },
]; ];
if (filter) { if (filter.id) {
if (filter.id) { where.id = Utils.uuid(filter.id);
where = {
...where,
['id']: Utils.uuid(filter.id),
};
}
if (filter.requested_key) {
where = {
...where,
[Op.and]: Utils.ilike(
'presigned_url_requests',
'requested_key',
filter.requested_key,
),
};
}
if (filter.mime_type) {
where = {
...where,
[Op.and]: Utils.ilike(
'presigned_url_requests',
'mime_type',
filter.mime_type,
),
};
}
if (filter.status) {
where = {
...where,
[Op.and]: Utils.ilike(
'presigned_url_requests',
'status',
filter.status,
),
};
}
if (filter.requested_size_mbRange) {
const [start, end] = filter.requested_size_mbRange;
if (start !== undefined && start !== null && start !== '') {
where = {
...where,
requested_size_mb: {
...where.requested_size_mb,
[Op.gte]: start,
},
};
}
if (end !== undefined && end !== null && end !== '') {
where = {
...where,
requested_size_mb: {
...where.requested_size_mb,
[Op.lte]: end,
},
};
}
}
if (filter.expires_atRange) {
const [start, end] = filter.expires_atRange;
if (start !== undefined && start !== null && start !== '') {
where = {
...where,
expires_at: {
...where.expires_at,
[Op.gte]: start,
},
};
}
if (end !== undefined && end !== null && end !== '') {
where = {
...where,
expires_at: {
...where.expires_at,
[Op.lte]: end,
},
};
}
}
if (filter.active !== undefined) {
where = {
...where,
active: filter.active === true || filter.active === 'true'
};
}
if (filter.purpose) {
where = {
...where,
purpose: filter.purpose,
};
}
if (filter.asset_type) {
where = {
...where,
asset_type: filter.asset_type,
};
}
if (filter.createdAtRange) {
const [start, end] = filter.createdAtRange;
if (start !== undefined && start !== null && start !== '') {
where = {
...where,
['createdAt']: {
...where.createdAt,
[Op.gte]: start,
},
};
}
if (end !== undefined && end !== null && end !== '') {
where = {
...where,
['createdAt']: {
...where.createdAt,
[Op.lte]: end,
},
};
}
}
}
const queryOptions = {
where,
include,
distinct: true,
order: filter.field && filter.sort
? [[filter.field, filter.sort]]
: [['createdAt', 'desc']],
transaction: options?.transaction,
logging: console.log
};
if (!options?.countOnly) {
queryOptions.limit = limit ? Number(limit) : undefined;
queryOptions.offset = offset ? Number(offset) : undefined;
}
try {
const { rows, count } = await db.presigned_url_requests.findAndCountAll(queryOptions);
return {
rows: options?.countOnly ? [] : rows,
count: count
};
} catch (error) {
console.error('Error executing query:', error);
throw error;
}
} }
static async findAllAutocomplete(query, limit, offset, ) { for (const field of this.SEARCHABLE_FIELDS) {
let where = {}; if (filter[field]) {
where[Op.and] = Utils.ilike(this.TABLE_NAME, field, filter[field]);
}
if (query) {
where = {
[Op.or]: [
{ ['id']: Utils.uuid(query) },
Utils.ilike(
'presigned_url_requests',
'requested_key',
query,
),
],
};
}
const records = await db.presigned_url_requests.findAll({
attributes: [ 'id', 'requested_key' ],
where,
limit: limit ? Number(limit) : undefined,
offset: offset ? Number(offset) : undefined,
orderBy: [['requested_key', 'ASC']],
});
return records.map((record) => ({
id: record.id,
label: record.requested_key,
}));
} }
for (const field of this.RANGE_FIELDS) {
const rangeKey = `${field}Range`;
if (filter[rangeKey]) {
const [start, end] = filter[rangeKey];
if (start !== undefined && start !== null && start !== '') {
where[field] = { ...where[field], [Op.gte]: start };
}
if (end !== undefined && end !== null && end !== '') {
where[field] = { ...where[field], [Op.lte]: end };
}
}
}
}; for (const field of this.ENUM_FIELDS) {
if (filter[field] !== undefined) {
where[field] = filter[field];
}
}
if (filter.active !== undefined) {
where.active = filter.active === true || filter.active === 'true';
}
if (filter.createdAtRange) {
const [start, end] = filter.createdAtRange;
if (start !== undefined && start !== null && start !== '') {
where.createdAt = { ...where.createdAt, [Op.gte]: start };
}
if (end !== undefined && end !== null && end !== '') {
where.createdAt = { ...where.createdAt, [Op.lte]: end };
}
}
const queryOptions = {
where,
include,
distinct: true,
order: filter.field && filter.sort
? [[filter.field, filter.sort]]
: [['createdAt', 'desc']],
transaction: options.transaction,
};
if (!options.countOnly) {
queryOptions.limit = limit ? Number(limit) : undefined;
queryOptions.offset = offset ? Number(offset) : undefined;
}
try {
const { rows, count } = await this.MODEL.findAndCountAll(queryOptions);
return {
rows: options.countOnly ? [] : rows,
count,
};
} catch (error) {
console.error('Error executing query:', error);
throw error;
}
}
}
module.exports = Presigned_url_requestsDBApi;

View File

@ -1,4 +1,4 @@
const GenericDBApi = require('./base.api');
const db = require('../models'); const db = require('../models');
const Utils = require('../utils'); const Utils = require('../utils');
const { const {
@ -6,342 +6,89 @@ const {
applyRuntimeProjectFilter, applyRuntimeProjectFilter,
} = require('./runtime-context'); } = require('./runtime-context');
const Sequelize = db.Sequelize; const Sequelize = db.Sequelize;
const Op = Sequelize.Op; const Op = Sequelize.Op;
module.exports = class Project_audio_tracksDBApi { class Project_audio_tracksDBApi extends GenericDBApi {
static get MODEL() {
return db.project_audio_tracks;
}
static get TABLE_NAME() {
return 'project_audio_tracks';
}
static get SEARCHABLE_FIELDS() {
return ['source_key', 'name', 'slug', 'url'];
}
static async create(data, options) { static get RANGE_FIELDS() {
const currentUser = (options && options.currentUser) || { id: null }; return ['volume', 'sort_order'];
const transaction = (options && options.transaction) || undefined; }
const project_audio_tracks = await db.project_audio_tracks.create( static get ENUM_FIELDS() {
{ return ['environment', 'loop', 'is_enabled'];
id: data.id || undefined, }
environment: data.environment static get CSV_FIELDS() {
|| return ['id', 'environment', 'source_key', 'name', 'slug', 'url', 'loop', 'volume', 'createdAt'];
null }
,
source_key: data.source_key static get AUTOCOMPLETE_FIELD() {
|| return 'name';
null }
,
name: data.name static get ASSOCIATIONS() {
|| return [
null { field: 'project', setter: 'setProject', isArray: false },
, ];
}
slug: data.slug static getFieldMapping(data) {
|| return {
null id: data.id || undefined,
, environment: data.environment || null,
source_key: data.source_key || null,
name: data.name || null,
slug: data.slug || null,
url: data.url || null,
loop: data.loop || false,
volume: data.volume || null,
sort_order: data.sort_order || null,
is_enabled: data.is_enabled || false,
};
}
url: data.url static async findBy(where, options = {}) {
|| const transaction = options.transaction;
null const queryWhere = applyRuntimeEnvironment({ ...where }, options);
, const projectInclude = applyRuntimeProjectFilter(
{ model: db.projects, as: 'project' },
loop: data.loop options
||
false
,
volume: data.volume
||
null
,
sort_order: data.sort_order
||
null
,
is_enabled: data.is_enabled
||
false
,
importHash: data.importHash || null,
createdById: currentUser.id,
updatedById: currentUser.id,
},
{ transaction },
); );
const record = await this.MODEL.findOne({
await project_audio_tracks.setProject( data.project || null, { where: queryWhere,
transaction, transaction,
}); include: [projectInclude],
});
if (!record) return null;
return record.get({ plain: true });
}
return project_audio_tracks; static async findAll(filter = {}, options = {}) {
} filter = filter || {};
const limit = filter.limit || 0;
const currentPage = +filter.page || 0;
static async bulkImport(data, options) { const offset = currentPage * limit;
const currentUser = (options && options.currentUser) || { id: null };
const transaction = (options && options.transaction) || undefined; let where = {};
// Prepare data - wrapping individual data transformations in a map() method
const project_audio_tracksData = data.map((item, index) => ({
id: item.id || undefined,
environment: item.environment
||
null
,
source_key: item.source_key
||
null
,
name: item.name
||
null
,
slug: item.slug
||
null
,
url: item.url
||
null
,
loop: item.loop
||
false
,
volume: item.volume
||
null
,
sort_order: item.sort_order
||
null
,
is_enabled: item.is_enabled
||
false
,
importHash: item.importHash || null,
createdById: currentUser.id,
updatedById: currentUser.id,
createdAt: new Date(Date.now() + index * 1000),
}));
// Bulk create items
const project_audio_tracks = await db.project_audio_tracks.bulkCreate(project_audio_tracksData, { transaction });
// For each item created, replace relation files
return project_audio_tracks;
}
static async update(id, data, options) {
const currentUser = (options && options.currentUser) || {id: null};
const transaction = (options && options.transaction) || undefined;
const project_audio_tracks = await db.project_audio_tracks.findByPk(id, {transaction});
const updatePayload = {};
if (data.environment !== undefined) updatePayload.environment = data.environment;
if (data.source_key !== undefined) updatePayload.source_key = data.source_key;
if (data.name !== undefined) updatePayload.name = data.name;
if (data.slug !== undefined) updatePayload.slug = data.slug;
if (data.url !== undefined) updatePayload.url = data.url;
if (data.loop !== undefined) updatePayload.loop = data.loop;
if (data.volume !== undefined) updatePayload.volume = data.volume;
if (data.sort_order !== undefined) updatePayload.sort_order = data.sort_order;
if (data.is_enabled !== undefined) updatePayload.is_enabled = data.is_enabled;
updatePayload.updatedById = currentUser.id;
await project_audio_tracks.update(updatePayload, {transaction});
if (data.project !== undefined) {
await project_audio_tracks.setProject(
data.project,
{ transaction }
);
}
return project_audio_tracks;
}
static async deleteByIds(ids, options) {
const currentUser = (options && options.currentUser) || { id: null };
const transaction = (options && options.transaction) || undefined;
const project_audio_tracks = await db.project_audio_tracks.findAll({
where: {
id: {
[Op.in]: ids,
},
},
transaction,
});
await db.sequelize.transaction(async (transaction) => {
for (const record of project_audio_tracks) {
await record.update(
{deletedBy: currentUser.id},
{transaction}
);
}
for (const record of project_audio_tracks) {
await record.destroy({transaction});
}
});
return project_audio_tracks;
}
static async remove(id, options) {
const currentUser = (options && options.currentUser) || {id: null};
const transaction = (options && options.transaction) || undefined;
const project_audio_tracks = await db.project_audio_tracks.findByPk(id, options);
await project_audio_tracks.update({
deletedBy: currentUser.id
}, {
transaction,
});
await project_audio_tracks.destroy({
transaction
});
return project_audio_tracks;
}
static async findBy(where, options) {
const transaction = (options && options.transaction) || undefined;
const queryWhere = applyRuntimeEnvironment({ ...where }, options);
const projectInclude = applyRuntimeProjectFilter(
{
model: db.projects,
as: 'project',
},
options,
);
const project_audio_tracks = await db.project_audio_tracks.findOne(
{ where: queryWhere, include: [projectInclude], transaction },
);
if (!project_audio_tracks) {
return project_audio_tracks;
}
const output = project_audio_tracks.get({plain: true});
output.project = await project_audio_tracks.getProject({
transaction
});
return output;
}
static async findAll(
filter,
options
) {
filter = filter || {};
const limit = filter.limit || 0;
let offset = 0;
let where = {};
const currentPage = +filter.page;
offset = currentPage * limit;
let include = [ let include = [
{ {
model: db.projects, model: db.projects,
as: 'project', as: 'project',
where: filter.project ? { where: filter.project ? {
[Op.or]: [ [Op.or]: [
{ id: { [Op.in]: filter.project.split('|').map(term => Utils.uuid(term)) } }, { id: { [Op.in]: filter.project.split('|').map(term => Utils.uuid(term)) } },
@ -352,245 +99,82 @@ module.exports = class Project_audio_tracksDBApi {
}, },
] ]
} : {}, } : {},
}, },
]; ];
include[0] = applyRuntimeProjectFilter(include[0], options);
if (filter) { include[0] = applyRuntimeProjectFilter(include[0], options);
if (filter.id) {
where = {
...where,
['id']: Utils.uuid(filter.id),
};
}
if (filter.id) {
if (filter.source_key) { where.id = Utils.uuid(filter.id);
where = {
...where,
[Op.and]: Utils.ilike(
'project_audio_tracks',
'source_key',
filter.source_key,
),
};
}
if (filter.name) {
where = {
...where,
[Op.and]: Utils.ilike(
'project_audio_tracks',
'name',
filter.name,
),
};
}
if (filter.slug) {
where = {
...where,
[Op.and]: Utils.ilike(
'project_audio_tracks',
'slug',
filter.slug,
),
};
}
if (filter.url) {
where = {
...where,
[Op.and]: Utils.ilike(
'project_audio_tracks',
'url',
filter.url,
),
};
}
if (filter.volumeRange) {
const [start, end] = filter.volumeRange;
if (start !== undefined && start !== null && start !== '') {
where = {
...where,
volume: {
...where.volume,
[Op.gte]: start,
},
};
}
if (end !== undefined && end !== null && end !== '') {
where = {
...where,
volume: {
...where.volume,
[Op.lte]: end,
},
};
}
}
if (filter.sort_orderRange) {
const [start, end] = filter.sort_orderRange;
if (start !== undefined && start !== null && start !== '') {
where = {
...where,
sort_order: {
...where.sort_order,
[Op.gte]: start,
},
};
}
if (end !== undefined && end !== null && end !== '') {
where = {
...where,
sort_order: {
...where.sort_order,
[Op.lte]: end,
},
};
}
}
if (filter.active !== undefined) {
where = {
...where,
active: filter.active === true || filter.active === 'true'
};
}
if (filter.environment) {
where = {
...where,
environment: filter.environment,
};
}
if (filter.loop) {
where = {
...where,
loop: filter.loop,
};
}
if (filter.is_enabled) {
where = {
...where,
is_enabled: filter.is_enabled,
};
}
if (filter.createdAtRange) {
const [start, end] = filter.createdAtRange;
if (start !== undefined && start !== null && start !== '') {
where = {
...where,
['createdAt']: {
...where.createdAt,
[Op.gte]: start,
},
};
}
if (end !== undefined && end !== null && end !== '') {
where = {
...where,
['createdAt']: {
...where.createdAt,
[Op.lte]: end,
},
};
}
}
}
where = applyRuntimeEnvironment(where, options);
const queryOptions = {
where,
include,
distinct: true,
order: filter.field && filter.sort
? [[filter.field, filter.sort]]
: [['createdAt', 'desc']],
transaction: options?.transaction,
logging: console.log
};
if (!options?.countOnly) {
queryOptions.limit = limit ? Number(limit) : undefined;
queryOptions.offset = offset ? Number(offset) : undefined;
}
try {
const { rows, count } = await db.project_audio_tracks.findAndCountAll(queryOptions);
return {
rows: options?.countOnly ? [] : rows,
count: count
};
} catch (error) {
console.error('Error executing query:', error);
throw error;
}
} }
static async findAllAutocomplete(query, limit, offset, ) { for (const field of this.SEARCHABLE_FIELDS) {
let where = {}; if (filter[field]) {
where[Op.and] = Utils.ilike(this.TABLE_NAME, field, filter[field]);
}
if (query) {
where = {
[Op.or]: [
{ ['id']: Utils.uuid(query) },
Utils.ilike(
'project_audio_tracks',
'name',
query,
),
],
};
}
const records = await db.project_audio_tracks.findAll({
attributes: [ 'id', 'name' ],
where,
limit: limit ? Number(limit) : undefined,
offset: offset ? Number(offset) : undefined,
orderBy: [['name', 'ASC']],
});
return records.map((record) => ({
id: record.id,
label: record.name,
}));
} }
for (const field of this.RANGE_FIELDS) {
const rangeKey = `${field}Range`;
if (filter[rangeKey]) {
const [start, end] = filter[rangeKey];
if (start !== undefined && start !== null && start !== '') {
where[field] = { ...where[field], [Op.gte]: start };
}
if (end !== undefined && end !== null && end !== '') {
where[field] = { ...where[field], [Op.lte]: end };
}
}
}
}; for (const field of this.ENUM_FIELDS) {
if (filter[field] !== undefined) {
where[field] = filter[field];
}
}
if (filter.active !== undefined) {
where.active = filter.active === true || filter.active === 'true';
}
if (filter.createdAtRange) {
const [start, end] = filter.createdAtRange;
if (start !== undefined && start !== null && start !== '') {
where.createdAt = { ...where.createdAt, [Op.gte]: start };
}
if (end !== undefined && end !== null && end !== '') {
where.createdAt = { ...where.createdAt, [Op.lte]: end };
}
}
where = applyRuntimeEnvironment(where, options);
const queryOptions = {
where,
include,
distinct: true,
order: filter.field && filter.sort
? [[filter.field, filter.sort]]
: [['createdAt', 'desc']],
transaction: options.transaction,
};
if (!options.countOnly) {
queryOptions.limit = limit ? Number(limit) : undefined;
queryOptions.offset = offset ? Number(offset) : undefined;
}
try {
const { rows, count } = await this.MODEL.findAndCountAll(queryOptions);
return {
rows: options.countOnly ? [] : rows,
count,
};
} catch (error) {
console.error('Error executing query:', error);
throw error;
}
}
}
module.exports = Project_audio_tracksDBApi;

View File

@ -1,286 +1,75 @@
const GenericDBApi = require('./base.api');
const db = require('../models'); const db = require('../models');
const Utils = require('../utils'); const Utils = require('../utils');
const Sequelize = db.Sequelize; const Sequelize = db.Sequelize;
const Op = Sequelize.Op; const Op = Sequelize.Op;
module.exports = class Project_membershipsDBApi { class Project_membershipsDBApi extends GenericDBApi {
static get MODEL() {
return db.project_memberships;
}
static async create(data, options) {
const currentUser = (options && options.currentUser) || { id: null }; static get TABLE_NAME() {
const transaction = (options && options.transaction) || undefined; return 'project_memberships';
}
const project_memberships = await db.project_memberships.create(
{ static get SEARCHABLE_FIELDS() {
id: data.id || undefined, return [];
}
access_level: data.access_level
|| static get RANGE_FIELDS() {
null return ['invited_at', 'accepted_at'];
, }
is_active: data.is_active static get ENUM_FIELDS() {
|| return ['access_level', 'is_active'];
false }
, static get CSV_FIELDS() {
return ['id', 'access_level', 'is_active', 'invited_at', 'accepted_at', 'createdAt'];
invited_at: data.invited_at }
||
null static get AUTOCOMPLETE_FIELD() {
, return 'access_level';
}
accepted_at: data.accepted_at
|| static get ASSOCIATIONS() {
null return [
, { field: 'project', setter: 'setProject', isArray: false },
{ field: 'user', setter: 'setUser', isArray: false },
importHash: data.importHash || null, ];
createdById: currentUser.id, }
updatedById: currentUser.id,
}, static get FIND_BY_INCLUDES() {
{ transaction }, return [
); { association: 'project' },
{ association: 'user' },
];
await project_memberships.setProject( data.project || null, { }
transaction,
}); static getFieldMapping(data) {
return {
await project_memberships.setUser( data.user || null, { id: data.id || undefined,
transaction, access_level: data.access_level || null,
}); is_active: data.is_active || false,
invited_at: data.invited_at || null,
accepted_at: data.accepted_at || null,
};
}
static async findAll(filter = {}, options = {}) {
return project_memberships; filter = filter || {};
} const limit = filter.limit || 0;
const currentPage = +filter.page || 0;
const offset = currentPage * limit;
static async bulkImport(data, options) {
const currentUser = (options && options.currentUser) || { id: null }; let where = {};
const transaction = (options && options.transaction) || undefined;
// Prepare data - wrapping individual data transformations in a map() method
const project_membershipsData = data.map((item, index) => ({
id: item.id || undefined,
access_level: item.access_level
||
null
,
is_active: item.is_active
||
false
,
invited_at: item.invited_at
||
null
,
accepted_at: item.accepted_at
||
null
,
importHash: item.importHash || null,
createdById: currentUser.id,
updatedById: currentUser.id,
createdAt: new Date(Date.now() + index * 1000),
}));
// Bulk create items
const project_memberships = await db.project_memberships.bulkCreate(project_membershipsData, { transaction });
// For each item created, replace relation files
return project_memberships;
}
static async update(id, data, options) {
const currentUser = (options && options.currentUser) || {id: null};
const transaction = (options && options.transaction) || undefined;
const project_memberships = await db.project_memberships.findByPk(id, {transaction});
const updatePayload = {};
if (data.access_level !== undefined) updatePayload.access_level = data.access_level;
if (data.is_active !== undefined) updatePayload.is_active = data.is_active;
if (data.invited_at !== undefined) updatePayload.invited_at = data.invited_at;
if (data.accepted_at !== undefined) updatePayload.accepted_at = data.accepted_at;
updatePayload.updatedById = currentUser.id;
await project_memberships.update(updatePayload, {transaction});
if (data.project !== undefined) {
await project_memberships.setProject(
data.project,
{ transaction }
);
}
if (data.user !== undefined) {
await project_memberships.setUser(
data.user,
{ transaction }
);
}
return project_memberships;
}
static async deleteByIds(ids, options) {
const currentUser = (options && options.currentUser) || { id: null };
const transaction = (options && options.transaction) || undefined;
const project_memberships = await db.project_memberships.findAll({
where: {
id: {
[Op.in]: ids,
},
},
transaction,
});
await db.sequelize.transaction(async (transaction) => {
for (const record of project_memberships) {
await record.update(
{deletedBy: currentUser.id},
{transaction}
);
}
for (const record of project_memberships) {
await record.destroy({transaction});
}
});
return project_memberships;
}
static async remove(id, options) {
const currentUser = (options && options.currentUser) || {id: null};
const transaction = (options && options.transaction) || undefined;
const project_memberships = await db.project_memberships.findByPk(id, options);
await project_memberships.update({
deletedBy: currentUser.id
}, {
transaction,
});
await project_memberships.destroy({
transaction
});
return project_memberships;
}
static async findBy(where, options) {
const transaction = (options && options.transaction) || undefined;
const project_memberships = await db.project_memberships.findOne({
where,
transaction,
});
if (!project_memberships) {
return project_memberships;
}
const output = project_memberships.get({plain: true});
output.project = await project_memberships.getProject({
transaction
});
output.user = await project_memberships.getUser({
transaction
});
return output;
}
static async findAll(
filter,
options
) {
const limit = filter.limit || 0;
let offset = 0;
let where = {};
const currentPage = +filter.page;
offset = currentPage * limit;
let include = [ let include = [
{ {
model: db.projects, model: db.projects,
as: 'project', as: 'project',
where: filter.project ? { where: filter.project ? {
[Op.or]: [ [Op.or]: [
{ id: { [Op.in]: filter.project.split('|').map(term => Utils.uuid(term)) } }, { id: { [Op.in]: filter.project.split('|').map(term => Utils.uuid(term)) } },
@ -291,13 +80,10 @@ module.exports = class Project_membershipsDBApi {
}, },
] ]
} : {}, } : {},
}, },
{ {
model: db.users, model: db.users,
as: 'user', as: 'user',
where: filter.user ? { where: filter.user ? {
[Op.or]: [ [Op.or]: [
{ id: { [Op.in]: filter.user.split('|').map(term => Utils.uuid(term)) } }, { id: { [Op.in]: filter.user.split('|').map(term => Utils.uuid(term)) } },
@ -308,194 +94,72 @@ module.exports = class Project_membershipsDBApi {
}, },
] ]
} : {}, } : {},
}, },
]; ];
if (filter) { if (filter.id) {
if (filter.id) { where.id = Utils.uuid(filter.id);
where = {
...where,
['id']: Utils.uuid(filter.id),
};
}
if (filter.invited_atRange) {
const [start, end] = filter.invited_atRange;
if (start !== undefined && start !== null && start !== '') {
where = {
...where,
invited_at: {
...where.invited_at,
[Op.gte]: start,
},
};
}
if (end !== undefined && end !== null && end !== '') {
where = {
...where,
invited_at: {
...where.invited_at,
[Op.lte]: end,
},
};
}
}
if (filter.accepted_atRange) {
const [start, end] = filter.accepted_atRange;
if (start !== undefined && start !== null && start !== '') {
where = {
...where,
accepted_at: {
...where.accepted_at,
[Op.gte]: start,
},
};
}
if (end !== undefined && end !== null && end !== '') {
where = {
...where,
accepted_at: {
...where.accepted_at,
[Op.lte]: end,
},
};
}
}
if (filter.active !== undefined) {
where = {
...where,
active: filter.active === true || filter.active === 'true'
};
}
if (filter.access_level) {
where = {
...where,
access_level: filter.access_level,
};
}
if (filter.is_active) {
where = {
...where,
is_active: filter.is_active,
};
}
if (filter.createdAtRange) {
const [start, end] = filter.createdAtRange;
if (start !== undefined && start !== null && start !== '') {
where = {
...where,
['createdAt']: {
...where.createdAt,
[Op.gte]: start,
},
};
}
if (end !== undefined && end !== null && end !== '') {
where = {
...where,
['createdAt']: {
...where.createdAt,
[Op.lte]: end,
},
};
}
}
}
const queryOptions = {
where,
include,
distinct: true,
order: filter.field && filter.sort
? [[filter.field, filter.sort]]
: [['createdAt', 'desc']],
transaction: options?.transaction,
logging: console.log
};
if (!options?.countOnly) {
queryOptions.limit = limit ? Number(limit) : undefined;
queryOptions.offset = offset ? Number(offset) : undefined;
}
try {
const { rows, count } = await db.project_memberships.findAndCountAll(queryOptions);
return {
rows: options?.countOnly ? [] : rows,
count: count
};
} catch (error) {
console.error('Error executing query:', error);
throw error;
}
} }
static async findAllAutocomplete(query, limit, offset, ) { for (const field of this.RANGE_FIELDS) {
let where = {}; const rangeKey = `${field}Range`;
if (filter[rangeKey]) {
const [start, end] = filter[rangeKey];
if (start !== undefined && start !== null && start !== '') {
if (query) { where[field] = { ...where[field], [Op.gte]: start };
where = {
[Op.or]: [
{ ['id']: Utils.uuid(query) },
Utils.ilike(
'project_memberships',
'access_level',
query,
),
],
};
} }
if (end !== undefined && end !== null && end !== '') {
const records = await db.project_memberships.findAll({ where[field] = { ...where[field], [Op.lte]: end };
attributes: [ 'id', 'access_level' ], }
where, }
limit: limit ? Number(limit) : undefined,
offset: offset ? Number(offset) : undefined,
orderBy: [['access_level', 'ASC']],
});
return records.map((record) => ({
id: record.id,
label: record.access_level,
}));
} }
for (const field of this.ENUM_FIELDS) {
if (filter[field] !== undefined) {
where[field] = filter[field];
}
}
}; if (filter.active !== undefined) {
where.active = filter.active === true || filter.active === 'true';
}
if (filter.createdAtRange) {
const [start, end] = filter.createdAtRange;
if (start !== undefined && start !== null && start !== '') {
where.createdAt = { ...where.createdAt, [Op.gte]: start };
}
if (end !== undefined && end !== null && end !== '') {
where.createdAt = { ...where.createdAt, [Op.lte]: end };
}
}
const queryOptions = {
where,
include,
distinct: true,
order: filter.field && filter.sort
? [[filter.field, filter.sort]]
: [['createdAt', 'desc']],
transaction: options.transaction,
};
if (!options.countOnly) {
queryOptions.limit = limit ? Number(limit) : undefined;
queryOptions.offset = offset ? Number(offset) : undefined;
}
try {
const { rows, count } = await this.MODEL.findAndCountAll(queryOptions);
return {
rows: options.countOnly ? [] : rows,
count,
};
} catch (error) {
console.error('Error executing query:', error);
throw error;
}
}
}
module.exports = Project_membershipsDBApi;

View File

@ -1,4 +1,4 @@
const GenericDBApi = require('./base.api');
const db = require('../models'); const db = require('../models');
const Utils = require('../utils'); const Utils = require('../utils');
const { const {
@ -6,694 +6,186 @@ const {
getRuntimeProjectSlug, getRuntimeProjectSlug,
} = require('./runtime-context'); } = require('./runtime-context');
const Sequelize = db.Sequelize; const Sequelize = db.Sequelize;
const Op = Sequelize.Op; const Op = Sequelize.Op;
module.exports = class ProjectsDBApi { class ProjectsDBApi extends GenericDBApi {
static get MODEL() {
return db.projects;
}
static get TABLE_NAME() {
return 'projects';
}
static get SEARCHABLE_FIELDS() {
return ['name', 'slug', 'description', 'logo_url', 'favicon_url', 'og_image_url', 'theme_config_json', 'custom_css_json', 'cdn_base_url', 'entry_page_slug'];
}
static async create(data, options) { static get RANGE_FIELDS() {
const currentUser = (options && options.currentUser) || { id: null }; return ['deleted_at_time'];
const transaction = (options && options.transaction) || undefined; }
const projects = await db.projects.create( static get ENUM_FIELDS() {
{ return ['phase', 'is_deleted'];
id: data.id || undefined, }
name: data.name static get CSV_FIELDS() {
|| return ['id', 'name', 'slug', 'description', 'phase', 'logo_url', 'cdn_base_url', 'createdAt'];
null }
,
slug: data.slug static get AUTOCOMPLETE_FIELD() {
|| return 'name';
null }
,
description: data.description static get ASSOCIATIONS() {
|| return [];
null }
,
phase: data.phase static getFieldMapping(data) {
|| return {
null id: data.id || undefined,
, name: data.name || null,
slug: data.slug || null,
description: data.description || null,
phase: data.phase || null,
logo_url: data.logo_url || null,
favicon_url: data.favicon_url || null,
og_image_url: data.og_image_url || null,
theme_config_json: data.theme_config_json || null,
custom_css_json: data.custom_css_json || null,
cdn_base_url: data.cdn_base_url || null,
entry_page_slug: data.entry_page_slug || null,
is_deleted: data.is_deleted || false,
deleted_at_time: data.deleted_at_time || null,
};
}
logo_url: data.logo_url static async findBy(where, options = {}) {
|| const transaction = options.transaction;
null const runtimeEnvironment = getRuntimeEnvironment(options);
, const runtimeProjectSlug = getRuntimeProjectSlug(options);
const queryWhere = { ...where };
favicon_url: data.favicon_url if (runtimeEnvironment) {
|| queryWhere.phase = runtimeEnvironment === 'production'
null ? 'production'
, : { [Op.in]: ['stage', 'production'] };
og_image_url: data.og_image_url
||
null
,
theme_config_json: data.theme_config_json
||
null
,
custom_css_json: data.custom_css_json
||
null
,
cdn_base_url: data.cdn_base_url
||
null
,
entry_page_slug: data.entry_page_slug
||
null
,
is_deleted: data.is_deleted
||
false
,
deleted_at_time: data.deleted_at_time
||
null
,
importHash: data.importHash || null,
createdById: currentUser.id,
updatedById: currentUser.id,
},
{ transaction },
);
return projects;
} }
if (runtimeProjectSlug) {
static async bulkImport(data, options) { queryWhere.slug = runtimeProjectSlug;
const currentUser = (options && options.currentUser) || { id: null };
const transaction = (options && options.transaction) || undefined;
// Prepare data - wrapping individual data transformations in a map() method
const projectsData = data.map((item, index) => ({
id: item.id || undefined,
name: item.name
||
null
,
slug: item.slug
||
null
,
description: item.description
||
null
,
phase: item.phase
||
null
,
logo_url: item.logo_url
||
null
,
favicon_url: item.favicon_url
||
null
,
og_image_url: item.og_image_url
||
null
,
theme_config_json: item.theme_config_json
||
null
,
custom_css_json: item.custom_css_json
||
null
,
cdn_base_url: item.cdn_base_url
||
null
,
entry_page_slug: item.entry_page_slug
||
null
,
is_deleted: item.is_deleted
||
false
,
deleted_at_time: item.deleted_at_time
||
null
,
importHash: item.importHash || null,
createdById: currentUser.id,
updatedById: currentUser.id,
createdAt: new Date(Date.now() + index * 1000),
}));
// Bulk create items
const projects = await db.projects.bulkCreate(projectsData, { transaction });
// For each item created, replace relation files
return projects;
} }
static async update(id, data, options) { const record = await this.MODEL.findOne({
const currentUser = (options && options.currentUser) || {id: null}; where: queryWhere,
const transaction = (options && options.transaction) || undefined; transaction,
include: [
{ association: 'project_memberships_project' },
{ association: 'assets_project' },
{ association: 'presigned_url_requests_project' },
{ association: 'tour_pages_project' },
{ association: 'transitions_project' },
{ association: 'project_audio_tracks_project' },
{ association: 'publish_events_project' },
{ association: 'pwa_caches_project' },
{ association: 'access_logs_project' },
],
});
if (!record) return null;
return record.get({ plain: true });
}
const projects = await db.projects.findByPk(id, {transaction}); static async findAll(filter = {}, options = {}) {
filter = filter || {};
const limit = filter.limit || 0;
const currentPage = +filter.page || 0;
const offset = currentPage * limit;
let where = {};
let include = [];
if (filter.id) {
where.id = Utils.uuid(filter.id);
const updatePayload = {};
if (data.name !== undefined) updatePayload.name = data.name;
if (data.slug !== undefined) updatePayload.slug = data.slug;
if (data.description !== undefined) updatePayload.description = data.description;
if (data.phase !== undefined) updatePayload.phase = data.phase;
if (data.logo_url !== undefined) updatePayload.logo_url = data.logo_url;
if (data.favicon_url !== undefined) updatePayload.favicon_url = data.favicon_url;
if (data.og_image_url !== undefined) updatePayload.og_image_url = data.og_image_url;
if (data.theme_config_json !== undefined) updatePayload.theme_config_json = data.theme_config_json;
if (data.custom_css_json !== undefined) updatePayload.custom_css_json = data.custom_css_json;
if (data.cdn_base_url !== undefined) updatePayload.cdn_base_url = data.cdn_base_url;
if (data.entry_page_slug !== undefined) updatePayload.entry_page_slug = data.entry_page_slug;
if (data.is_deleted !== undefined) updatePayload.is_deleted = data.is_deleted;
if (data.deleted_at_time !== undefined) updatePayload.deleted_at_time = data.deleted_at_time;
updatePayload.updatedById = currentUser.id;
await projects.update(updatePayload, {transaction});
return projects;
} }
static async deleteByIds(ids, options) { for (const field of this.SEARCHABLE_FIELDS) {
const currentUser = (options && options.currentUser) || { id: null }; if (filter[field]) {
const transaction = (options && options.transaction) || undefined; where[Op.and] = Utils.ilike(this.TABLE_NAME, field, filter[field]);
}
const projects = await db.projects.findAll({
where: {
id: {
[Op.in]: ids,
},
},
transaction,
});
await db.sequelize.transaction(async (transaction) => {
for (const record of projects) {
await record.update(
{deletedBy: currentUser.id},
{transaction}
);
}
for (const record of projects) {
await record.destroy({transaction});
}
});
return projects;
} }
static async remove(id, options) { for (const field of this.RANGE_FIELDS) {
const currentUser = (options && options.currentUser) || {id: null}; const rangeKey = `${field}Range`;
const transaction = (options && options.transaction) || undefined; if (filter[rangeKey]) {
const [start, end] = filter[rangeKey];
const projects = await db.projects.findByPk(id, options); if (start !== undefined && start !== null && start !== '') {
where[field] = { ...where[field], [Op.gte]: start };
await projects.update({ }
deletedBy: currentUser.id if (end !== undefined && end !== null && end !== '') {
}, { where[field] = { ...where[field], [Op.lte]: end };
transaction, }
}); }
await projects.destroy({
transaction
});
return projects;
} }
static async findBy(where, options) { for (const field of this.ENUM_FIELDS) {
const transaction = (options && options.transaction) || undefined; if (filter[field] !== undefined) {
const runtimeEnvironment = getRuntimeEnvironment(options); where[field] = filter[field];
const runtimeProjectSlug = getRuntimeProjectSlug(options); }
const queryWhere = { ...where };
if (runtimeEnvironment) {
queryWhere.phase = runtimeEnvironment === 'production'
? 'production'
: { [Op.in]: ['stage', 'production'] };
}
if (runtimeProjectSlug) {
queryWhere.slug = runtimeProjectSlug;
}
const projects = await db.projects.findOne(
{ where: queryWhere, transaction },
);
if (!projects) {
return projects;
}
const output = projects.get({plain: true});
output.project_memberships_project = await projects.getProject_memberships_project({
transaction
});
output.assets_project = await projects.getAssets_project({
transaction
});
output.presigned_url_requests_project = await projects.getPresigned_url_requests_project({
transaction
});
output.tour_pages_project = await projects.getTour_pages_project({
transaction
});
output.transitions_project = await projects.getTransitions_project({
transaction
});
output.project_audio_tracks_project = await projects.getProject_audio_tracks_project({
transaction
});
output.publish_events_project = await projects.getPublish_events_project({
transaction
});
output.pwa_caches_project = await projects.getPwa_caches_project({
transaction
});
output.access_logs_project = await projects.getAccess_logs_project({
transaction
});
return output;
} }
static async findAll( if (filter.active !== undefined) {
filter, where.active = filter.active === true || filter.active === 'true';
options
) {
filter = filter || {};
const limit = filter.limit || 0;
let offset = 0;
let where = {};
const currentPage = +filter.page;
offset = currentPage * limit;
let include = [
];
if (filter) {
if (filter.id) {
where = {
...where,
['id']: Utils.uuid(filter.id),
};
}
if (filter.name) {
where = {
...where,
[Op.and]: Utils.ilike(
'projects',
'name',
filter.name,
),
};
}
if (filter.slug) {
where = {
...where,
[Op.and]: Utils.ilike(
'projects',
'slug',
filter.slug,
),
};
}
if (filter.description) {
where = {
...where,
[Op.and]: Utils.ilike(
'projects',
'description',
filter.description,
),
};
}
if (filter.logo_url) {
where = {
...where,
[Op.and]: Utils.ilike(
'projects',
'logo_url',
filter.logo_url,
),
};
}
if (filter.favicon_url) {
where = {
...where,
[Op.and]: Utils.ilike(
'projects',
'favicon_url',
filter.favicon_url,
),
};
}
if (filter.og_image_url) {
where = {
...where,
[Op.and]: Utils.ilike(
'projects',
'og_image_url',
filter.og_image_url,
),
};
}
if (filter.theme_config_json) {
where = {
...where,
[Op.and]: Utils.ilike(
'projects',
'theme_config_json',
filter.theme_config_json,
),
};
}
if (filter.custom_css_json) {
where = {
...where,
[Op.and]: Utils.ilike(
'projects',
'custom_css_json',
filter.custom_css_json,
),
};
}
if (filter.cdn_base_url) {
where = {
...where,
[Op.and]: Utils.ilike(
'projects',
'cdn_base_url',
filter.cdn_base_url,
),
};
}
if (filter.entry_page_slug) {
where = {
...where,
[Op.and]: Utils.ilike(
'projects',
'entry_page_slug',
filter.entry_page_slug,
),
};
}
if (filter.deleted_at_timeRange) {
const [start, end] = filter.deleted_at_timeRange;
if (start !== undefined && start !== null && start !== '') {
where = {
...where,
deleted_at_time: {
...where.deleted_at_time,
[Op.gte]: start,
},
};
}
if (end !== undefined && end !== null && end !== '') {
where = {
...where,
deleted_at_time: {
...where.deleted_at_time,
[Op.lte]: end,
},
};
}
}
if (filter.active !== undefined) {
where = {
...where,
active: filter.active === true || filter.active === 'true'
};
}
if (filter.phase) {
where = {
...where,
phase: filter.phase,
};
}
if (filter.is_deleted) {
where = {
...where,
is_deleted: filter.is_deleted,
};
}
if (filter.createdAtRange) {
const [start, end] = filter.createdAtRange;
if (start !== undefined && start !== null && start !== '') {
where = {
...where,
['createdAt']: {
...where.createdAt,
[Op.gte]: start,
},
};
}
if (end !== undefined && end !== null && end !== '') {
where = {
...where,
['createdAt']: {
...where.createdAt,
[Op.lte]: end,
},
};
}
}
}
const runtimeEnvironment = getRuntimeEnvironment(options);
const runtimeProjectSlug = getRuntimeProjectSlug(options);
if (runtimeEnvironment) {
where = {
...where,
phase: runtimeEnvironment,
};
}
if (runtimeProjectSlug) {
where = {
...where,
slug: runtimeProjectSlug,
};
}
const queryOptions = {
where,
include,
distinct: true,
order: filter.field && filter.sort
? [[filter.field, filter.sort]]
: [['createdAt', 'desc']],
transaction: options?.transaction,
logging: console.log
};
if (!options?.countOnly) {
queryOptions.limit = limit ? Number(limit) : undefined;
queryOptions.offset = offset ? Number(offset) : undefined;
}
try {
const { rows, count } = await db.projects.findAndCountAll(queryOptions);
return {
rows: options?.countOnly ? [] : rows,
count: count
};
} catch (error) {
console.error('Error executing query:', error);
throw error;
}
} }
static async findAllAutocomplete(query, limit, offset, ) { if (filter.createdAtRange) {
let where = {}; const [start, end] = filter.createdAtRange;
if (start !== undefined && start !== null && start !== '') {
where.createdAt = { ...where.createdAt, [Op.gte]: start };
}
if (query) { if (end !== undefined && end !== null && end !== '') {
where = { where.createdAt = { ...where.createdAt, [Op.lte]: end };
[Op.or]: [ }
{ ['id']: Utils.uuid(query) },
Utils.ilike(
'projects',
'name',
query,
),
],
};
}
const records = await db.projects.findAll({
attributes: [ 'id', 'name' ],
where,
limit: limit ? Number(limit) : undefined,
offset: offset ? Number(offset) : undefined,
orderBy: [['name', 'ASC']],
});
return records.map((record) => ({
id: record.id,
label: record.name,
}));
} }
const runtimeEnvironment = getRuntimeEnvironment(options);
const runtimeProjectSlug = getRuntimeProjectSlug(options);
}; if (runtimeEnvironment) {
where.phase = runtimeEnvironment;
}
if (runtimeProjectSlug) {
where.slug = runtimeProjectSlug;
}
const queryOptions = {
where,
include,
distinct: true,
order: filter.field && filter.sort
? [[filter.field, filter.sort]]
: [['createdAt', 'desc']],
transaction: options.transaction,
};
if (!options.countOnly) {
queryOptions.limit = limit ? Number(limit) : undefined;
queryOptions.offset = offset ? Number(offset) : undefined;
}
try {
const { rows, count } = await this.MODEL.findAndCountAll(queryOptions);
return {
rows: options.countOnly ? [] : rows,
count,
};
} catch (error) {
console.error('Error executing query:', error);
throw error;
}
}
}
module.exports = ProjectsDBApi;

View File

@ -1,375 +1,82 @@
const GenericDBApi = require('./base.api');
const db = require('../models'); const db = require('../models');
const Utils = require('../utils'); const Utils = require('../utils');
const Sequelize = db.Sequelize; const Sequelize = db.Sequelize;
const Op = Sequelize.Op; const Op = Sequelize.Op;
module.exports = class Publish_eventsDBApi { class Publish_eventsDBApi extends GenericDBApi {
static get MODEL() {
return db.publish_events;
}
static async create(data, options) {
const currentUser = (options && options.currentUser) || { id: null }; static get TABLE_NAME() {
const transaction = (options && options.transaction) || undefined; return 'publish_events';
}
const publish_events = await db.publish_events.create(
{ static get SEARCHABLE_FIELDS() {
id: data.id || undefined, return ['title', 'description', 'error_message'];
}
title: data.title
|| static get RANGE_FIELDS() {
null return ['started_at', 'finished_at', 'pages_copied', 'transitions_copied', 'audios_copied'];
, }
description: data.description static get ENUM_FIELDS() {
|| return ['from_environment', 'to_environment', 'status'];
null }
,
static get CSV_FIELDS() {
from_environment: data.from_environment return ['id', 'title', 'description', 'from_environment', 'to_environment', 'status', 'pages_copied', 'createdAt'];
|| }
null
, static get AUTOCOMPLETE_FIELD() {
return 'status';
to_environment: data.to_environment }
||
null static get ASSOCIATIONS() {
, return [
{ field: 'project', setter: 'setProject', isArray: false },
started_at: data.started_at { field: 'user', setter: 'setUser', isArray: false },
|| ];
null }
,
static get FIND_BY_INCLUDES() {
finished_at: data.finished_at return [
|| { association: 'project' },
null { association: 'user' },
, ];
}
status: data.status
|| static getFieldMapping(data) {
null return {
, id: data.id || undefined,
title: data.title || null,
error_message: data.error_message description: data.description || null,
|| from_environment: data.from_environment || null,
null to_environment: data.to_environment || null,
, started_at: data.started_at || null,
finished_at: data.finished_at || null,
pages_copied: data.pages_copied status: data.status || null,
|| error_message: data.error_message || null,
null pages_copied: data.pages_copied || null,
, transitions_copied: data.transitions_copied || null,
audios_copied: data.audios_copied || null,
transitions_copied: data.transitions_copied };
|| }
null
, static async findAll(filter = {}, options = {}) {
filter = filter || {};
audios_copied: data.audios_copied const limit = filter.limit || 0;
|| const currentPage = +filter.page || 0;
null const offset = currentPage * limit;
,
let where = {};
importHash: data.importHash || null,
createdById: currentUser.id,
updatedById: currentUser.id,
},
{ transaction },
);
await publish_events.setProject( data.project || null, {
transaction,
});
await publish_events.setUser( data.user || null, {
transaction,
});
return publish_events;
}
static async bulkImport(data, options) {
const currentUser = (options && options.currentUser) || { id: null };
const transaction = (options && options.transaction) || undefined;
// Prepare data - wrapping individual data transformations in a map() method
const publish_eventsData = data.map((item, index) => ({
id: item.id || undefined,
title: item.title
||
null
,
description: item.description
||
null
,
from_environment: item.from_environment
||
null
,
to_environment: item.to_environment
||
null
,
started_at: item.started_at
||
null
,
finished_at: item.finished_at
||
null
,
status: item.status
||
null
,
error_message: item.error_message
||
null
,
pages_copied: item.pages_copied
||
null
,
transitions_copied: item.transitions_copied
||
null
,
audios_copied: item.audios_copied
||
null
,
importHash: item.importHash || null,
createdById: currentUser.id,
updatedById: currentUser.id,
createdAt: new Date(Date.now() + index * 1000),
}));
// Bulk create items
const publish_events = await db.publish_events.bulkCreate(publish_eventsData, { transaction });
// For each item created, replace relation files
return publish_events;
}
static async update(id, data, options) {
const currentUser = (options && options.currentUser) || {id: null};
const transaction = (options && options.transaction) || undefined;
const publish_events = await db.publish_events.findByPk(id, {transaction});
const updatePayload = {};
if (data.title !== undefined) updatePayload.title = data.title;
if (data.description !== undefined) updatePayload.description = data.description;
if (data.from_environment !== undefined) updatePayload.from_environment = data.from_environment;
if (data.to_environment !== undefined) updatePayload.to_environment = data.to_environment;
if (data.started_at !== undefined) updatePayload.started_at = data.started_at;
if (data.finished_at !== undefined) updatePayload.finished_at = data.finished_at;
if (data.status !== undefined) updatePayload.status = data.status;
if (data.error_message !== undefined) updatePayload.error_message = data.error_message;
if (data.pages_copied !== undefined) updatePayload.pages_copied = data.pages_copied;
if (data.transitions_copied !== undefined) updatePayload.transitions_copied = data.transitions_copied;
if (data.audios_copied !== undefined) updatePayload.audios_copied = data.audios_copied;
updatePayload.updatedById = currentUser.id;
await publish_events.update(updatePayload, {transaction});
if (data.project !== undefined) {
await publish_events.setProject(
data.project,
{ transaction }
);
}
if (data.user !== undefined) {
await publish_events.setUser(
data.user,
{ transaction }
);
}
return publish_events;
}
static async deleteByIds(ids, options) {
const currentUser = (options && options.currentUser) || { id: null };
const transaction = (options && options.transaction) || undefined;
const publish_events = await db.publish_events.findAll({
where: {
id: {
[Op.in]: ids,
},
},
transaction,
});
await db.sequelize.transaction(async (transaction) => {
for (const record of publish_events) {
await record.update(
{deletedBy: currentUser.id},
{transaction}
);
}
for (const record of publish_events) {
await record.destroy({transaction});
}
});
return publish_events;
}
static async remove(id, options) {
const currentUser = (options && options.currentUser) || {id: null};
const transaction = (options && options.transaction) || undefined;
const publish_events = await db.publish_events.findByPk(id, options);
await publish_events.update({
deletedBy: currentUser.id
}, {
transaction,
});
await publish_events.destroy({
transaction
});
return publish_events;
}
static async findBy(where, options) {
const transaction = (options && options.transaction) || undefined;
const publish_events = await db.publish_events.findOne({
where,
transaction,
});
if (!publish_events) {
return publish_events;
}
const output = publish_events.get({plain: true});
output.project = await publish_events.getProject({
transaction
});
output.user = await publish_events.getUser({
transaction
});
return output;
}
static async findAll(
filter,
options
) {
const limit = filter.limit || 0;
let offset = 0;
let where = {};
const currentPage = +filter.page;
offset = currentPage * limit;
let include = [ let include = [
{ {
model: db.projects, model: db.projects,
as: 'project', as: 'project',
where: filter.project ? { where: filter.project ? {
[Op.or]: [ [Op.or]: [
{ id: { [Op.in]: filter.project.split('|').map(term => Utils.uuid(term)) } }, { id: { [Op.in]: filter.project.split('|').map(term => Utils.uuid(term)) } },
@ -380,13 +87,10 @@ module.exports = class Publish_eventsDBApi {
}, },
] ]
} : {}, } : {},
}, },
{ {
model: db.users, model: db.users,
as: 'user', as: 'user',
where: filter.user ? { where: filter.user ? {
[Op.or]: [ [Op.or]: [
{ id: { [Op.in]: filter.user.split('|').map(term => Utils.uuid(term)) } }, { id: { [Op.in]: filter.user.split('|').map(term => Utils.uuid(term)) } },
@ -397,307 +101,78 @@ module.exports = class Publish_eventsDBApi {
}, },
] ]
} : {}, } : {},
}, },
]; ];
if (filter) { if (filter.id) {
if (filter.id) { where.id = Utils.uuid(filter.id);
where = {
...where,
['id']: Utils.uuid(filter.id),
};
}
if (filter.title) {
where = {
...where,
[Op.and]: Utils.ilike(
'publish_events',
'title',
filter.title,
),
};
}
if (filter.description) {
where = {
...where,
[Op.and]: Utils.ilike(
'publish_events',
'description',
filter.description,
),
};
}
if (filter.error_message) {
where = {
...where,
[Op.and]: Utils.ilike(
'publish_events',
'error_message',
filter.error_message,
),
};
}
if (filter.started_atRange) {
const [start, end] = filter.started_atRange;
if (start !== undefined && start !== null && start !== '') {
where = {
...where,
started_at: {
...where.started_at,
[Op.gte]: start,
},
};
}
if (end !== undefined && end !== null && end !== '') {
where = {
...where,
started_at: {
...where.started_at,
[Op.lte]: end,
},
};
}
}
if (filter.finished_atRange) {
const [start, end] = filter.finished_atRange;
if (start !== undefined && start !== null && start !== '') {
where = {
...where,
finished_at: {
...where.finished_at,
[Op.gte]: start,
},
};
}
if (end !== undefined && end !== null && end !== '') {
where = {
...where,
finished_at: {
...where.finished_at,
[Op.lte]: end,
},
};
}
}
if (filter.pages_copiedRange) {
const [start, end] = filter.pages_copiedRange;
if (start !== undefined && start !== null && start !== '') {
where = {
...where,
pages_copied: {
...where.pages_copied,
[Op.gte]: start,
},
};
}
if (end !== undefined && end !== null && end !== '') {
where = {
...where,
pages_copied: {
...where.pages_copied,
[Op.lte]: end,
},
};
}
}
if (filter.transitions_copiedRange) {
const [start, end] = filter.transitions_copiedRange;
if (start !== undefined && start !== null && start !== '') {
where = {
...where,
transitions_copied: {
...where.transitions_copied,
[Op.gte]: start,
},
};
}
if (end !== undefined && end !== null && end !== '') {
where = {
...where,
transitions_copied: {
...where.transitions_copied,
[Op.lte]: end,
},
};
}
}
if (filter.audios_copiedRange) {
const [start, end] = filter.audios_copiedRange;
if (start !== undefined && start !== null && start !== '') {
where = {
...where,
audios_copied: {
...where.audios_copied,
[Op.gte]: start,
},
};
}
if (end !== undefined && end !== null && end !== '') {
where = {
...where,
audios_copied: {
...where.audios_copied,
[Op.lte]: end,
},
};
}
}
if (filter.active !== undefined) {
where = {
...where,
active: filter.active === true || filter.active === 'true'
};
}
if (filter.from_environment) {
where = {
...where,
from_environment: filter.from_environment,
};
}
if (filter.to_environment) {
where = {
...where,
to_environment: filter.to_environment,
};
}
if (filter.status) {
where = {
...where,
status: filter.status,
};
}
if (filter.createdAtRange) {
const [start, end] = filter.createdAtRange;
if (start !== undefined && start !== null && start !== '') {
where = {
...where,
['createdAt']: {
...where.createdAt,
[Op.gte]: start,
},
};
}
if (end !== undefined && end !== null && end !== '') {
where = {
...where,
['createdAt']: {
...where.createdAt,
[Op.lte]: end,
},
};
}
}
}
const queryOptions = {
where,
include,
distinct: true,
order: filter.field && filter.sort
? [[filter.field, filter.sort]]
: [['createdAt', 'desc']],
transaction: options?.transaction,
logging: console.log
};
if (!options?.countOnly) {
queryOptions.limit = limit ? Number(limit) : undefined;
queryOptions.offset = offset ? Number(offset) : undefined;
}
try {
const { rows, count } = await db.publish_events.findAndCountAll(queryOptions);
return {
rows: options?.countOnly ? [] : rows,
count: count
};
} catch (error) {
console.error('Error executing query:', error);
throw error;
}
} }
static async findAllAutocomplete(query, limit, offset, ) { for (const field of this.SEARCHABLE_FIELDS) {
let where = {}; if (filter[field]) {
where[Op.and] = Utils.ilike(this.TABLE_NAME, field, filter[field]);
}
if (query) {
where = {
[Op.or]: [
{ ['id']: Utils.uuid(query) },
Utils.ilike(
'publish_events',
'status',
query,
),
],
};
}
const records = await db.publish_events.findAll({
attributes: [ 'id', 'status' ],
where,
limit: limit ? Number(limit) : undefined,
offset: offset ? Number(offset) : undefined,
orderBy: [['status', 'ASC']],
});
return records.map((record) => ({
id: record.id,
label: record.status,
}));
} }
for (const field of this.RANGE_FIELDS) {
const rangeKey = `${field}Range`;
if (filter[rangeKey]) {
const [start, end] = filter[rangeKey];
if (start !== undefined && start !== null && start !== '') {
where[field] = { ...where[field], [Op.gte]: start };
}
if (end !== undefined && end !== null && end !== '') {
where[field] = { ...where[field], [Op.lte]: end };
}
}
}
}; for (const field of this.ENUM_FIELDS) {
if (filter[field] !== undefined) {
where[field] = filter[field];
}
}
if (filter.active !== undefined) {
where.active = filter.active === true || filter.active === 'true';
}
if (filter.createdAtRange) {
const [start, end] = filter.createdAtRange;
if (start !== undefined && start !== null && start !== '') {
where.createdAt = { ...where.createdAt, [Op.gte]: start };
}
if (end !== undefined && end !== null && end !== '') {
where.createdAt = { ...where.createdAt, [Op.lte]: end };
}
}
const queryOptions = {
where,
include,
distinct: true,
order: filter.field && filter.sort
? [[filter.field, filter.sort]]
: [['createdAt', 'desc']],
transaction: options.transaction,
};
if (!options.countOnly) {
queryOptions.limit = limit ? Number(limit) : undefined;
queryOptions.offset = offset ? Number(offset) : undefined;
}
try {
const { rows, count } = await this.MODEL.findAndCountAll(queryOptions);
return {
rows: options.countOnly ? [] : rows,
count,
};
} catch (error) {
console.error('Error executing query:', error);
throw error;
}
}
}
module.exports = Publish_eventsDBApi;

View File

@ -1,294 +1,73 @@
const GenericDBApi = require('./base.api');
const db = require('../models'); const db = require('../models');
const Utils = require('../utils'); const Utils = require('../utils');
const Sequelize = db.Sequelize; const Sequelize = db.Sequelize;
const Op = Sequelize.Op; const Op = Sequelize.Op;
module.exports = class Pwa_cachesDBApi { class Pwa_cachesDBApi extends GenericDBApi {
static get MODEL() {
return db.pwa_caches;
}
static async create(data, options) {
const currentUser = (options && options.currentUser) || { id: null }; static get TABLE_NAME() {
const transaction = (options && options.transaction) || undefined; return 'pwa_caches';
}
const pwa_caches = await db.pwa_caches.create(
{ static get SEARCHABLE_FIELDS() {
id: data.id || undefined, return ['cache_version', 'manifest_json', 'asset_list_json'];
}
environment: data.environment
|| static get RANGE_FIELDS() {
null return ['generated_at'];
, }
cache_version: data.cache_version static get ENUM_FIELDS() {
|| return ['environment', 'is_active'];
null }
,
static get CSV_FIELDS() {
manifest_json: data.manifest_json return ['id', 'environment', 'cache_version', 'is_active', 'generated_at', 'createdAt'];
|| }
null
, static get AUTOCOMPLETE_FIELD() {
return 'cache_version';
asset_list_json: data.asset_list_json }
||
null static get ASSOCIATIONS() {
, return [
{ field: 'project', setter: 'setProject', isArray: false },
generated_at: data.generated_at ];
|| }
null
, static get FIND_BY_INCLUDES() {
return [{ association: 'project' }];
is_active: data.is_active }
||
false static getFieldMapping(data) {
return {
, id: data.id || undefined,
environment: data.environment || null,
importHash: data.importHash || null, cache_version: data.cache_version || null,
createdById: currentUser.id, manifest_json: data.manifest_json || null,
updatedById: currentUser.id, asset_list_json: data.asset_list_json || null,
}, generated_at: data.generated_at || null,
{ transaction }, is_active: data.is_active || false,
); };
}
await pwa_caches.setProject( data.project || null, { static async findAll(filter = {}, options = {}) {
transaction, filter = filter || {};
}); const limit = filter.limit || 0;
const currentPage = +filter.page || 0;
const offset = currentPage * limit;
let where = {};
return pwa_caches;
}
static async bulkImport(data, options) {
const currentUser = (options && options.currentUser) || { id: null };
const transaction = (options && options.transaction) || undefined;
// Prepare data - wrapping individual data transformations in a map() method
const pwa_cachesData = data.map((item, index) => ({
id: item.id || undefined,
environment: item.environment
||
null
,
cache_version: item.cache_version
||
null
,
manifest_json: item.manifest_json
||
null
,
asset_list_json: item.asset_list_json
||
null
,
generated_at: item.generated_at
||
null
,
is_active: item.is_active
||
false
,
importHash: item.importHash || null,
createdById: currentUser.id,
updatedById: currentUser.id,
createdAt: new Date(Date.now() + index * 1000),
}));
// Bulk create items
const pwa_caches = await db.pwa_caches.bulkCreate(pwa_cachesData, { transaction });
// For each item created, replace relation files
return pwa_caches;
}
static async update(id, data, options) {
const currentUser = (options && options.currentUser) || {id: null};
const transaction = (options && options.transaction) || undefined;
const pwa_caches = await db.pwa_caches.findByPk(id, {transaction});
const updatePayload = {};
if (data.environment !== undefined) updatePayload.environment = data.environment;
if (data.cache_version !== undefined) updatePayload.cache_version = data.cache_version;
if (data.manifest_json !== undefined) updatePayload.manifest_json = data.manifest_json;
if (data.asset_list_json !== undefined) updatePayload.asset_list_json = data.asset_list_json;
if (data.generated_at !== undefined) updatePayload.generated_at = data.generated_at;
if (data.is_active !== undefined) updatePayload.is_active = data.is_active;
updatePayload.updatedById = currentUser.id;
await pwa_caches.update(updatePayload, {transaction});
if (data.project !== undefined) {
await pwa_caches.setProject(
data.project,
{ transaction }
);
}
return pwa_caches;
}
static async deleteByIds(ids, options) {
const currentUser = (options && options.currentUser) || { id: null };
const transaction = (options && options.transaction) || undefined;
const pwa_caches = await db.pwa_caches.findAll({
where: {
id: {
[Op.in]: ids,
},
},
transaction,
});
await db.sequelize.transaction(async (transaction) => {
for (const record of pwa_caches) {
await record.update(
{deletedBy: currentUser.id},
{transaction}
);
}
for (const record of pwa_caches) {
await record.destroy({transaction});
}
});
return pwa_caches;
}
static async remove(id, options) {
const currentUser = (options && options.currentUser) || {id: null};
const transaction = (options && options.transaction) || undefined;
const pwa_caches = await db.pwa_caches.findByPk(id, options);
await pwa_caches.update({
deletedBy: currentUser.id
}, {
transaction,
});
await pwa_caches.destroy({
transaction
});
return pwa_caches;
}
static async findBy(where, options) {
const transaction = (options && options.transaction) || undefined;
const pwa_caches = await db.pwa_caches.findOne({
where,
transaction,
});
if (!pwa_caches) {
return pwa_caches;
}
const output = pwa_caches.get({plain: true});
output.project = await pwa_caches.getProject({
transaction
});
return output;
}
static async findAll(
filter,
options
) {
const limit = filter.limit || 0;
let offset = 0;
let where = {};
const currentPage = +filter.page;
offset = currentPage * limit;
let include = [ let include = [
{ {
model: db.projects, model: db.projects,
as: 'project', as: 'project',
where: filter.project ? { where: filter.project ? {
[Op.or]: [ [Op.or]: [
{ id: { [Op.in]: filter.project.split('|').map(term => Utils.uuid(term)) } }, { id: { [Op.in]: filter.project.split('|').map(term => Utils.uuid(term)) } },
@ -299,201 +78,78 @@ module.exports = class Pwa_cachesDBApi {
}, },
] ]
} : {}, } : {},
}, },
]; ];
if (filter) { if (filter.id) {
if (filter.id) { where.id = Utils.uuid(filter.id);
where = {
...where,
['id']: Utils.uuid(filter.id),
};
}
if (filter.cache_version) {
where = {
...where,
[Op.and]: Utils.ilike(
'pwa_caches',
'cache_version',
filter.cache_version,
),
};
}
if (filter.manifest_json) {
where = {
...where,
[Op.and]: Utils.ilike(
'pwa_caches',
'manifest_json',
filter.manifest_json,
),
};
}
if (filter.asset_list_json) {
where = {
...where,
[Op.and]: Utils.ilike(
'pwa_caches',
'asset_list_json',
filter.asset_list_json,
),
};
}
if (filter.generated_atRange) {
const [start, end] = filter.generated_atRange;
if (start !== undefined && start !== null && start !== '') {
where = {
...where,
generated_at: {
...where.generated_at,
[Op.gte]: start,
},
};
}
if (end !== undefined && end !== null && end !== '') {
where = {
...where,
generated_at: {
...where.generated_at,
[Op.lte]: end,
},
};
}
}
if (filter.active !== undefined) {
where = {
...where,
active: filter.active === true || filter.active === 'true'
};
}
if (filter.environment) {
where = {
...where,
environment: filter.environment,
};
}
if (filter.is_active) {
where = {
...where,
is_active: filter.is_active,
};
}
if (filter.createdAtRange) {
const [start, end] = filter.createdAtRange;
if (start !== undefined && start !== null && start !== '') {
where = {
...where,
['createdAt']: {
...where.createdAt,
[Op.gte]: start,
},
};
}
if (end !== undefined && end !== null && end !== '') {
where = {
...where,
['createdAt']: {
...where.createdAt,
[Op.lte]: end,
},
};
}
}
}
const queryOptions = {
where,
include,
distinct: true,
order: filter.field && filter.sort
? [[filter.field, filter.sort]]
: [['createdAt', 'desc']],
transaction: options?.transaction,
logging: console.log
};
if (!options?.countOnly) {
queryOptions.limit = limit ? Number(limit) : undefined;
queryOptions.offset = offset ? Number(offset) : undefined;
}
try {
const { rows, count } = await db.pwa_caches.findAndCountAll(queryOptions);
return {
rows: options?.countOnly ? [] : rows,
count: count
};
} catch (error) {
console.error('Error executing query:', error);
throw error;
}
} }
static async findAllAutocomplete(query, limit, offset, ) { for (const field of this.SEARCHABLE_FIELDS) {
let where = {}; if (filter[field]) {
where[Op.and] = Utils.ilike(this.TABLE_NAME, field, filter[field]);
}
if (query) {
where = {
[Op.or]: [
{ ['id']: Utils.uuid(query) },
Utils.ilike(
'pwa_caches',
'cache_version',
query,
),
],
};
}
const records = await db.pwa_caches.findAll({
attributes: [ 'id', 'cache_version' ],
where,
limit: limit ? Number(limit) : undefined,
offset: offset ? Number(offset) : undefined,
orderBy: [['cache_version', 'ASC']],
});
return records.map((record) => ({
id: record.id,
label: record.cache_version,
}));
} }
for (const field of this.RANGE_FIELDS) {
const rangeKey = `${field}Range`;
if (filter[rangeKey]) {
const [start, end] = filter[rangeKey];
if (start !== undefined && start !== null && start !== '') {
where[field] = { ...where[field], [Op.gte]: start };
}
if (end !== undefined && end !== null && end !== '') {
where[field] = { ...where[field], [Op.lte]: end };
}
}
}
}; for (const field of this.ENUM_FIELDS) {
if (filter[field] !== undefined) {
where[field] = filter[field];
}
}
if (filter.active !== undefined) {
where.active = filter.active === true || filter.active === 'true';
}
if (filter.createdAtRange) {
const [start, end] = filter.createdAtRange;
if (start !== undefined && start !== null && start !== '') {
where.createdAt = { ...where.createdAt, [Op.gte]: start };
}
if (end !== undefined && end !== null && end !== '') {
where.createdAt = { ...where.createdAt, [Op.lte]: end };
}
}
const queryOptions = {
where,
include,
distinct: true,
order: filter.field && filter.sort
? [[filter.field, filter.sort]]
: [['createdAt', 'desc']],
transaction: options.transaction,
};
if (!options.countOnly) {
queryOptions.limit = limit ? Number(limit) : undefined;
queryOptions.offset = offset ? Number(offset) : undefined;
}
try {
const { rows, count } = await this.MODEL.findAndCountAll(queryOptions);
return {
rows: options.countOnly ? [] : rows,
count,
};
} catch (error) {
console.error('Error executing query:', error);
throw error;
}
}
}
module.exports = Pwa_cachesDBApi;

View File

@ -1,405 +1,148 @@
const GenericDBApi = require('./base.api');
const db = require('../models'); const db = require('../models');
const Utils = require('../utils'); const Utils = require('../utils');
const Sequelize = db.Sequelize; const Sequelize = db.Sequelize;
const Op = Sequelize.Op; const Op = Sequelize.Op;
module.exports = class RolesDBApi { class RolesDBApi extends GenericDBApi {
static get MODEL() {
return db.roles;
}
static async create(data, options) {
const currentUser = (options && options.currentUser) || { id: null }; static get TABLE_NAME() {
const transaction = (options && options.transaction) || undefined; return 'roles';
}
const roles = await db.roles.create(
{ static get SEARCHABLE_FIELDS() {
id: data.id || undefined, return ['name', 'role_customization'];
}
name: data.name
|| static get RANGE_FIELDS() {
null return [];
, }
role_customization: data.role_customization static get ENUM_FIELDS() {
|| return [];
null }
,
static get CSV_FIELDS() {
importHash: data.importHash || null, return ['id', 'name', 'role_customization', 'createdAt'];
createdById: currentUser.id, }
updatedById: currentUser.id,
}, static get AUTOCOMPLETE_FIELD() {
{ transaction }, return 'name';
); }
static get ASSOCIATIONS() {
return [
{ field: 'permissions', setter: 'setPermissions', isArray: true },
await roles.setPermissions(data.permissions || [], { ];
transaction, }
});
static get FIND_BY_INCLUDES() {
return [
{ association: 'users_app_role' },
{ association: 'permissions' },
return roles; ];
} }
static getFieldMapping(data) {
static async bulkImport(data, options) { return {
const currentUser = (options && options.currentUser) || { id: null }; id: data.id || undefined,
const transaction = (options && options.transaction) || undefined; name: data.name || null,
role_customization: data.role_customization || null,
// Prepare data - wrapping individual data transformations in a map() method };
const rolesData = data.map((item, index) => ({ }
id: item.id || undefined,
static async findAll(filter = {}, options = {}) {
name: item.name filter = filter || {};
|| const limit = filter.limit || 0;
null const currentPage = +filter.page || 0;
, const offset = currentPage * limit;
role_customization: item.role_customization let where = {};
||
null
,
importHash: item.importHash || null,
createdById: currentUser.id,
updatedById: currentUser.id,
createdAt: new Date(Date.now() + index * 1000),
}));
// Bulk create items
const roles = await db.roles.bulkCreate(rolesData, { transaction });
// For each item created, replace relation files
return roles;
}
static async update(id, data, options) {
const currentUser = (options && options.currentUser) || {id: null};
const transaction = (options && options.transaction) || undefined;
const roles = await db.roles.findByPk(id, {transaction});
const updatePayload = {};
if (data.name !== undefined) updatePayload.name = data.name;
if (data.role_customization !== undefined) updatePayload.role_customization = data.role_customization;
updatePayload.updatedById = currentUser.id;
await roles.update(updatePayload, {transaction});
if (data.permissions !== undefined) {
await roles.setPermissions(data.permissions, { transaction });
}
return roles;
}
static async deleteByIds(ids, options) {
const currentUser = (options && options.currentUser) || { id: null };
const transaction = (options && options.transaction) || undefined;
const roles = await db.roles.findAll({
where: {
id: {
[Op.in]: ids,
},
},
transaction,
});
await db.sequelize.transaction(async (transaction) => {
for (const record of roles) {
await record.update(
{deletedBy: currentUser.id},
{transaction}
);
}
for (const record of roles) {
await record.destroy({transaction});
}
});
return roles;
}
static async remove(id, options) {
const currentUser = (options && options.currentUser) || {id: null};
const transaction = (options && options.transaction) || undefined;
const roles = await db.roles.findByPk(id, options);
await roles.update({
deletedBy: currentUser.id
}, {
transaction,
});
await roles.destroy({
transaction
});
return roles;
}
static async findBy(where, options) {
const transaction = (options && options.transaction) || undefined;
const roles = await db.roles.findOne({
where,
transaction,
});
if (!roles) {
return roles;
}
const output = roles.get({plain: true});
output.users_app_role = await roles.getUsers_app_role({
transaction
});
output.permissions = await roles.getPermissions({
transaction
});
return output;
}
static async findAll(
filter,
options
) {
const limit = filter.limit || 0;
let offset = 0;
let where = {};
const currentPage = +filter.page;
offset = currentPage * limit;
let include = [ let include = [
{ {
model: db.permissions, model: db.permissions,
as: 'permissions', as: 'permissions',
required: false, required: false,
}, },
]; ];
if (filter) { if (filter.id) {
if (filter.id) { where.id = Utils.uuid(filter.id);
where = { }
...where,
['id']: Utils.uuid(filter.id),
};
}
for (const field of this.SEARCHABLE_FIELDS) {
if (filter.name) { if (filter[field]) {
where = { where[Op.and] = Utils.ilike(this.TABLE_NAME, field, filter[field]);
...where,
[Op.and]: Utils.ilike(
'roles',
'name',
filter.name,
),
};
}
if (filter.role_customization) {
where = {
...where,
[Op.and]: Utils.ilike(
'roles',
'role_customization',
filter.role_customization,
),
};
}
if (filter.active !== undefined) {
where = {
...where,
active: filter.active === true || filter.active === 'true'
};
}
if (filter.permissions) {
const searchTerms = filter.permissions.split('|');
include = [
{
model: db.permissions,
as: 'permissions_filter',
required: searchTerms.length > 0,
where: searchTerms.length > 0 ? {
[Op.or]: [
{ id: { [Op.in]: searchTerms.map(term => Utils.uuid(term)) } },
{
name: {
[Op.or]: searchTerms.map(term => ({ [Op.iLike]: `%${term}%` }))
}
}
]
} : undefined
},
...include,
]
} }
if (filter.createdAtRange) {
const [start, end] = filter.createdAtRange;
if (start !== undefined && start !== null && start !== '') {
where = {
...where,
['createdAt']: {
...where.createdAt,
[Op.gte]: start,
},
};
}
if (end !== undefined && end !== null && end !== '') {
where = {
...where,
['createdAt']: {
...where.createdAt,
[Op.lte]: end,
},
};
}
}
}
const queryOptions = {
where,
include,
distinct: true,
order: filter.field && filter.sort
? [[filter.field, filter.sort]]
: [['createdAt', 'desc']],
transaction: options?.transaction,
logging: console.log
};
if (!options?.countOnly) {
queryOptions.limit = limit ? Number(limit) : undefined;
queryOptions.offset = offset ? Number(offset) : undefined;
}
try {
const { rows, count } = await db.roles.findAndCountAll(queryOptions);
return {
rows: options?.countOnly ? [] : rows,
count: count
};
} catch (error) {
console.error('Error executing query:', error);
throw error;
}
} }
static async findAllAutocomplete(query, limit, offset, ) { if (filter.active !== undefined) {
let where = {}; where.active = filter.active === true || filter.active === 'true';
if (query) {
where = {
[Op.or]: [
{ ['id']: Utils.uuid(query) },
Utils.ilike(
'roles',
'name',
query,
),
],
};
}
const records = await db.roles.findAll({
attributes: [ 'id', 'name' ],
where,
limit: limit ? Number(limit) : undefined,
offset: offset ? Number(offset) : undefined,
orderBy: [['name', 'ASC']],
});
return records.map((record) => ({
id: record.id,
label: record.name,
}));
} }
if (filter.permissions) {
const searchTerms = filter.permissions.split('|');
include = [
{
model: db.permissions,
as: 'permissions_filter',
required: searchTerms.length > 0,
where: searchTerms.length > 0 ? {
[Op.or]: [
{ id: { [Op.in]: searchTerms.map(term => Utils.uuid(term)) } },
{
name: {
[Op.or]: searchTerms.map(term => ({ [Op.iLike]: `%${term}%` }))
}
}
]
} : undefined
},
...include,
];
}
}; if (filter.createdAtRange) {
const [start, end] = filter.createdAtRange;
if (start !== undefined && start !== null && start !== '') {
where.createdAt = { ...where.createdAt, [Op.gte]: start };
}
if (end !== undefined && end !== null && end !== '') {
where.createdAt = { ...where.createdAt, [Op.lte]: end };
}
}
const queryOptions = {
where,
include,
distinct: true,
order: filter.field && filter.sort
? [[filter.field, filter.sort]]
: [['createdAt', 'desc']],
transaction: options.transaction,
};
if (!options.countOnly) {
queryOptions.limit = limit ? Number(limit) : undefined;
queryOptions.offset = offset ? Number(offset) : undefined;
}
try {
const { rows, count } = await this.MODEL.findAndCountAll(queryOptions);
return {
rows: options.countOnly ? [] : rows,
count,
};
} catch (error) {
console.error('Error executing query:', error);
throw error;
}
}
}
module.exports = RolesDBApi;

View File

@ -1,4 +1,4 @@
const GenericDBApi = require('./base.api');
const db = require('../models'); const db = require('../models');
const Utils = require('../utils'); const Utils = require('../utils');
const { const {
@ -6,382 +6,120 @@ const {
applyRuntimeProjectFilter, applyRuntimeProjectFilter,
} = require('./runtime-context'); } = require('./runtime-context');
const Sequelize = db.Sequelize; const Sequelize = db.Sequelize;
const Op = Sequelize.Op; const Op = Sequelize.Op;
module.exports = class Tour_pagesDBApi { class Tour_pagesDBApi extends GenericDBApi {
static get MODEL() {
return db.tour_pages;
}
static get TABLE_NAME() {
return 'tour_pages';
}
static get SEARCHABLE_FIELDS() {
return ['source_key', 'name', 'slug', 'background_image_url', 'background_video_url', 'background_audio_url', 'ui_schema_json'];
}
static async create(data, options) { static get RANGE_FIELDS() {
const currentUser = (options && options.currentUser) || { id: null }; return ['sort_order'];
const transaction = (options && options.transaction) || undefined; }
const projectId = data.project || data.projectId || null;
const tour_pages = await db.tour_pages.create( static get ENUM_FIELDS() {
{ return ['environment', 'background_loop', 'requires_auth'];
id: data.id || undefined, }
environment: data.environment static get CSV_FIELDS() {
|| return ['id', 'environment', 'source_key', 'name', 'slug', 'sort_order', 'createdAt'];
null }
,
source_key: data.source_key static get AUTOCOMPLETE_FIELD() {
|| return 'name';
null }
,
name: data.name static get ASSOCIATIONS() {
|| return [
null { field: 'project', setter: 'setProject', isArray: false },
, ];
}
slug: data.slug static getFieldMapping(data) {
|| return {
null id: data.id || undefined,
, environment: data.environment || null,
source_key: data.source_key || null,
name: data.name || null,
slug: data.slug || null,
sort_order: data.sort_order || null,
background_image_url: data.background_image_url || null,
background_video_url: data.background_video_url || null,
background_audio_url: data.background_audio_url || null,
background_loop: data.background_loop || false,
requires_auth: data.requires_auth || false,
ui_schema_json: data.ui_schema_json || null,
};
}
sort_order: data.sort_order static async create(data, options = {}) {
|| const currentUser = options.currentUser || { id: null };
null const transaction = options.transaction;
, const projectId = data.project || data.projectId || null;
background_image_url: data.background_image_url const record = await this.MODEL.create(
|| {
null ...this.getFieldMapping(data),
, projectId,
importHash: data.importHash || null,
background_video_url: data.background_video_url createdById: currentUser.id,
|| updatedById: currentUser.id,
null },
, { transaction }
background_audio_url: data.background_audio_url
||
null
,
background_loop: data.background_loop
||
false
,
requires_auth: data.requires_auth
||
false
,
ui_schema_json: data.ui_schema_json
||
null
,
projectId,
importHash: data.importHash || null,
createdById: currentUser.id,
updatedById: currentUser.id,
},
{ transaction },
); );
await record.setProject(projectId, { transaction });
await tour_pages.setProject(projectId, { return record;
transaction, }
});
return tour_pages;
}
static async bulkImport(data, options) {
const currentUser = (options && options.currentUser) || { id: null };
const transaction = (options && options.transaction) || undefined;
// Prepare data - wrapping individual data transformations in a map() method
const tour_pagesData = data.map((item, index) => ({
id: item.id || undefined,
environment: item.environment
||
null
,
source_key: item.source_key
||
null
,
name: item.name
||
null
,
slug: item.slug
||
null
,
sort_order: item.sort_order
||
null
,
background_image_url: item.background_image_url
||
null
,
background_video_url: item.background_video_url
||
null
,
background_audio_url: item.background_audio_url
||
null
,
background_loop: item.background_loop
||
false
,
requires_auth: item.requires_auth
||
false
,
ui_schema_json: item.ui_schema_json
||
null
,
importHash: item.importHash || null,
createdById: currentUser.id,
updatedById: currentUser.id,
createdAt: new Date(Date.now() + index * 1000),
}));
// Bulk create items
const tour_pages = await db.tour_pages.bulkCreate(tour_pagesData, { transaction });
// For each item created, replace relation files
return tour_pages;
}
static async update(id, data, options) {
const currentUser = (options && options.currentUser) || {id: null};
const transaction = (options && options.transaction) || undefined;
const tour_pages = await db.tour_pages.findByPk(id, {transaction});
const updatePayload = {};
if (data.environment !== undefined) updatePayload.environment = data.environment;
if (data.source_key !== undefined) updatePayload.source_key = data.source_key;
if (data.name !== undefined) updatePayload.name = data.name;
if (data.slug !== undefined) updatePayload.slug = data.slug;
if (data.sort_order !== undefined) updatePayload.sort_order = data.sort_order;
if (data.background_image_url !== undefined) updatePayload.background_image_url = data.background_image_url;
if (data.background_video_url !== undefined) updatePayload.background_video_url = data.background_video_url;
if (data.background_audio_url !== undefined) updatePayload.background_audio_url = data.background_audio_url;
if (data.background_loop !== undefined) updatePayload.background_loop = data.background_loop;
if (data.requires_auth !== undefined) updatePayload.requires_auth = data.requires_auth;
if (data.ui_schema_json !== undefined) updatePayload.ui_schema_json = data.ui_schema_json;
updatePayload.updatedById = currentUser.id;
await tour_pages.update(updatePayload, {transaction});
if (data.project !== undefined) {
await tour_pages.setProject(
data.project,
{ transaction }
);
}
return tour_pages;
}
static async deleteByIds(ids, options) {
const currentUser = (options && options.currentUser) || { id: null };
const transaction = (options && options.transaction) || undefined;
const tour_pages = await db.tour_pages.findAll({
where: {
id: {
[Op.in]: ids,
},
},
transaction,
});
await db.sequelize.transaction(async (transaction) => {
for (const record of tour_pages) {
await record.update(
{deletedBy: currentUser.id},
{transaction}
);
}
for (const record of tour_pages) {
await record.destroy({transaction});
}
});
return tour_pages;
}
static async remove(id, options) {
const currentUser = (options && options.currentUser) || {id: null};
const transaction = (options && options.transaction) || undefined;
const tour_pages = await db.tour_pages.findByPk(id, options);
await tour_pages.update({
deletedBy: currentUser.id
}, {
transaction,
});
await tour_pages.destroy({
transaction
});
return tour_pages;
}
static async findBy(where, options) {
const transaction = (options && options.transaction) || undefined;
const queryWhere = applyRuntimeEnvironment({ ...where }, options);
const projectInclude = applyRuntimeProjectFilter(
{
model: db.projects,
as: 'project',
},
options,
);
const tour_pages = await db.tour_pages.findOne(
{ where: queryWhere, include: [projectInclude], transaction },
);
if (!tour_pages) {
return tour_pages;
}
const output = tour_pages.get({plain: true});
output.page_elements_page = await tour_pages.getPage_elements_page({
transaction
});
output.page_links_from_page = await tour_pages.getPage_links_from_page({
transaction
});
output.page_links_to_page = await tour_pages.getPage_links_to_page({
transaction
});
output.project = await tour_pages.getProject({
transaction
});
return output;
}
static async findAll(
filter,
options
) {
filter = filter || {};
const limit = filter.limit || 0;
let offset = 0;
let where = {};
const currentPage = +filter.page;
offset = currentPage * limit;
let include = [
static async findBy(where, options = {}) {
const transaction = options.transaction;
const queryWhere = applyRuntimeEnvironment({ ...where }, options);
const projectInclude = applyRuntimeProjectFilter(
{ {
model: db.projects, model: db.projects,
as: 'project', as: 'project',
},
options,
);
const record = await this.MODEL.findOne({
where: queryWhere,
transaction,
include: [
projectInclude,
{ association: 'page_elements_page' },
{ association: 'page_links_from_page' },
{ association: 'page_links_to_page' },
],
});
if (!record) return null;
return record.get({ plain: true });
}
static async findAll(filter = {}, options = {}) {
filter = filter || {};
const limit = filter.limit || 0;
const currentPage = +filter.page || 0;
const offset = currentPage * limit;
let where = {};
let include = [
{
model: db.projects,
as: 'project',
where: filter.project ? { where: filter.project ? {
[Op.or]: [ [Op.or]: [
{ id: { [Op.in]: filter.project.split('|').map(term => Utils.uuid(term)) } }, { id: { [Op.in]: filter.project.split('|').map(term => Utils.uuid(term)) } },
@ -392,254 +130,82 @@ module.exports = class Tour_pagesDBApi {
}, },
] ]
} : {}, } : {},
}, },
]; ];
include[0] = applyRuntimeProjectFilter(include[0], options);
if (filter) { include[0] = applyRuntimeProjectFilter(include[0], options);
if (filter.id) {
where = {
...where,
['id']: Utils.uuid(filter.id),
};
}
if (filter.id) {
if (filter.source_key) { where.id = Utils.uuid(filter.id);
where = {
...where,
[Op.and]: Utils.ilike(
'tour_pages',
'source_key',
filter.source_key,
),
};
}
if (filter.name) {
where = {
...where,
[Op.and]: Utils.ilike(
'tour_pages',
'name',
filter.name,
),
};
}
if (filter.slug) {
where = {
...where,
[Op.and]: Utils.ilike(
'tour_pages',
'slug',
filter.slug,
),
};
}
if (filter.background_image_url) {
where = {
...where,
[Op.and]: Utils.ilike(
'tour_pages',
'background_image_url',
filter.background_image_url,
),
};
}
if (filter.background_video_url) {
where = {
...where,
[Op.and]: Utils.ilike(
'tour_pages',
'background_video_url',
filter.background_video_url,
),
};
}
if (filter.background_audio_url) {
where = {
...where,
[Op.and]: Utils.ilike(
'tour_pages',
'background_audio_url',
filter.background_audio_url,
),
};
}
if (filter.ui_schema_json) {
where = {
...where,
[Op.and]: Utils.ilike(
'tour_pages',
'ui_schema_json',
filter.ui_schema_json,
),
};
}
if (filter.sort_orderRange) {
const [start, end] = filter.sort_orderRange;
if (start !== undefined && start !== null && start !== '') {
where = {
...where,
sort_order: {
...where.sort_order,
[Op.gte]: start,
},
};
}
if (end !== undefined && end !== null && end !== '') {
where = {
...where,
sort_order: {
...where.sort_order,
[Op.lte]: end,
},
};
}
}
if (filter.active !== undefined) {
where = {
...where,
active: filter.active === true || filter.active === 'true'
};
}
if (filter.environment) {
where = {
...where,
environment: filter.environment,
};
}
if (filter.background_loop) {
where = {
...where,
background_loop: filter.background_loop,
};
}
if (filter.requires_auth) {
where = {
...where,
requires_auth: filter.requires_auth,
};
}
if (filter.createdAtRange) {
const [start, end] = filter.createdAtRange;
if (start !== undefined && start !== null && start !== '') {
where = {
...where,
['createdAt']: {
...where.createdAt,
[Op.gte]: start,
},
};
}
if (end !== undefined && end !== null && end !== '') {
where = {
...where,
['createdAt']: {
...where.createdAt,
[Op.lte]: end,
},
};
}
}
}
where = applyRuntimeEnvironment(where, options);
const queryOptions = {
where,
include,
distinct: true,
order: filter.field && filter.sort
? [[filter.field, filter.sort]]
: [['createdAt', 'desc']],
transaction: options?.transaction,
logging: console.log
};
if (!options?.countOnly) {
queryOptions.limit = limit ? Number(limit) : undefined;
queryOptions.offset = offset ? Number(offset) : undefined;
}
try {
const { rows, count } = await db.tour_pages.findAndCountAll(queryOptions);
return {
rows: options?.countOnly ? [] : rows,
count: count
};
} catch (error) {
console.error('Error executing query:', error);
throw error;
}
} }
static async findAllAutocomplete(query, limit, offset, ) { for (const field of this.SEARCHABLE_FIELDS) {
let where = {}; if (filter[field]) {
where[Op.and] = Utils.ilike(this.TABLE_NAME, field, filter[field]);
}
if (query) {
where = {
[Op.or]: [
{ ['id']: Utils.uuid(query) },
Utils.ilike(
'tour_pages',
'name',
query,
),
],
};
}
const records = await db.tour_pages.findAll({
attributes: [ 'id', 'name' ],
where,
limit: limit ? Number(limit) : undefined,
offset: offset ? Number(offset) : undefined,
orderBy: [['name', 'ASC']],
});
return records.map((record) => ({
id: record.id,
label: record.name,
}));
} }
for (const field of this.RANGE_FIELDS) {
const rangeKey = `${field}Range`;
if (filter[rangeKey]) {
const [start, end] = filter[rangeKey];
if (start !== undefined && start !== null && start !== '') {
where[field] = { ...where[field], [Op.gte]: start };
}
if (end !== undefined && end !== null && end !== '') {
where[field] = { ...where[field], [Op.lte]: end };
}
}
}
}; for (const field of this.ENUM_FIELDS) {
if (filter[field] !== undefined) {
where[field] = filter[field];
}
}
if (filter.active !== undefined) {
where.active = filter.active === true || filter.active === 'true';
}
if (filter.createdAtRange) {
const [start, end] = filter.createdAtRange;
if (start !== undefined && start !== null && start !== '') {
where.createdAt = { ...where.createdAt, [Op.gte]: start };
}
if (end !== undefined && end !== null && end !== '') {
where.createdAt = { ...where.createdAt, [Op.lte]: end };
}
}
where = applyRuntimeEnvironment(where, options);
const queryOptions = {
where,
include,
distinct: true,
order: filter.field && filter.sort
? [[filter.field, filter.sort]]
: [['createdAt', 'desc']],
transaction: options.transaction,
};
if (!options.countOnly) {
queryOptions.limit = limit ? Number(limit) : undefined;
queryOptions.offset = offset ? Number(offset) : undefined;
}
try {
const { rows, count } = await this.MODEL.findAndCountAll(queryOptions);
return {
rows: options.countOnly ? [] : rows,
count,
};
} catch (error) {
console.error('Error executing query:', error);
throw error;
}
}
}
module.exports = Tour_pagesDBApi;

View File

@ -1,4 +1,4 @@
const GenericDBApi = require('./base.api');
const db = require('../models'); const db = require('../models');
const Utils = require('../utils'); const Utils = require('../utils');
const { const {
@ -6,331 +6,91 @@ const {
applyRuntimeProjectFilter, applyRuntimeProjectFilter,
} = require('./runtime-context'); } = require('./runtime-context');
const Sequelize = db.Sequelize; const Sequelize = db.Sequelize;
const Op = Sequelize.Op; const Op = Sequelize.Op;
module.exports = class TransitionsDBApi { class TransitionsDBApi extends GenericDBApi {
static get MODEL() {
return db.transitions;
}
static get TABLE_NAME() {
return 'transitions';
}
static get SEARCHABLE_FIELDS() {
return ['source_key', 'name', 'slug', 'video_url', 'audio_url'];
}
static async create(data, options) { static get RANGE_FIELDS() {
const currentUser = (options && options.currentUser) || { id: null }; return ['duration_sec'];
const transaction = (options && options.transaction) || undefined; }
const transitions = await db.transitions.create( static get ENUM_FIELDS() {
{ return ['environment', 'supports_reverse'];
id: data.id || undefined, }
environment: data.environment static get CSV_FIELDS() {
|| return ['id', 'source_key', 'name', 'slug', 'video_url', 'audio_url', 'duration_sec', 'createdAt'];
null }
,
source_key: data.source_key static get AUTOCOMPLETE_FIELD() {
|| return 'name';
null }
,
name: data.name static get ASSOCIATIONS() {
|| return [
null { field: 'project', setter: 'setProject', isArray: false },
, ];
}
slug: data.slug static getFieldMapping(data) {
|| return {
null id: data.id || undefined,
, environment: data.environment || null,
source_key: data.source_key || null,
name: data.name || null,
slug: data.slug || null,
video_url: data.video_url || null,
audio_url: data.audio_url || null,
supports_reverse: data.supports_reverse || false,
duration_sec: data.duration_sec || null,
};
}
video_url: data.video_url static async findBy(where, options = {}) {
|| const transaction = options.transaction;
null const queryWhere = applyRuntimeEnvironment({ ...where }, options);
, const projectInclude = applyRuntimeProjectFilter(
{ model: db.projects, as: 'project' },
audio_url: data.audio_url options
||
null
,
supports_reverse: data.supports_reverse
||
false
,
duration_sec: data.duration_sec
||
null
,
importHash: data.importHash || null,
createdById: currentUser.id,
updatedById: currentUser.id,
},
{ transaction },
); );
const record = await this.MODEL.findOne({
await transitions.setProject( data.project || null, { where: queryWhere,
transaction, transaction,
}); include: [
projectInclude,
{ association: 'page_links_transition' },
],
});
if (!record) return null;
return transitions; return record.get({ plain: true });
} }
static async findAll(filter = {}, options = {}) {
static async bulkImport(data, options) { filter = filter || {};
const currentUser = (options && options.currentUser) || { id: null }; const limit = filter.limit || 0;
const transaction = (options && options.transaction) || undefined; const currentPage = +filter.page || 0;
const offset = currentPage * limit;
// Prepare data - wrapping individual data transformations in a map() method
const transitionsData = data.map((item, index) => ({ let where = {};
id: item.id || undefined,
environment: item.environment
||
null
,
source_key: item.source_key
||
null
,
name: item.name
||
null
,
slug: item.slug
||
null
,
video_url: item.video_url
||
null
,
audio_url: item.audio_url
||
null
,
supports_reverse: item.supports_reverse
||
false
,
duration_sec: item.duration_sec
||
null
,
importHash: item.importHash || null,
createdById: currentUser.id,
updatedById: currentUser.id,
createdAt: new Date(Date.now() + index * 1000),
}));
// Bulk create items
const transitions = await db.transitions.bulkCreate(transitionsData, { transaction });
// For each item created, replace relation files
return transitions;
}
static async update(id, data, options) {
const currentUser = (options && options.currentUser) || {id: null};
const transaction = (options && options.transaction) || undefined;
const transitions = await db.transitions.findByPk(id, {transaction});
const updatePayload = {};
if (data.environment !== undefined) updatePayload.environment = data.environment;
if (data.source_key !== undefined) updatePayload.source_key = data.source_key;
if (data.name !== undefined) updatePayload.name = data.name;
if (data.slug !== undefined) updatePayload.slug = data.slug;
if (data.video_url !== undefined) updatePayload.video_url = data.video_url;
if (data.audio_url !== undefined) updatePayload.audio_url = data.audio_url;
if (data.supports_reverse !== undefined) updatePayload.supports_reverse = data.supports_reverse;
if (data.duration_sec !== undefined) updatePayload.duration_sec = data.duration_sec;
updatePayload.updatedById = currentUser.id;
await transitions.update(updatePayload, {transaction});
if (data.project !== undefined) {
await transitions.setProject(
data.project,
{ transaction }
);
}
return transitions;
}
static async deleteByIds(ids, options) {
const currentUser = (options && options.currentUser) || { id: null };
const transaction = (options && options.transaction) || undefined;
const transitions = await db.transitions.findAll({
where: {
id: {
[Op.in]: ids,
},
},
transaction,
});
await db.sequelize.transaction(async (transaction) => {
for (const record of transitions) {
await record.update(
{deletedBy: currentUser.id},
{transaction}
);
}
for (const record of transitions) {
await record.destroy({transaction});
}
});
return transitions;
}
static async remove(id, options) {
const currentUser = (options && options.currentUser) || {id: null};
const transaction = (options && options.transaction) || undefined;
const transitions = await db.transitions.findByPk(id, options);
await transitions.update({
deletedBy: currentUser.id
}, {
transaction,
});
await transitions.destroy({
transaction
});
return transitions;
}
static async findBy(where, options) {
const transaction = (options && options.transaction) || undefined;
const queryWhere = applyRuntimeEnvironment({ ...where }, options);
const projectInclude = applyRuntimeProjectFilter(
{
model: db.projects,
as: 'project',
},
options,
);
const transitions = await db.transitions.findOne(
{ where: queryWhere, include: [projectInclude], transaction },
);
if (!transitions) {
return transitions;
}
const output = transitions.get({plain: true});
output.page_links_transition = await transitions.getPage_links_transition({
transaction
});
output.project = await transitions.getProject({
transaction
});
return output;
}
static async findAll(
filter,
options
) {
filter = filter || {};
const limit = filter.limit || 0;
let offset = 0;
let where = {};
const currentPage = +filter.page;
offset = currentPage * limit;
let include = [ let include = [
{ {
model: db.projects, model: db.projects,
as: 'project', as: 'project',
where: filter.project ? { where: filter.project ? {
[Op.or]: [ [Op.or]: [
{ id: { [Op.in]: filter.project.split('|').map(term => Utils.uuid(term)) } }, { id: { [Op.in]: filter.project.split('|').map(term => Utils.uuid(term)) } },
@ -341,225 +101,82 @@ module.exports = class TransitionsDBApi {
}, },
] ]
} : {}, } : {},
}, },
]; ];
include[0] = applyRuntimeProjectFilter(include[0], options);
if (filter) { include[0] = applyRuntimeProjectFilter(include[0], options);
if (filter.id) {
where = {
...where,
['id']: Utils.uuid(filter.id),
};
}
if (filter.id) {
if (filter.source_key) { where.id = Utils.uuid(filter.id);
where = {
...where,
[Op.and]: Utils.ilike(
'transitions',
'source_key',
filter.source_key,
),
};
}
if (filter.name) {
where = {
...where,
[Op.and]: Utils.ilike(
'transitions',
'name',
filter.name,
),
};
}
if (filter.slug) {
where = {
...where,
[Op.and]: Utils.ilike(
'transitions',
'slug',
filter.slug,
),
};
}
if (filter.video_url) {
where = {
...where,
[Op.and]: Utils.ilike(
'transitions',
'video_url',
filter.video_url,
),
};
}
if (filter.audio_url) {
where = {
...where,
[Op.and]: Utils.ilike(
'transitions',
'audio_url',
filter.audio_url,
),
};
}
if (filter.duration_secRange) {
const [start, end] = filter.duration_secRange;
if (start !== undefined && start !== null && start !== '') {
where = {
...where,
duration_sec: {
...where.duration_sec,
[Op.gte]: start,
},
};
}
if (end !== undefined && end !== null && end !== '') {
where = {
...where,
duration_sec: {
...where.duration_sec,
[Op.lte]: end,
},
};
}
}
if (filter.active !== undefined) {
where = {
...where,
active: filter.active === true || filter.active === 'true'
};
}
if (filter.environment) {
where = {
...where,
environment: filter.environment,
};
}
if (filter.supports_reverse) {
where = {
...where,
supports_reverse: filter.supports_reverse,
};
}
if (filter.createdAtRange) {
const [start, end] = filter.createdAtRange;
if (start !== undefined && start !== null && start !== '') {
where = {
...where,
['createdAt']: {
...where.createdAt,
[Op.gte]: start,
},
};
}
if (end !== undefined && end !== null && end !== '') {
where = {
...where,
['createdAt']: {
...where.createdAt,
[Op.lte]: end,
},
};
}
}
}
where = applyRuntimeEnvironment(where, options);
const queryOptions = {
where,
include,
distinct: true,
order: filter.field && filter.sort
? [[filter.field, filter.sort]]
: [['createdAt', 'desc']],
transaction: options?.transaction,
logging: console.log
};
if (!options?.countOnly) {
queryOptions.limit = limit ? Number(limit) : undefined;
queryOptions.offset = offset ? Number(offset) : undefined;
}
try {
const { rows, count } = await db.transitions.findAndCountAll(queryOptions);
return {
rows: options?.countOnly ? [] : rows,
count: count
};
} catch (error) {
console.error('Error executing query:', error);
throw error;
}
} }
static async findAllAutocomplete(query, limit, offset, ) { for (const field of this.SEARCHABLE_FIELDS) {
let where = {}; if (filter[field]) {
where[Op.and] = Utils.ilike(this.TABLE_NAME, field, filter[field]);
}
if (query) {
where = {
[Op.or]: [
{ ['id']: Utils.uuid(query) },
Utils.ilike(
'transitions',
'name',
query,
),
],
};
}
const records = await db.transitions.findAll({
attributes: [ 'id', 'name' ],
where,
limit: limit ? Number(limit) : undefined,
offset: offset ? Number(offset) : undefined,
orderBy: [['name', 'ASC']],
});
return records.map((record) => ({
id: record.id,
label: record.name,
}));
} }
for (const field of this.RANGE_FIELDS) {
const rangeKey = `${field}Range`;
if (filter[rangeKey]) {
const [start, end] = filter[rangeKey];
if (start !== undefined && start !== null && start !== '') {
where[field] = { ...where[field], [Op.gte]: start };
}
if (end !== undefined && end !== null && end !== '') {
where[field] = { ...where[field], [Op.lte]: end };
}
}
}
}; for (const field of this.ENUM_FIELDS) {
if (filter[field] !== undefined) {
where[field] = filter[field];
}
}
if (filter.active !== undefined) {
where.active = filter.active === true || filter.active === 'true';
}
if (filter.createdAtRange) {
const [start, end] = filter.createdAtRange;
if (start !== undefined && start !== null && start !== '') {
where.createdAt = { ...where.createdAt, [Op.gte]: start };
}
if (end !== undefined && end !== null && end !== '') {
where.createdAt = { ...where.createdAt, [Op.lte]: end };
}
}
where = applyRuntimeEnvironment(where, options);
const queryOptions = {
where,
include,
distinct: true,
order: filter.field && filter.sort
? [[filter.field, filter.sort]]
: [['createdAt', 'desc']],
transaction: options.transaction,
};
if (!options.countOnly) {
queryOptions.limit = limit ? Number(limit) : undefined;
queryOptions.offset = offset ? Number(offset) : undefined;
}
try {
const { rows, count } = await this.MODEL.findAndCountAll(queryOptions);
return {
rows: options.countOnly ? [] : rows,
count,
};
} catch (error) {
console.error('Error executing query:', error);
throw error;
}
}
}
module.exports = TransitionsDBApi;

View File

@ -1,233 +0,0 @@
const db = require('../models');
const Utils = require('../utils');
const Sequelize = db.Sequelize;
const Op = Sequelize.Op;
module.exports = class Ui_elementsDBApi {
static async create(data, options) {
const currentUser = (options && options.currentUser) || { id: null };
const transaction = (options && options.transaction) || undefined;
const ui_elements = await db.ui_elements.create(
{
id: data.id || undefined,
element_type: data.element_type ?? null,
name: data.name ?? null,
settings_json: data.settings_json ?? null,
sort_order: data.sort_order ?? 0,
importHash: data.importHash || null,
createdById: currentUser.id,
updatedById: currentUser.id,
},
{ transaction },
);
return ui_elements;
}
static async bulkImport(data, options) {
const currentUser = (options && options.currentUser) || { id: null };
const transaction = (options && options.transaction) || undefined;
const uiElementsData = data.map((item, index) => ({
id: item.id || undefined,
element_type: item.element_type ?? null,
name: item.name ?? null,
settings_json: item.settings_json ?? null,
sort_order: item.sort_order ?? 0,
importHash: item.importHash || null,
createdById: currentUser.id,
updatedById: currentUser.id,
createdAt: new Date(Date.now() + index * 1000),
}));
const ui_elements = await db.ui_elements.bulkCreate(uiElementsData, { transaction });
return ui_elements;
}
static async update(id, data, options) {
const currentUser = (options && options.currentUser) || { id: null };
const transaction = (options && options.transaction) || undefined;
const ui_elements = await db.ui_elements.findByPk(id, { transaction });
const updatePayload = {};
if (data.element_type !== undefined) updatePayload.element_type = data.element_type;
if (data.name !== undefined) updatePayload.name = data.name;
if (data.settings_json !== undefined) updatePayload.settings_json = data.settings_json;
if (data.sort_order !== undefined) updatePayload.sort_order = data.sort_order;
updatePayload.updatedById = currentUser.id;
await ui_elements.update(updatePayload, { transaction });
return ui_elements;
}
static async deleteByIds(ids, options) {
const currentUser = (options && options.currentUser) || { id: null };
const transaction = (options && options.transaction) || undefined;
const ui_elements = await db.ui_elements.findAll({
where: {
id: {
[Op.in]: ids,
},
},
transaction,
});
await db.sequelize.transaction(async (innerTransaction) => {
for (const record of ui_elements) {
await record.update({ deletedBy: currentUser.id }, { transaction: innerTransaction });
}
for (const record of ui_elements) {
await record.destroy({ transaction: innerTransaction });
}
});
return ui_elements;
}
static async remove(id, options) {
const currentUser = (options && options.currentUser) || { id: null };
const transaction = (options && options.transaction) || undefined;
const ui_elements = await db.ui_elements.findByPk(id, options);
await ui_elements.update(
{
deletedBy: currentUser.id,
},
{
transaction,
},
);
await ui_elements.destroy({
transaction,
});
return ui_elements;
}
static async findBy(where, options) {
const transaction = (options && options.transaction) || undefined;
const ui_elements = await db.ui_elements.findOne({ where, transaction });
if (!ui_elements) {
return ui_elements;
}
return ui_elements.get({ plain: true });
}
static async findAll(filter, options) {
filter = filter || {};
const limit = Number(filter.limit) || 0;
const currentPage = Number(filter.page) || 0;
const offset = limit ? currentPage * limit : undefined;
let where = {};
if (filter.id) {
where = {
...where,
id: Utils.uuid(filter.id),
};
}
if (filter.name) {
where = {
...where,
[Op.and]: Utils.ilike('ui_elements', 'name', filter.name),
};
}
if (filter.element_type) {
where = {
...where,
element_type: filter.element_type,
};
}
if (filter.sort_orderRange) {
const [start, end] = filter.sort_orderRange;
if (start !== undefined && start !== null && start !== '') {
where = {
...where,
sort_order: {
...where.sort_order,
[Op.gte]: start,
},
};
}
if (end !== undefined && end !== null && end !== '') {
where = {
...where,
sort_order: {
...where.sort_order,
[Op.lte]: end,
},
};
}
}
let { orderBy = null } = options || {};
if (!orderBy) {
const sort = filter.sort || 'desc';
const field = filter.field || 'createdAt';
orderBy = [[field, sort]];
}
const { rows, count } = await db.ui_elements.findAndCountAll({
where,
limit: limit || undefined,
offset,
order: orderBy,
});
return {
rows,
count,
};
}
static async findAllAutocomplete(query, limit) {
let where = {};
if (query) {
where = {
[Op.or]: [
{
id: {
[Op.eq]: Utils.uuid(query),
},
},
{
name: {
[Op.iLike]: `%${query}%`,
},
},
],
};
}
const records = await db.ui_elements.findAll({
attributes: ['id', 'name'],
where,
limit: Number(limit) || undefined,
order: [['name', 'ASC']],
});
return records.map((record) => ({
id: record.id,
label: record.name,
}));
}
};

View File

@ -391,6 +391,18 @@ module.exports = class UsersDBApi {
const users = await db.users.findOne({ const users = await db.users.findOne({
where, where,
transaction, transaction,
include: [
{ association: 'project_memberships_user' },
{ association: 'presigned_url_requests_user' },
{ association: 'publish_events_user' },
{ association: 'access_logs_user' },
{ association: 'avatar' },
{
association: 'app_role',
include: [{ association: 'permissions' }],
},
{ association: 'custom_permissions' },
],
}); });
if (!users) { if (!users) {
@ -399,62 +411,11 @@ module.exports = class UsersDBApi {
const output = users.get({plain: true}); const output = users.get({plain: true});
// Map nested permissions from app_role for backward compatibility
output.project_memberships_user = await users.getProject_memberships_user({
transaction
});
output.presigned_url_requests_user = await users.getPresigned_url_requests_user({
transaction
});
output.publish_events_user = await users.getPublish_events_user({
transaction
});
output.access_logs_user = await users.getAccess_logs_user({
transaction
});
output.avatar = await users.getAvatar({
transaction
});
output.app_role = await users.getApp_role({
transaction
});
if (output.app_role) { if (output.app_role) {
output.app_role_permissions = await output.app_role.getPermissions({ output.app_role_permissions = output.app_role.permissions || [];
transaction,
});
} }
output.custom_permissions = await users.getCustom_permissions({
transaction
});
return output; return output;
} }
@ -744,8 +705,7 @@ module.exports = class UsersDBApi {
order: filter.field && filter.sort order: filter.field && filter.sort
? [[filter.field, filter.sort]] ? [[filter.field, filter.sort]]
: [['createdAt', 'desc']], : [['createdAt', 'desc']],
transaction: options?.transaction, transaction: options?.transaction
logging: console.log
}; };
if (!options?.countOnly) { if (!options?.countOnly) {
@ -921,7 +881,9 @@ module.exports = class UsersDBApi {
const token = crypto const token = crypto
.randomBytes(20) .randomBytes(20)
.toString('hex'); .toString('hex');
const tokenExpiresAt = Date.now() + 360000; // Token expires in 24 hours (was 6 minutes - too short for email verification flows)
const TOKEN_EXPIRY_MS = 24 * 60 * 60 * 1000; // 24 hours
const tokenExpiresAt = Date.now() + TOKEN_EXPIRY_MS;
if(users){ if(users){
await users.update( await users.update(

View File

@ -8,7 +8,7 @@ module.exports = {
database: process.env.DB_NAME, database: process.env.DB_NAME,
host: process.env.DB_HOST, host: process.env.DB_HOST,
port: process.env.DB_PORT, port: process.env.DB_PORT,
logging: console.log, logging: false,
seederStorage: 'sequelize', seederStorage: 'sequelize',
}, },
development: { development: {
@ -20,14 +20,14 @@ module.exports = {
logging: console.log, logging: console.log,
seederStorage: 'sequelize', seederStorage: 'sequelize',
}, },
dev_stage: { dev_stage: {
dialect: 'postgres', dialect: 'postgres',
username: process.env.DB_USER, username: process.env.DB_USER,
password: process.env.DB_PASS, password: process.env.DB_PASS,
database: process.env.DB_NAME, database: process.env.DB_NAME,
host: process.env.DB_HOST, host: process.env.DB_HOST,
port: process.env.DB_PORT, port: process.env.DB_PORT,
logging: console.log, logging: console.log,
seederStorage: 'sequelize', seederStorage: 'sequelize',
} }
}; };

File diff suppressed because it is too large Load Diff

View File

@ -1,124 +0,0 @@
module.exports = {
async up(queryInterface, Sequelize) {
const transaction = await queryInterface.sequelize.transaction();
try {
const rows = await queryInterface.sequelize.query(
"SELECT to_regclass('public.files') AS regclass_name;",
{
transaction,
type: Sequelize.QueryTypes.SELECT,
},
);
const tableName = rows[0].regclass_name;
if (tableName) {
await transaction.commit();
return;
}
await queryInterface.createTable(
'files',
{
id: {
type: Sequelize.DataTypes.UUID,
defaultValue: Sequelize.DataTypes.UUIDV4,
primaryKey: true,
},
belongsTo: {
type: Sequelize.DataTypes.STRING(255),
allowNull: true,
},
belongsToId: {
type: Sequelize.DataTypes.UUID,
allowNull: true,
},
belongsToColumn: {
type: Sequelize.DataTypes.STRING(255),
allowNull: true,
},
name: {
type: Sequelize.DataTypes.STRING(2083),
allowNull: false,
},
sizeInBytes: {
type: Sequelize.DataTypes.INTEGER,
allowNull: true,
},
privateUrl: {
type: Sequelize.DataTypes.STRING(2083),
allowNull: true,
},
publicUrl: {
type: Sequelize.DataTypes.STRING(2083),
allowNull: false,
},
createdAt: {
type: Sequelize.DataTypes.DATE,
allowNull: false,
},
updatedAt: {
type: Sequelize.DataTypes.DATE,
allowNull: false,
},
deletedAt: {
type: Sequelize.DataTypes.DATE,
allowNull: true,
},
createdById: {
type: Sequelize.DataTypes.UUID,
allowNull: true,
references: {
key: 'id',
model: 'users',
},
onDelete: 'SET NULL',
onUpdate: 'CASCADE',
},
updatedById: {
type: Sequelize.DataTypes.UUID,
allowNull: true,
references: {
key: 'id',
model: 'users',
},
onDelete: 'SET NULL',
onUpdate: 'CASCADE',
},
},
{ transaction },
);
await transaction.commit();
} catch (err) {
await transaction.rollback();
throw err;
}
},
async down(queryInterface, Sequelize) {
const transaction = await queryInterface.sequelize.transaction();
try {
const rows = await queryInterface.sequelize.query(
"SELECT to_regclass('public.files') AS regclass_name;",
{
transaction,
type: Sequelize.QueryTypes.SELECT,
},
);
const tableName = rows[0].regclass_name;
if (!tableName) {
await transaction.commit();
return;
}
await queryInterface.dropTable('files', { transaction });
await transaction.commit();
} catch (err) {
await transaction.rollback();
throw err;
}
},
};

View File

@ -1,95 +0,0 @@
module.exports = {
async up(queryInterface, Sequelize) {
const transaction = await queryInterface.sequelize.transaction();
try {
const rows = await queryInterface.sequelize.query(
"SELECT to_regclass('public.\"usersCustom_permissionsPermissions\"') AS regclass_name;",
{
transaction,
type: Sequelize.QueryTypes.SELECT,
},
);
const tableName = rows[0].regclass_name;
if (tableName) {
await transaction.commit();
return;
}
await queryInterface.createTable(
'usersCustom_permissionsPermissions',
{
createdAt: {
type: Sequelize.DataTypes.DATE,
allowNull: false,
},
updatedAt: {
type: Sequelize.DataTypes.DATE,
allowNull: false,
},
users_custom_permissionsId: {
type: Sequelize.DataTypes.UUID,
allowNull: false,
primaryKey: true,
references: {
model: 'users',
key: 'id',
},
onDelete: 'CASCADE',
onUpdate: 'CASCADE',
},
permissionId: {
type: Sequelize.DataTypes.UUID,
allowNull: false,
primaryKey: true,
references: {
model: 'permissions',
key: 'id',
},
onDelete: 'CASCADE',
onUpdate: 'CASCADE',
},
},
{ transaction },
);
await queryInterface.addIndex(
'usersCustom_permissionsPermissions',
['permissionId'],
{ transaction },
);
await transaction.commit();
} catch (err) {
await transaction.rollback();
throw err;
}
},
async down(queryInterface, Sequelize) {
const transaction = await queryInterface.sequelize.transaction();
try {
const rows = await queryInterface.sequelize.query(
"SELECT to_regclass('public.\"usersCustom_permissionsPermissions\"') AS regclass_name;",
{
transaction,
type: Sequelize.QueryTypes.SELECT,
},
);
const tableName = rows[0].regclass_name;
if (!tableName) {
await transaction.commit();
return;
}
await queryInterface.dropTable('usersCustom_permissionsPermissions', { transaction });
await transaction.commit();
} catch (err) {
await transaction.rollback();
throw err;
}
},
};

View File

@ -1,123 +0,0 @@
module.exports = {
async up(queryInterface, Sequelize) {
const transaction = await queryInterface.sequelize.transaction();
try {
const tableRows = await queryInterface.sequelize.query(
"SELECT to_regclass('public.page_elements') AS regclass_name;",
{
transaction,
type: Sequelize.QueryTypes.SELECT,
},
);
if (!tableRows[0]?.regclass_name) {
await transaction.commit();
return;
}
const nullableRows = await queryInterface.sequelize.query(
`SELECT is_nullable
FROM information_schema.columns
WHERE table_schema = 'public'
AND table_name = 'page_elements'
AND column_name = 'pageId';`,
{
transaction,
type: Sequelize.QueryTypes.SELECT,
},
);
if (!nullableRows.length || nullableRows[0].is_nullable === 'YES') {
await transaction.commit();
return;
}
await queryInterface.changeColumn(
'page_elements',
'pageId',
{
type: Sequelize.DataTypes.UUID,
allowNull: true,
references: {
model: 'tour_pages',
key: 'id',
},
},
{ transaction },
);
await transaction.commit();
} catch (err) {
await transaction.rollback();
throw err;
}
},
async down(queryInterface, Sequelize) {
const transaction = await queryInterface.sequelize.transaction();
try {
const tableRows = await queryInterface.sequelize.query(
"SELECT to_regclass('public.page_elements') AS regclass_name;",
{
transaction,
type: Sequelize.QueryTypes.SELECT,
},
);
if (!tableRows[0]?.regclass_name) {
await transaction.commit();
return;
}
const nullableRows = await queryInterface.sequelize.query(
`SELECT is_nullable
FROM information_schema.columns
WHERE table_schema = 'public'
AND table_name = 'page_elements'
AND column_name = 'pageId';`,
{
transaction,
type: Sequelize.QueryTypes.SELECT,
},
);
if (!nullableRows.length || nullableRows[0].is_nullable === 'NO') {
await transaction.commit();
return;
}
const nullCountRows = await queryInterface.sequelize.query(
'SELECT COUNT(*)::int AS count FROM "page_elements" WHERE "pageId" IS NULL;',
{
transaction,
type: Sequelize.QueryTypes.SELECT,
},
);
if (Number(nullCountRows[0]?.count || 0) > 0) {
throw new Error('Cannot make page_elements.pageId NOT NULL because NULL values exist.');
}
await queryInterface.changeColumn(
'page_elements',
'pageId',
{
type: Sequelize.DataTypes.UUID,
allowNull: false,
references: {
model: 'tour_pages',
key: 'id',
},
},
{ transaction },
);
await transaction.commit();
} catch (err) {
await transaction.rollback();
throw err;
}
},
};

View File

@ -1,105 +0,0 @@
module.exports = {
async up(queryInterface, Sequelize) {
const transaction = await queryInterface.sequelize.transaction();
try {
const tableRows = await queryInterface.sequelize.query(
"SELECT to_regclass('public.page_elements') AS regclass_name;",
{
transaction,
type: Sequelize.QueryTypes.SELECT,
},
);
if (!tableRows[0]?.regclass_name) {
await transaction.commit();
return;
}
const nullableRows = await queryInterface.sequelize.query(
`SELECT is_nullable
FROM information_schema.columns
WHERE table_schema = 'public'
AND table_name = 'page_elements'
AND column_name = 'pageId';`,
{
transaction,
type: Sequelize.QueryTypes.SELECT,
},
);
if (!nullableRows.length || nullableRows[0].is_nullable === 'YES') {
await transaction.commit();
return;
}
await queryInterface.sequelize.query(
'ALTER TABLE "page_elements" ALTER COLUMN "pageId" DROP NOT NULL;',
{ transaction },
);
await transaction.commit();
} catch (err) {
await transaction.rollback();
throw err;
}
},
async down(queryInterface, Sequelize) {
const transaction = await queryInterface.sequelize.transaction();
try {
const tableRows = await queryInterface.sequelize.query(
"SELECT to_regclass('public.page_elements') AS regclass_name;",
{
transaction,
type: Sequelize.QueryTypes.SELECT,
},
);
if (!tableRows[0]?.regclass_name) {
await transaction.commit();
return;
}
const nullableRows = await queryInterface.sequelize.query(
`SELECT is_nullable
FROM information_schema.columns
WHERE table_schema = 'public'
AND table_name = 'page_elements'
AND column_name = 'pageId';`,
{
transaction,
type: Sequelize.QueryTypes.SELECT,
},
);
if (!nullableRows.length || nullableRows[0].is_nullable === 'NO') {
await transaction.commit();
return;
}
const nullCountRows = await queryInterface.sequelize.query(
'SELECT COUNT(*)::int AS count FROM "page_elements" WHERE "pageId" IS NULL;',
{
transaction,
type: Sequelize.QueryTypes.SELECT,
},
);
if (Number(nullCountRows[0]?.count || 0) > 0) {
throw new Error('Cannot make page_elements.pageId NOT NULL because NULL values exist.');
}
await queryInterface.sequelize.query(
'ALTER TABLE "page_elements" ALTER COLUMN "pageId" SET NOT NULL;',
{ transaction },
);
await transaction.commit();
} catch (err) {
await transaction.rollback();
throw err;
}
},
};

View File

@ -1,108 +0,0 @@
module.exports = {
async up(queryInterface, Sequelize) {
const transaction = await queryInterface.sequelize.transaction();
try {
const tableRows = await queryInterface.sequelize.query(
"SELECT to_regclass('public.ui_elements') AS regclass_name;",
{
transaction,
type: Sequelize.QueryTypes.SELECT,
},
);
if (tableRows[0]?.regclass_name) {
await transaction.commit();
return;
}
await queryInterface.createTable(
'ui_elements',
{
id: {
type: Sequelize.DataTypes.UUID,
defaultValue: Sequelize.DataTypes.UUIDV4,
primaryKey: true,
},
element_type: {
type: Sequelize.DataTypes.TEXT,
allowNull: false,
},
name: {
type: Sequelize.DataTypes.TEXT,
allowNull: true,
},
settings_json: {
type: Sequelize.DataTypes.TEXT,
allowNull: true,
},
sort_order: {
type: Sequelize.DataTypes.INTEGER,
allowNull: false,
defaultValue: 0,
},
createdById: {
type: Sequelize.DataTypes.UUID,
allowNull: true,
references: {
model: 'users',
key: 'id',
},
},
updatedById: {
type: Sequelize.DataTypes.UUID,
allowNull: true,
references: {
model: 'users',
key: 'id',
},
},
createdAt: { type: Sequelize.DataTypes.DATE },
updatedAt: { type: Sequelize.DataTypes.DATE },
deletedAt: { type: Sequelize.DataTypes.DATE },
importHash: {
type: Sequelize.DataTypes.STRING(255),
allowNull: true,
unique: true,
},
},
{ transaction },
);
await queryInterface.addIndex('ui_elements', ['element_type'], { transaction });
await queryInterface.addIndex('ui_elements', ['sort_order'], { transaction });
await queryInterface.addIndex('ui_elements', ['deletedAt'], { transaction });
await transaction.commit();
} catch (error) {
await transaction.rollback();
throw error;
}
},
async down(queryInterface, Sequelize) {
const transaction = await queryInterface.sequelize.transaction();
try {
const tableRows = await queryInterface.sequelize.query(
"SELECT to_regclass('public.ui_elements') AS regclass_name;",
{
transaction,
type: Sequelize.QueryTypes.SELECT,
},
);
if (!tableRows[0]?.regclass_name) {
await transaction.commit();
return;
}
await queryInterface.dropTable('ui_elements', { transaction });
await transaction.commit();
} catch (error) {
await transaction.rollback();
throw error;
}
},
};

View File

@ -1,85 +0,0 @@
module.exports = {
async up(queryInterface, Sequelize) {
const transaction = await queryInterface.sequelize.transaction();
try {
const tableRows = await queryInterface.sequelize.query(
"SELECT to_regclass('public.publish_events') AS regclass_name;",
{
transaction,
type: Sequelize.QueryTypes.SELECT,
},
);
if (!tableRows[0]?.regclass_name) {
await transaction.commit();
return;
}
const tableDefinition = await queryInterface.describeTable('publish_events', { transaction });
if (!tableDefinition.title) {
await queryInterface.addColumn(
'publish_events',
'title',
{
type: Sequelize.DataTypes.STRING,
allowNull: true,
},
{ transaction },
);
}
if (!tableDefinition.description) {
await queryInterface.addColumn(
'publish_events',
'description',
{
type: Sequelize.DataTypes.TEXT,
allowNull: true,
},
{ transaction },
);
}
await transaction.commit();
} catch (error) {
await transaction.rollback();
throw error;
}
},
async down(queryInterface, Sequelize) {
const transaction = await queryInterface.sequelize.transaction();
try {
const tableRows = await queryInterface.sequelize.query(
"SELECT to_regclass('public.publish_events') AS regclass_name;",
{
transaction,
type: Sequelize.QueryTypes.SELECT,
},
);
if (!tableRows[0]?.regclass_name) {
await transaction.commit();
return;
}
const tableDefinition = await queryInterface.describeTable('publish_events', { transaction });
if (tableDefinition.description) {
await queryInterface.removeColumn('publish_events', 'description', { transaction });
}
if (tableDefinition.title) {
await queryInterface.removeColumn('publish_events', 'title', { transaction });
}
await transaction.commit();
} catch (error) {
await transaction.rollback();
throw error;
}
},
};

View File

@ -28,23 +28,23 @@ environment: {
path: { path: {
type: DataTypes.TEXT, type: DataTypes.TEXT,
validate: {
len: { args: [0, 2048], msg: 'Path must be at most 2048 characters' },
},
}, },
ip_address: { ip_address: {
type: DataTypes.TEXT, type: DataTypes.TEXT,
validate: {
len: { args: [0, 45], msg: 'IP address must be at most 45 characters' },
},
}, },
user_agent: { user_agent: {
type: DataTypes.TEXT, type: DataTypes.TEXT,
validate: {
len: { args: [0, 1024], msg: 'User agent must be at most 1024 characters' },
},
}, },
accessed_at: { accessed_at: {

View File

@ -38,30 +38,35 @@ variant_type: {
cdn_url: { cdn_url: {
type: DataTypes.TEXT, type: DataTypes.TEXT,
validate: {
len: { args: [0, 2048], msg: 'CDN URL must be at most 2048 characters' },
isUrlOrEmpty(value) {
if (value && value.length > 0 && !/^https?:\/\/.+/.test(value)) {
throw new Error('CDN URL must be a valid URL');
}
},
},
}, },
width_px: { width_px: {
type: DataTypes.INTEGER, type: DataTypes.INTEGER,
validate: {
min: { args: [0], msg: 'Width must be a non-negative integer' },
},
}, },
height_px: { height_px: {
type: DataTypes.INTEGER, type: DataTypes.INTEGER,
validate: {
min: { args: [0], msg: 'Height must be a non-negative integer' },
},
}, },
size_mb: { size_mb: {
type: DataTypes.DECIMAL, type: DataTypes.DECIMAL,
validate: {
min: { args: [0], msg: 'Size must be a non-negative number' },
},
}, },
importHash: { importHash: {

View File

@ -10,9 +10,9 @@ module.exports = function(sequelize, DataTypes) {
name: { name: {
type: DataTypes.TEXT, type: DataTypes.TEXT,
validate: {
len: { args: [0, 255], msg: 'Asset name must be at most 255 characters' },
},
}, },
asset_type: { asset_type: {
@ -36,6 +36,43 @@ asset_type: {
}, },
type: {
type: DataTypes.ENUM,
allowNull: false,
defaultValue: "general",
values: [
"icon",
"background_image",
"audio",
"video",
"transition",
"logo",
"favicon",
"document",
"general"
],
},
cdn_url: { cdn_url: {
type: DataTypes.TEXT, type: DataTypes.TEXT,
@ -52,9 +89,9 @@ storage_key: {
mime_type: { mime_type: {
type: DataTypes.TEXT, type: DataTypes.TEXT,
validate: {
is: { args: /^[a-z0-9]+\/[a-z0-9.+-]+$/i, msg: 'Invalid MIME type format' },
},
}, },
size_mb: { size_mb: {
@ -132,6 +169,7 @@ deleted_at_time: {
indexes: [ indexes: [
{ fields: ['projectId'] }, { fields: ['projectId'] },
{ fields: ['asset_type'] }, { fields: ['asset_type'] },
{ fields: ['type'] },
{ fields: ['is_public'] }, { fields: ['is_public'] },
{ fields: ['is_deleted'] }, { fields: ['is_deleted'] },
{ fields: ['deletedAt'] }, { fields: ['deletedAt'] },
@ -198,4 +236,3 @@ deleted_at_time: {
return assets; return assets;
}; };

View File

@ -46,9 +46,9 @@ element_type: {
name: { name: {
type: DataTypes.TEXT, type: DataTypes.TEXT,
validate: {
len: { args: [0, 255], msg: 'Element name must be at most 255 characters' },
},
}, },
sort_order: { sort_order: {
@ -104,17 +104,11 @@ rotation_deg: {
}, },
style_json: { style_json: {
type: DataTypes.TEXT, type: DataTypes.JSON,
}, },
content_json: { content_json: {
type: DataTypes.TEXT, type: DataTypes.JSON,
}, },
importHash: { importHash: {
@ -167,6 +161,7 @@ content_json: {
as: 'page', as: 'page',
foreignKey: { foreignKey: {
name: 'pageId', name: 'pageId',
allowNull: false,
}, },
constraints: false, constraints: false,
}); });

View File

@ -29,26 +29,27 @@ direction: {
external_url: { external_url: {
type: DataTypes.TEXT, type: DataTypes.TEXT,
validate: {
len: { args: [0, 2048], msg: 'External URL must be at most 2048 characters' },
isUrlOrEmpty(value) {
if (value && value.length > 0 && !/^https?:\/\/.+/.test(value)) {
throw new Error('External URL must be a valid URL');
}
},
},
}, },
is_active: { is_active: {
type: DataTypes.BOOLEAN, type: DataTypes.BOOLEAN,
allowNull: false, allowNull: false,
defaultValue: false, defaultValue: false,
}, },
trigger_selector: { trigger_selector: {
type: DataTypes.TEXT, type: DataTypes.TEXT,
validate: {
len: { args: [0, 1024], msg: 'Trigger selector must be at most 1024 characters' },
},
}, },
importHash: { importHash: {

View File

@ -10,9 +10,12 @@ module.exports = function(sequelize, DataTypes) {
name: { name: {
type: DataTypes.TEXT, type: DataTypes.TEXT,
allowNull: false,
unique: true,
validate: {
notEmpty: { msg: 'Permission name is required' },
len: { args: [1, 100], msg: 'Permission name must be between 1 and 100 characters' },
},
}, },
importHash: { importHash: {

View File

@ -48,23 +48,28 @@ asset_type: {
requested_key: { requested_key: {
type: DataTypes.TEXT, type: DataTypes.TEXT,
validate: {
len: { args: [0, 1024], msg: 'Requested key must be at most 1024 characters' },
},
}, },
mime_type: { mime_type: {
type: DataTypes.TEXT, type: DataTypes.TEXT,
validate: {
len: { args: [0, 255], msg: 'MIME type must be at most 255 characters' },
isMimeTypeOrEmpty(value) {
if (value && value.length > 0 && !/^[\w.-]+\/[\w.+-]+$/.test(value)) {
throw new Error('MIME type must be in format type/subtype');
}
},
},
}, },
requested_size_mb: { requested_size_mb: {
type: DataTypes.DECIMAL, type: DataTypes.DECIMAL,
validate: {
min: { args: [0], msg: 'Requested size must be a non-negative number' },
},
}, },
expires_at: { expires_at: {

View File

@ -36,9 +36,9 @@ source_key: {
name: { name: {
type: DataTypes.TEXT, type: DataTypes.TEXT,
validate: {
len: { args: [0, 255], msg: 'Audio track name must be at most 255 characters' },
},
}, },
slug: { slug: {
@ -67,9 +67,10 @@ loop: {
volume: { volume: {
type: DataTypes.DECIMAL, type: DataTypes.DECIMAL,
validate: {
min: { args: [0], msg: 'Volume must be at least 0' },
max: { args: [1], msg: 'Volume must be at most 1' },
},
}, },
sort_order: { sort_order: {

View File

@ -11,14 +11,21 @@ module.exports = function(sequelize, DataTypes) {
name: { name: {
type: DataTypes.TEXT, type: DataTypes.TEXT,
allowNull: false, allowNull: false,
validate: {
notEmpty: { msg: 'Project name is required' },
len: { args: [1, 255], msg: 'Project name must be between 1 and 255 characters' },
},
}, },
slug: { slug: {
type: DataTypes.TEXT, type: DataTypes.TEXT,
allowNull: false, allowNull: false,
unique: true, unique: true,
validate: {
notEmpty: { msg: 'Slug is required' },
is: { args: /^[a-z0-9_-]+$/i, msg: 'Slug can only contain letters, numbers, dashes, and underscores' },
len: { args: [1, 255], msg: 'Slug must be between 1 and 255 characters' },
},
}, },
description: { description: {
@ -69,17 +76,11 @@ og_image_url: {
}, },
theme_config_json: { theme_config_json: {
type: DataTypes.TEXT, type: DataTypes.JSON,
}, },
custom_css_json: { custom_css_json: {
type: DataTypes.TEXT, type: DataTypes.JSON,
}, },
cdn_base_url: { cdn_base_url: {

View File

@ -11,13 +11,17 @@ module.exports = function(sequelize, DataTypes) {
title: { title: {
type: DataTypes.STRING, type: DataTypes.STRING,
allowNull: true, allowNull: true,
validate: {
len: { args: [0, 255], msg: 'Title must be at most 255 characters' },
},
}, },
description: { description: {
type: DataTypes.TEXT, type: DataTypes.TEXT,
allowNull: true, allowNull: true,
validate: {
len: { args: [0, 5000], msg: 'Description must be at most 5000 characters' },
},
}, },
from_environment: { from_environment: {
@ -101,23 +105,23 @@ error_message: {
pages_copied: { pages_copied: {
type: DataTypes.INTEGER, type: DataTypes.INTEGER,
validate: {
min: { args: [0], msg: 'Pages copied must be a non-negative integer' },
},
}, },
transitions_copied: { transitions_copied: {
type: DataTypes.INTEGER, type: DataTypes.INTEGER,
validate: {
min: { args: [0], msg: 'Transitions copied must be a non-negative integer' },
},
}, },
audios_copied: { audios_copied: {
type: DataTypes.INTEGER, type: DataTypes.INTEGER,
validate: {
min: { args: [0], msg: 'Audios copied must be a non-negative integer' },
},
}, },
importHash: { importHash: {

View File

@ -15,6 +15,9 @@ environment: {
values: [ values: [
"dev",
"stage", "stage",
@ -26,23 +29,17 @@ environment: {
cache_version: { cache_version: {
type: DataTypes.TEXT, type: DataTypes.TEXT,
validate: {
len: { args: [0, 255], msg: 'Cache version must be at most 255 characters' },
},
}, },
manifest_json: { manifest_json: {
type: DataTypes.TEXT, type: DataTypes.JSON,
}, },
asset_list_json: { asset_list_json: {
type: DataTypes.TEXT, type: DataTypes.JSON,
}, },
generated_at: { generated_at: {

View File

@ -10,9 +10,11 @@ module.exports = function(sequelize, DataTypes) {
name: { name: {
type: DataTypes.TEXT, type: DataTypes.TEXT,
allowNull: false,
validate: {
notEmpty: { msg: 'Role name is required' },
len: { args: [1, 100], msg: 'Role name must be between 1 and 100 characters' },
},
}, },
role_customization: { role_customization: {

View File

@ -37,13 +37,20 @@ source_key: {
name: { name: {
type: DataTypes.TEXT, type: DataTypes.TEXT,
allowNull: false, allowNull: false,
validate: {
notEmpty: { msg: 'Page name is required' },
len: { args: [1, 255], msg: 'Page name must be between 1 and 255 characters' },
},
}, },
slug: { slug: {
type: DataTypes.TEXT, type: DataTypes.TEXT,
allowNull: false, allowNull: false,
validate: {
notEmpty: { msg: 'Slug is required' },
is: { args: /^[a-z0-9_-]+$/i, msg: 'Slug can only contain letters, numbers, dashes, and underscores' },
len: { args: [1, 255], msg: 'Slug must be between 1 and 255 characters' },
},
}, },
sort_order: { sort_order: {
@ -95,10 +102,7 @@ requires_auth: {
}, },
ui_schema_json: { ui_schema_json: {
type: DataTypes.TEXT, type: DataTypes.JSON,
}, },
importHash: { importHash: {

View File

@ -37,13 +37,20 @@ source_key: {
name: { name: {
type: DataTypes.TEXT, type: DataTypes.TEXT,
allowNull: false, allowNull: false,
validate: {
notEmpty: { msg: 'Transition name is required' },
len: { args: [1, 255], msg: 'Transition name must be between 1 and 255 characters' },
},
}, },
slug: { slug: {
type: DataTypes.TEXT, type: DataTypes.TEXT,
allowNull: false, allowNull: false,
validate: {
notEmpty: { msg: 'Slug is required' },
is: { args: /^[a-z0-9_-]+$/i, msg: 'Slug can only contain letters, numbers, dashes, and underscores' },
len: { args: [1, 255], msg: 'Slug must be between 1 and 255 characters' },
},
}, },
video_url: { video_url: {

View File

@ -1,50 +0,0 @@
module.exports = function (sequelize, DataTypes) {
const ui_elements = sequelize.define(
'ui_elements',
{
id: {
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV4,
primaryKey: true,
},
element_type: {
type: DataTypes.TEXT,
allowNull: false,
},
name: {
type: DataTypes.TEXT,
},
settings_json: {
type: DataTypes.TEXT,
},
sort_order: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: 0,
},
importHash: {
type: DataTypes.STRING(255),
allowNull: true,
unique: true,
},
},
{
timestamps: true,
paranoid: true,
freezeTableName: true,
indexes: [{ fields: ['element_type'] }, { fields: ['sort_order'] }, { fields: ['deletedAt'] }],
},
);
ui_elements.associate = (db) => {
db.ui_elements.belongsTo(db.users, {
as: 'createdBy',
});
db.ui_elements.belongsTo(db.users, {
as: 'updatedBy',
});
};
return ui_elements;
};

View File

@ -38,7 +38,10 @@ email: {
type: DataTypes.TEXT, type: DataTypes.TEXT,
allowNull: false, allowNull: false,
unique: true, unique: true,
validate: {
isEmail: { msg: 'Must be a valid email address' },
notEmpty: { msg: 'Email is required' },
},
}, },
disabled: { disabled: {

View File

@ -68,7 +68,7 @@ await queryInterface.bulkInsert("permissions", [{ id: getId(`READ_API_DOCS`), cr
await queryInterface.bulkInsert("permissions", [{ id: getId(`CREATE_SEARCH`), createdAt, updatedAt, name: `CREATE_SEARCH`}]); await queryInterface.bulkInsert("permissions", [{ id: getId(`CREATE_SEARCH`), createdAt, updatedAt, name: `CREATE_SEARCH`}]);
await queryInterface.sequelize.query(`create table "rolesPermissionsPermissions" await queryInterface.sequelize.query(`CREATE TABLE IF NOT EXISTS "rolesPermissionsPermissions"
( (
"createdAt" timestamp with time zone not null, "createdAt" timestamp with time zone not null,
"updatedAt" timestamp with time zone not null, "updatedAt" timestamp with time zone not null,
@ -84,7 +84,7 @@ constraint "rolesPermissionsPermissions_permission_fk"
);`); );`);
await queryInterface.sequelize.query( await queryInterface.sequelize.query(
'create index "rolesPermissionsPermissions_permission_idx" on "rolesPermissionsPermissions" ("permissionId");', 'CREATE INDEX IF NOT EXISTS "rolesPermissionsPermissions_permission_idx" ON "rolesPermissionsPermissions" ("permissionId");',
); );

15
backend/src/db/sync.js Normal file
View File

@ -0,0 +1,15 @@
const db = require('./models');
async function syncDatabase() {
try {
console.log('Syncing database...');
await db.sequelize.sync({ force: true });
console.log('Database synced successfully!');
process.exit(0);
} catch (error) {
console.error('Error syncing database:', error);
process.exit(1);
}
}
syncDatabase();

View File

@ -0,0 +1,101 @@
const express = require('express');
const { wrapAsync, commonErrorHandler } = require('../helpers');
const { checkCrudPermissions } = require('../middlewares/check-permissions');
const { parse } = require('json2csv');
const isUuidV4 = (value) =>
/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(value);
function createEntityRouter(entityName, Service, DBApi, options = {}) {
const router = express.Router();
const permissionEntity = options.permissionEntity || entityName;
router.use(checkCrudPermissions(permissionEntity));
router.post('/', wrapAsync(async (req, res) => {
const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`;
const link = new URL(referer);
const payload = await Service.create(req.body.data, req.currentUser, true, link.host);
res.status(200).send(payload);
}));
router.post('/bulk-import', wrapAsync(async (req, res) => {
const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`;
const link = new URL(referer);
await Service.bulkImport(req, res, true, link.host);
res.status(200).send(true);
}));
router.put('/:id', wrapAsync(async (req, res) => {
await Service.update(req.body.data, req.body.id, req.currentUser);
res.status(200).send(true);
}));
router.delete('/:id', wrapAsync(async (req, res) => {
await Service.remove(req.params.id, req.currentUser);
res.status(200).send(true);
}));
router.post('/deleteByIds', wrapAsync(async (req, res) => {
await Service.deleteByIds(req.body.data, req.currentUser);
res.status(200).send(true);
}));
router.get('/', wrapAsync(async (req, res) => {
const filetype = req.query.filetype;
const currentUser = req.currentUser;
const runtimeContext = req.runtimeContext;
const payload = await DBApi.findAll(req.query, { currentUser, runtimeContext });
if (filetype === 'csv') {
const fields = options.csvFields || DBApi.CSV_FIELDS || ['id', 'createdAt'];
const opts = { fields };
try {
const csv = parse(payload.rows, opts);
res.status(200).attachment('export.csv').send(csv);
} catch (err) {
console.error(err);
res.status(500).send('CSV export error');
}
} else {
res.status(200).send(payload);
}
}));
router.get('/count', wrapAsync(async (req, res) => {
const currentUser = req.currentUser;
const runtimeContext = req.runtimeContext;
const payload = await DBApi.findAll(req.query, { countOnly: true, currentUser, runtimeContext });
res.status(200).send(payload);
}));
router.get('/autocomplete', async (req, res) => {
const payload = await DBApi.findAllAutocomplete(
req.query.query,
req.query.limit,
req.query.offset
);
res.status(200).send(payload);
});
router.get('/:id', wrapAsync(async (req, res) => {
if (!isUuidV4(req.params.id)) {
return res.status(400).send(`Invalid ${entityName} id`);
}
const runtimeContext = req.runtimeContext;
const payload = await DBApi.findBy({ id: req.params.id }, { runtimeContext });
res.status(200).send(payload);
}));
if (options.customRoutes) {
options.customRoutes(router, Service, DBApi);
}
router.use('/', commonErrorHandler);
return router;
}
module.exports = { createEntityRouter, isUuidV4 };

View File

@ -0,0 +1,97 @@
const db = require('../db/models');
const processFile = require('../middlewares/upload');
const ValidationError = require('../services/notifications/errors/validation');
const csv = require('csv-parser');
const stream = require('stream');
function createEntityService(DBApi, options = {}) {
const entityName = options.entityName || 'Entity';
return class GenericService {
static async create(data, currentUser) {
const transaction = await db.sequelize.transaction();
try {
const record = await DBApi.create(data, { currentUser, transaction });
await transaction.commit();
return record;
} catch (error) {
await transaction.rollback();
throw error;
}
}
static async bulkImport(req, res) {
const transaction = await db.sequelize.transaction();
try {
await processFile(req, res);
const bufferStream = new stream.PassThrough();
const results = [];
await bufferStream.end(Buffer.from(req.file.buffer, 'utf-8'));
await new Promise((resolve, reject) => {
bufferStream
.pipe(csv())
.on('data', (data) => results.push(data))
.on('end', () => resolve())
.on('error', (error) => reject(error));
});
await DBApi.bulkImport(results, {
transaction,
ignoreDuplicates: true,
validate: true,
currentUser: req.currentUser,
});
await transaction.commit();
} catch (error) {
await transaction.rollback();
throw error;
}
}
static async update(data, id, currentUser) {
const transaction = await db.sequelize.transaction();
try {
const record = await DBApi.findBy({ id }, { transaction });
if (!record) {
throw new ValidationError(`${entityName}NotFound`);
}
const updated = await DBApi.update(id, data, { currentUser, transaction });
await transaction.commit();
return updated;
} catch (error) {
await transaction.rollback();
throw error;
}
}
static async deleteByIds(ids, currentUser) {
const transaction = await db.sequelize.transaction();
try {
await DBApi.deleteByIds(ids, { currentUser, transaction });
await transaction.commit();
} catch (error) {
await transaction.rollback();
throw error;
}
}
static async remove(id, currentUser) {
const transaction = await db.sequelize.transaction();
try {
await DBApi.remove(id, { currentUser, transaction });
await transaction.commit();
} catch (error) {
await transaction.rollback();
throw error;
}
}
};
}
module.exports = { createEntityService };

View File

@ -9,6 +9,7 @@ const bodyParser = require('body-parser');
const config = require('./config'); const config = require('./config');
const swaggerUI = require('swagger-ui-express'); const swaggerUI = require('swagger-ui-express');
const swaggerJsDoc = require('swagger-jsdoc'); const swaggerJsDoc = require('swagger-jsdoc');
const { logger, requestLogger } = require('./utils/logger');
const authRoutes = require('./routes/auth'); const authRoutes = require('./routes/auth');
const fileRoutes = require('./routes/file'); const fileRoutes = require('./routes/file');
@ -39,7 +40,6 @@ const presigned_url_requestsRoutes = require('./routes/presigned_url_requests');
const tour_pagesRoutes = require('./routes/tour_pages'); const tour_pagesRoutes = require('./routes/tour_pages');
const page_elementsRoutes = require('./routes/page_elements'); const page_elementsRoutes = require('./routes/page_elements');
const ui_elementsRoutes = require('./routes/ui_elements');
const page_linksRoutes = require('./routes/page_links'); const page_linksRoutes = require('./routes/page_links');
@ -120,6 +120,7 @@ require('./auth/auth');
app.use(bodyParser.json({ limit: '1mb' })); app.use(bodyParser.json({ limit: '1mb' }));
app.use(bodyParser.urlencoded({ extended: true, limit: '1mb' })); app.use(bodyParser.urlencoded({ extended: true, limit: '1mb' }));
app.use(requestLogger);
app.use(runtimeContextMiddleware); app.use(runtimeContextMiddleware);
const jwtAuth = passport.authenticate('jwt', { session: false }); const jwtAuth = passport.authenticate('jwt', { session: false });
@ -137,6 +138,29 @@ const requireRuntimeReadOrAuth = (req, res, next) => {
return jwtAuth(req, res, next); return jwtAuth(req, res, next);
}; };
// Health check endpoint (no auth required)
app.get('/api/health', async (req, res) => {
const db = require('./db/models');
const health = {
status: 'ok',
timestamp: new Date().toISOString(),
uptime: process.uptime(),
environment: process.env.NODE_ENV || 'development',
};
try {
await db.sequelize.authenticate();
health.database = 'connected';
} catch (error) {
health.status = 'degraded';
health.database = 'disconnected';
health.databaseError = error.message;
}
const statusCode = health.status === 'ok' ? 200 : 503;
res.status(statusCode).json(health);
});
app.use('/api/auth', authRoutes); app.use('/api/auth', authRoutes);
app.use('/api/file', fileRoutes); app.use('/api/file', fileRoutes);
app.use('/api/pexels', pexelsRoutes); app.use('/api/pexels', pexelsRoutes);
@ -172,7 +196,6 @@ app.use('/api/presigned_url_requests', jwtAuth, presigned_url_requestsRoutes);
mountRuntimeEntityRoute('/api/tour_pages', 'tour_pages', tour_pagesRoutes); mountRuntimeEntityRoute('/api/tour_pages', 'tour_pages', tour_pagesRoutes);
mountRuntimeEntityRoute('/api/page_elements', 'page_elements', page_elementsRoutes); mountRuntimeEntityRoute('/api/page_elements', 'page_elements', page_elementsRoutes);
app.use('/api/ui-elements', jwtAuth, ui_elementsRoutes);
mountRuntimeEntityRoute('/api/page_links', 'page_links', page_linksRoutes); mountRuntimeEntityRoute('/api/page_links', 'page_links', page_linksRoutes);
@ -227,7 +250,7 @@ if (fs.existsSync(publicDir)) {
const PORT = process.env.NODE_ENV === 'dev_stage' ? 3000 : 8080; const PORT = process.env.NODE_ENV === 'dev_stage' ? 3000 : 8080;
app.listen(PORT, () => { app.listen(PORT, () => {
console.log(`Listening on port ${PORT}`); logger.info({ port: PORT, env: process.env.NODE_ENV || 'development' }, 'Server started');
}); });
module.exports = app; module.exports = app;

View File

@ -0,0 +1,103 @@
const { body, param, query, validationResult } = require('express-validator');
const handleValidationErrors = (req, res, next) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({
error: 'Validation failed',
details: errors.array().map(err => ({
field: err.path,
message: err.msg,
value: err.value,
})),
});
}
next();
};
const validators = {
uuid: (field, location = 'param') => {
const validator = location === 'param' ? param(field) : body(field);
return validator.isUUID().withMessage(`${field} must be a valid UUID`);
},
requiredString: (field, min = 1, max = 255) =>
body(field)
.trim()
.notEmpty().withMessage(`${field} is required`)
.isLength({ min, max }).withMessage(`${field} must be ${min}-${max} characters`),
optionalString: (field, max = 255) =>
body(field)
.optional()
.trim()
.isLength({ max }).withMessage(`${field} must be at most ${max} characters`),
slug: (field) =>
body(field)
.optional()
.trim()
.matches(/^[a-z0-9_-]+$/i).withMessage(`${field} can only contain letters, numbers, dashes, underscores`),
email: (field) =>
body(field)
.trim()
.isEmail().withMessage('Must be a valid email')
.normalizeEmail(),
optionalEmail: (field) =>
body(field)
.optional()
.trim()
.isEmail().withMessage('Must be a valid email')
.normalizeEmail(),
enum: (field, values) =>
body(field)
.optional()
.isIn(values).withMessage(`${field} must be one of: ${values.join(', ')}`),
boolean: (field) =>
body(field)
.optional()
.isBoolean().withMessage(`${field} must be a boolean`),
integer: (field, min, max) => {
let validator = body(field).optional().isInt();
if (min !== undefined) validator = validator.custom(val => val >= min).withMessage(`${field} must be at least ${min}`);
if (max !== undefined) validator = validator.custom(val => val <= max).withMessage(`${field} must be at most ${max}`);
return validator;
},
pagination: () => [
query('page').optional().isInt({ min: 0 }).toInt(),
query('limit').optional().isInt({ min: 1, max: 100 }).toInt(),
],
url: (field) =>
body(field)
.optional()
.trim()
.isURL().withMessage(`${field} must be a valid URL`),
};
function createEntityValidation(entityConfig = {}) {
const { fields = [], requiredFields = [] } = entityConfig;
const fieldValidators = fields.map(field => {
if (requiredFields.includes(field.name)) {
return validators.requiredString(`data.${field.name}`, field.min || 1, field.max || 255);
}
return validators.optionalString(`data.${field.name}`, field.max || 255);
});
return {
create: [...fieldValidators, handleValidationErrors],
update: [validators.uuid('id'), ...fieldValidators, handleValidationErrors],
delete: [validators.uuid('id'), handleValidationErrors],
get: [validators.uuid('id'), handleValidationErrors],
list: [...validators.pagination(), handleValidationErrors],
};
}
module.exports = { handleValidationErrors, validators, createEntityValidation };

View File

@ -1,22 +1,6 @@
const express = require('express');
const Access_logsService = require('../services/access_logs'); const Access_logsService = require('../services/access_logs');
const Access_logsDBApi = require('../db/api/access_logs'); const Access_logsDBApi = require('../db/api/access_logs');
const wrapAsync = require('../helpers').wrapAsync; const { createEntityRouter } = require('../factories/router.factory');
const router = express.Router();
const { parse } = require('json2csv');
const {
checkCrudPermissions,
} = require('../middlewares/check-permissions');
router.use(checkCrudPermissions('access_logs'));
/** /**
* @swagger * @swagger
@ -25,20 +9,12 @@ router.use(checkCrudPermissions('access_logs'));
* Access_logs: * Access_logs:
* type: object * type: object
* properties: * properties:
* path: * path:
* type: string * type: string
* default: path
* ip_address: * ip_address:
* type: string * type: string
* default: ip_address
* user_agent: * user_agent:
* type: string * type: string
* default: user_agent
*
*/ */
/** /**
@ -48,389 +24,118 @@ router.use(checkCrudPermissions('access_logs'));
* description: The Access_logs managing API * description: The Access_logs managing API
*/ */
/**
* @swagger
* /api/access_logs:
* post:
* security:
* - bearerAuth: []
* tags: [Access_logs]
* summary: Add new item
* description: Add new item
* requestBody:
* required: true
* content:
* application/json:
* schema:
* properties:
* data:
* description: Data of the updated item
* type: object
* $ref: "#/components/schemas/Access_logs"
* responses:
* 200:
* description: The item was successfully added
* content:
* application/json:
* schema:
* $ref: "#/components/schemas/Access_logs"
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 405:
* description: Invalid input data
* 500:
* description: Some server error
*/
router.post('/', wrapAsync(async (req, res) => {
const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`;
const link = new URL(referer);
await Access_logsService.create(req.body.data, req.currentUser, true, link.host);
const payload = true;
res.status(200).send(payload);
}));
/**
* @swagger
* /api/budgets/bulk-import:
* post:
* security:
* - bearerAuth: []
* tags: [Access_logs]
* summary: Bulk import items
* description: Bulk import items
* requestBody:
* required: true
* content:
* application/json:
* schema:
* properties:
* data:
* description: Data of the updated items
* type: array
* items:
* $ref: "#/components/schemas/Access_logs"
* responses:
* 200:
* description: The items were successfully imported
* content:
* application/json:
* schema:
* $ref: "#/components/schemas/Access_logs"
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 405:
* description: Invalid input data
* 500:
* description: Some server error
*
*/
router.post('/bulk-import', wrapAsync(async (req, res) => {
const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`;
const link = new URL(referer);
await Access_logsService.bulkImport(req, res, true, link.host);
const payload = true;
res.status(200).send(payload);
}));
/**
* @swagger
* /api/access_logs/{id}:
* put:
* security:
* - bearerAuth: []
* tags: [Access_logs]
* summary: Update the data of the selected item
* description: Update the data of the selected item
* parameters:
* - in: path
* name: id
* description: Item ID to update
* required: true
* schema:
* type: string
* requestBody:
* description: Set new item data
* required: true
* content:
* application/json:
* schema:
* properties:
* id:
* description: ID of the updated item
* type: string
* data:
* description: Data of the updated item
* type: object
* $ref: "#/components/schemas/Access_logs"
* required:
* - id
* responses:
* 200:
* description: The item data was successfully updated
* content:
* application/json:
* schema:
* $ref: "#/components/schemas/Access_logs"
* 400:
* description: Invalid ID supplied
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Item not found
* 500:
* description: Some server error
*/
router.put('/:id', wrapAsync(async (req, res) => {
await Access_logsService.update(req.body.data, req.body.id, req.currentUser);
const payload = true;
res.status(200).send(payload);
}));
/**
* @swagger
* /api/access_logs/{id}:
* delete:
* security:
* - bearerAuth: []
* tags: [Access_logs]
* summary: Delete the selected item
* description: Delete the selected item
* parameters:
* - in: path
* name: id
* description: Item ID to delete
* required: true
* schema:
* type: string
* responses:
* 200:
* description: The item was successfully deleted
* content:
* application/json:
* schema:
* $ref: "#/components/schemas/Access_logs"
* 400:
* description: Invalid ID supplied
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Item not found
* 500:
* description: Some server error
*/
router.delete('/:id', wrapAsync(async (req, res) => {
await Access_logsService.remove(req.params.id, req.currentUser);
const payload = true;
res.status(200).send(payload);
}));
/**
* @swagger
* /api/access_logs/deleteByIds:
* post:
* security:
* - bearerAuth: []
* tags: [Access_logs]
* summary: Delete the selected item list
* description: Delete the selected item list
* requestBody:
* required: true
* content:
* application/json:
* schema:
* properties:
* ids:
* description: IDs of the updated items
* type: array
* responses:
* 200:
* description: The items was successfully deleted
* content:
* application/json:
* schema:
* $ref: "#/components/schemas/Access_logs"
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Items not found
* 500:
* description: Some server error
*/
router.post('/deleteByIds', wrapAsync(async (req, res) => {
await Access_logsService.deleteByIds(req.body.data, req.currentUser);
const payload = true;
res.status(200).send(payload);
}));
/**
* @swagger
* /api/access_logs:
* get:
* security:
* - bearerAuth: []
* tags: [Access_logs]
* summary: Get all access_logs
* description: Get all access_logs
* responses:
* 200:
* description: Access_logs list successfully received
* content:
* application/json:
* schema:
* type: array
* items:
* $ref: "#/components/schemas/Access_logs"
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Data not found
* 500:
* description: Some server error
*/
router.get('/', wrapAsync(async (req, res) => {
const filetype = req.query.filetype
const currentUser = req.currentUser;
const payload = await Access_logsDBApi.findAll(
req.query, { currentUser }
);
if (filetype && filetype === 'csv') {
const fields = ['id','path','ip_address','user_agent',
'accessed_at',
];
const opts = { fields };
try {
const csv = parse(payload.rows, opts);
res.status(200).attachment(csv);
res.send(csv)
} catch (err) {
console.error(err);
}
} else {
res.status(200).send(payload);
}
}));
/** /**
* @swagger * @swagger
* /api/access_logs/count: * /api/access_logs:
* get: * post:
* security: * security:
* - bearerAuth: [] * - bearerAuth: []
* tags: [Access_logs] * tags: [Access_logs]
* summary: Count all access_logs * summary: Add new item
* description: Count all access_logs * requestBody:
* required: true
* content:
* application/json:
* schema:
* properties:
* data:
* type: object
* $ref: "#/components/schemas/Access_logs"
* responses: * responses:
* 200: * 200:
* description: Access_logs count successfully received * description: The item was successfully added
* content:
* application/json:
* schema:
* type: array
* items:
* $ref: "#/components/schemas/Access_logs"
* 401: * 401:
* $ref: "#/components/responses/UnauthorizedError" * $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Data not found
* 500: * 500:
* description: Some server error * description: Some server error
*/
router.get('/count', wrapAsync(async (req, res) => {
const currentUser = req.currentUser;
const payload = await Access_logsDBApi.findAll(
req.query,
null,
{ countOnly: true, currentUser }
);
res.status(200).send(payload);
}));
/**
* @swagger
* /api/access_logs/autocomplete:
* get: * get:
* security: * security:
* - bearerAuth: [] * - bearerAuth: []
* tags: [Access_logs] * tags: [Access_logs]
* summary: Find all access_logs that match search criteria * summary: Get all access_logs
* description: Find all access_logs that match search criteria
* responses: * responses:
* 200: * 200:
* description: Access_logs list successfully received * description: Access_logs list successfully received
* content:
* application/json:
* schema:
* type: array
* items:
* $ref: "#/components/schemas/Access_logs"
* 401: * 401:
* $ref: "#/components/responses/UnauthorizedError" * $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Data not found
* 500: * 500:
* description: Some server error * description: Some server error
*/ */
router.get('/autocomplete', async (req, res) => {
const payload = await Access_logsDBApi.findAllAutocomplete(
req.query.query,
req.query.limit,
req.query.offset,
);
res.status(200).send(payload);
});
/** /**
* @swagger * @swagger
* /api/access_logs/{id}: * /api/access_logs/{id}:
* get: * put:
* security: * security:
* - bearerAuth: [] * - bearerAuth: []
* tags: [Access_logs] * tags: [Access_logs]
* summary: Get selected item * summary: Update the selected item
* description: Get selected item * parameters:
* parameters: * - in: path
* - in: path * name: id
* name: id * required: true
* description: ID of item to get * schema:
* required: true * type: string
* schema: * requestBody:
* type: string * required: true
* responses: * content:
* 200: * application/json:
* description: Selected item successfully received * schema:
* content: * properties:
* application/json: * id:
* schema: * type: string
* $ref: "#/components/schemas/Access_logs" * data:
* 400: * type: object
* description: Invalid ID supplied * $ref: "#/components/schemas/Access_logs"
* 401: * responses:
* $ref: "#/components/responses/UnauthorizedError" * 200:
* 404: * description: The item was successfully updated
* description: Item not found * 401:
* 500: * $ref: "#/components/responses/UnauthorizedError"
* description: Some server error * 404:
*/ * description: Item not found
router.get('/:id', wrapAsync(async (req, res) => { * 500:
const payload = await Access_logsDBApi.findBy( * description: Some server error
{ id: req.params.id }, * delete:
); * security:
* - bearerAuth: []
* tags: [Access_logs]
* summary: Delete the selected item
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: string
* responses:
* 200:
* description: The item was successfully deleted
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Item not found
* 500:
* description: Some server error
* get:
* security:
* - bearerAuth: []
* tags: [Access_logs]
* summary: Get selected item
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: string
* responses:
* 200:
* description: Selected item successfully received
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Item not found
* 500:
* description: Some server error
*/
module.exports = createEntityRouter('access_logs', Access_logsService, Access_logsDBApi);
res.status(200).send(payload);
}));
router.use('/', require('../helpers').commonErrorHandler);
module.exports = router;

View File

@ -1,22 +1,6 @@
const express = require('express');
const Asset_variantsService = require('../services/asset_variants'); const Asset_variantsService = require('../services/asset_variants');
const Asset_variantsDBApi = require('../db/api/asset_variants'); const Asset_variantsDBApi = require('../db/api/asset_variants');
const wrapAsync = require('../helpers').wrapAsync; const { createEntityRouter } = require('../factories/router.factory');
const router = express.Router();
const { parse } = require('json2csv');
const {
checkCrudPermissions,
} = require('../middlewares/check-permissions');
router.use(checkCrudPermissions('asset_variants'));
/** /**
* @swagger * @swagger
@ -25,23 +9,14 @@ router.use(checkCrudPermissions('asset_variants'));
* Asset_variants: * Asset_variants:
* type: object * type: object
* properties: * properties:
* cdn_url: * cdn_url:
* type: string * type: string
* default: cdn_url
* width_px: * width_px:
* type: integer * type: integer
* format: int64
* height_px: * height_px:
* type: integer * type: integer
* format: int64
* size_mb: * size_mb:
* type: integer * type: number
* format: int64
*
*/ */
/** /**
@ -51,389 +26,118 @@ router.use(checkCrudPermissions('asset_variants'));
* description: The Asset_variants managing API * description: The Asset_variants managing API
*/ */
/**
* @swagger
* /api/asset_variants:
* post:
* security:
* - bearerAuth: []
* tags: [Asset_variants]
* summary: Add new item
* description: Add new item
* requestBody:
* required: true
* content:
* application/json:
* schema:
* properties:
* data:
* description: Data of the updated item
* type: object
* $ref: "#/components/schemas/Asset_variants"
* responses:
* 200:
* description: The item was successfully added
* content:
* application/json:
* schema:
* $ref: "#/components/schemas/Asset_variants"
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 405:
* description: Invalid input data
* 500:
* description: Some server error
*/
router.post('/', wrapAsync(async (req, res) => {
const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`;
const link = new URL(referer);
await Asset_variantsService.create(req.body.data, req.currentUser, true, link.host);
const payload = true;
res.status(200).send(payload);
}));
/**
* @swagger
* /api/budgets/bulk-import:
* post:
* security:
* - bearerAuth: []
* tags: [Asset_variants]
* summary: Bulk import items
* description: Bulk import items
* requestBody:
* required: true
* content:
* application/json:
* schema:
* properties:
* data:
* description: Data of the updated items
* type: array
* items:
* $ref: "#/components/schemas/Asset_variants"
* responses:
* 200:
* description: The items were successfully imported
* content:
* application/json:
* schema:
* $ref: "#/components/schemas/Asset_variants"
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 405:
* description: Invalid input data
* 500:
* description: Some server error
*
*/
router.post('/bulk-import', wrapAsync(async (req, res) => {
const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`;
const link = new URL(referer);
await Asset_variantsService.bulkImport(req, res, true, link.host);
const payload = true;
res.status(200).send(payload);
}));
/**
* @swagger
* /api/asset_variants/{id}:
* put:
* security:
* - bearerAuth: []
* tags: [Asset_variants]
* summary: Update the data of the selected item
* description: Update the data of the selected item
* parameters:
* - in: path
* name: id
* description: Item ID to update
* required: true
* schema:
* type: string
* requestBody:
* description: Set new item data
* required: true
* content:
* application/json:
* schema:
* properties:
* id:
* description: ID of the updated item
* type: string
* data:
* description: Data of the updated item
* type: object
* $ref: "#/components/schemas/Asset_variants"
* required:
* - id
* responses:
* 200:
* description: The item data was successfully updated
* content:
* application/json:
* schema:
* $ref: "#/components/schemas/Asset_variants"
* 400:
* description: Invalid ID supplied
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Item not found
* 500:
* description: Some server error
*/
router.put('/:id', wrapAsync(async (req, res) => {
await Asset_variantsService.update(req.body.data, req.body.id, req.currentUser);
const payload = true;
res.status(200).send(payload);
}));
/**
* @swagger
* /api/asset_variants/{id}:
* delete:
* security:
* - bearerAuth: []
* tags: [Asset_variants]
* summary: Delete the selected item
* description: Delete the selected item
* parameters:
* - in: path
* name: id
* description: Item ID to delete
* required: true
* schema:
* type: string
* responses:
* 200:
* description: The item was successfully deleted
* content:
* application/json:
* schema:
* $ref: "#/components/schemas/Asset_variants"
* 400:
* description: Invalid ID supplied
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Item not found
* 500:
* description: Some server error
*/
router.delete('/:id', wrapAsync(async (req, res) => {
await Asset_variantsService.remove(req.params.id, req.currentUser);
const payload = true;
res.status(200).send(payload);
}));
/**
* @swagger
* /api/asset_variants/deleteByIds:
* post:
* security:
* - bearerAuth: []
* tags: [Asset_variants]
* summary: Delete the selected item list
* description: Delete the selected item list
* requestBody:
* required: true
* content:
* application/json:
* schema:
* properties:
* ids:
* description: IDs of the updated items
* type: array
* responses:
* 200:
* description: The items was successfully deleted
* content:
* application/json:
* schema:
* $ref: "#/components/schemas/Asset_variants"
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Items not found
* 500:
* description: Some server error
*/
router.post('/deleteByIds', wrapAsync(async (req, res) => {
await Asset_variantsService.deleteByIds(req.body.data, req.currentUser);
const payload = true;
res.status(200).send(payload);
}));
/**
* @swagger
* /api/asset_variants:
* get:
* security:
* - bearerAuth: []
* tags: [Asset_variants]
* summary: Get all asset_variants
* description: Get all asset_variants
* responses:
* 200:
* description: Asset_variants list successfully received
* content:
* application/json:
* schema:
* type: array
* items:
* $ref: "#/components/schemas/Asset_variants"
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Data not found
* 500:
* description: Some server error
*/
router.get('/', wrapAsync(async (req, res) => {
const filetype = req.query.filetype
const currentUser = req.currentUser;
const payload = await Asset_variantsDBApi.findAll(
req.query, { currentUser }
);
if (filetype && filetype === 'csv') {
const fields = ['id','cdn_url',
'width_px','height_px',
'size_mb',
];
const opts = { fields };
try {
const csv = parse(payload.rows, opts);
res.status(200).attachment(csv);
res.send(csv)
} catch (err) {
console.error(err);
}
} else {
res.status(200).send(payload);
}
}));
/** /**
* @swagger * @swagger
* /api/asset_variants/count: * /api/asset_variants:
* get: * post:
* security: * security:
* - bearerAuth: [] * - bearerAuth: []
* tags: [Asset_variants] * tags: [Asset_variants]
* summary: Count all asset_variants * summary: Add new item
* description: Count all asset_variants * requestBody:
* required: true
* content:
* application/json:
* schema:
* properties:
* data:
* type: object
* $ref: "#/components/schemas/Asset_variants"
* responses: * responses:
* 200: * 200:
* description: Asset_variants count successfully received * description: The item was successfully added
* content:
* application/json:
* schema:
* type: array
* items:
* $ref: "#/components/schemas/Asset_variants"
* 401: * 401:
* $ref: "#/components/responses/UnauthorizedError" * $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Data not found
* 500: * 500:
* description: Some server error * description: Some server error
*/
router.get('/count', wrapAsync(async (req, res) => {
const currentUser = req.currentUser;
const payload = await Asset_variantsDBApi.findAll(
req.query,
null,
{ countOnly: true, currentUser }
);
res.status(200).send(payload);
}));
/**
* @swagger
* /api/asset_variants/autocomplete:
* get: * get:
* security: * security:
* - bearerAuth: [] * - bearerAuth: []
* tags: [Asset_variants] * tags: [Asset_variants]
* summary: Find all asset_variants that match search criteria * summary: Get all asset variants
* description: Find all asset_variants that match search criteria
* responses: * responses:
* 200: * 200:
* description: Asset_variants list successfully received * description: Asset_variants list successfully received
* content:
* application/json:
* schema:
* type: array
* items:
* $ref: "#/components/schemas/Asset_variants"
* 401: * 401:
* $ref: "#/components/responses/UnauthorizedError" * $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Data not found
* 500: * 500:
* description: Some server error * description: Some server error
*/ */
router.get('/autocomplete', async (req, res) => {
const payload = await Asset_variantsDBApi.findAllAutocomplete(
req.query.query,
req.query.limit,
req.query.offset,
);
res.status(200).send(payload);
});
/** /**
* @swagger * @swagger
* /api/asset_variants/{id}: * /api/asset_variants/{id}:
* get: * put:
* security: * security:
* - bearerAuth: [] * - bearerAuth: []
* tags: [Asset_variants] * tags: [Asset_variants]
* summary: Get selected item * summary: Update the selected item
* description: Get selected item * parameters:
* parameters: * - in: path
* - in: path * name: id
* name: id * required: true
* description: ID of item to get * schema:
* required: true * type: string
* schema: * requestBody:
* type: string * required: true
* responses: * content:
* 200: * application/json:
* description: Selected item successfully received * schema:
* content: * properties:
* application/json: * id:
* schema: * type: string
* $ref: "#/components/schemas/Asset_variants" * data:
* 400: * type: object
* description: Invalid ID supplied * $ref: "#/components/schemas/Asset_variants"
* 401: * responses:
* $ref: "#/components/responses/UnauthorizedError" * 200:
* 404: * description: The item was successfully updated
* description: Item not found * 401:
* 500: * $ref: "#/components/responses/UnauthorizedError"
* description: Some server error * 404:
*/ * description: Item not found
router.get('/:id', wrapAsync(async (req, res) => { * 500:
const payload = await Asset_variantsDBApi.findBy( * description: Some server error
{ id: req.params.id }, * delete:
); * security:
* - bearerAuth: []
* tags: [Asset_variants]
* summary: Delete the selected item
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: string
* responses:
* 200:
* description: The item was successfully deleted
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Item not found
* 500:
* description: Some server error
* get:
* security:
* - bearerAuth: []
* tags: [Asset_variants]
* summary: Get selected item
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: string
* responses:
* 200:
* description: Selected item successfully received
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Item not found
* 500:
* description: Some server error
*/
module.exports = createEntityRouter('asset_variants', Asset_variantsService, Asset_variantsDBApi);
res.status(200).send(payload);
}));
router.use('/', require('../helpers').commonErrorHandler);
module.exports = router;

View File

@ -1,22 +1,6 @@
const express = require('express');
const AssetsService = require('../services/assets'); const AssetsService = require('../services/assets');
const AssetsDBApi = require('../db/api/assets'); const AssetsDBApi = require('../db/api/assets');
const wrapAsync = require('../helpers').wrapAsync; const { createEntityRouter } = require('../factories/router.factory');
const router = express.Router();
const { parse } = require('json2csv');
const {
checkCrudPermissions,
} = require('../middlewares/check-permissions');
router.use(checkCrudPermissions('assets'));
/** /**
* @swagger * @swagger
@ -25,38 +9,24 @@ router.use(checkCrudPermissions('assets'));
* Assets: * Assets:
* type: object * type: object
* properties: * properties:
* name: * name:
* type: string * type: string
* default: name
* cdn_url: * cdn_url:
* type: string * type: string
* default: cdn_url
* storage_key: * storage_key:
* type: string * type: string
* default: storage_key
* mime_type: * mime_type:
* type: string * type: string
* default: mime_type
* checksum: * checksum:
* type: string * type: string
* default: checksum
* width_px: * width_px:
* type: integer * type: integer
* format: int64
* height_px: * height_px:
* type: integer * type: integer
* format: int64
* size_mb: * size_mb:
* type: integer * type: number
* format: int64
* duration_sec: * duration_sec:
* type: integer * type: number
* format: int64
*
*/ */
/** /**
@ -66,389 +36,118 @@ router.use(checkCrudPermissions('assets'));
* description: The Assets managing API * description: The Assets managing API
*/ */
/**
* @swagger
* /api/assets:
* post:
* security:
* - bearerAuth: []
* tags: [Assets]
* summary: Add new item
* description: Add new item
* requestBody:
* required: true
* content:
* application/json:
* schema:
* properties:
* data:
* description: Data of the updated item
* type: object
* $ref: "#/components/schemas/Assets"
* responses:
* 200:
* description: The item was successfully added
* content:
* application/json:
* schema:
* $ref: "#/components/schemas/Assets"
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 405:
* description: Invalid input data
* 500:
* description: Some server error
*/
router.post('/', wrapAsync(async (req, res) => {
const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`;
const link = new URL(referer);
await AssetsService.create(req.body.data, req.currentUser, true, link.host);
const payload = true;
res.status(200).send(payload);
}));
/**
* @swagger
* /api/budgets/bulk-import:
* post:
* security:
* - bearerAuth: []
* tags: [Assets]
* summary: Bulk import items
* description: Bulk import items
* requestBody:
* required: true
* content:
* application/json:
* schema:
* properties:
* data:
* description: Data of the updated items
* type: array
* items:
* $ref: "#/components/schemas/Assets"
* responses:
* 200:
* description: The items were successfully imported
* content:
* application/json:
* schema:
* $ref: "#/components/schemas/Assets"
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 405:
* description: Invalid input data
* 500:
* description: Some server error
*
*/
router.post('/bulk-import', wrapAsync(async (req, res) => {
const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`;
const link = new URL(referer);
await AssetsService.bulkImport(req, res, true, link.host);
const payload = true;
res.status(200).send(payload);
}));
/**
* @swagger
* /api/assets/{id}:
* put:
* security:
* - bearerAuth: []
* tags: [Assets]
* summary: Update the data of the selected item
* description: Update the data of the selected item
* parameters:
* - in: path
* name: id
* description: Item ID to update
* required: true
* schema:
* type: string
* requestBody:
* description: Set new item data
* required: true
* content:
* application/json:
* schema:
* properties:
* id:
* description: ID of the updated item
* type: string
* data:
* description: Data of the updated item
* type: object
* $ref: "#/components/schemas/Assets"
* required:
* - id
* responses:
* 200:
* description: The item data was successfully updated
* content:
* application/json:
* schema:
* $ref: "#/components/schemas/Assets"
* 400:
* description: Invalid ID supplied
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Item not found
* 500:
* description: Some server error
*/
router.put('/:id', wrapAsync(async (req, res) => {
await AssetsService.update(req.body.data, req.body.id, req.currentUser);
const payload = true;
res.status(200).send(payload);
}));
/**
* @swagger
* /api/assets/{id}:
* delete:
* security:
* - bearerAuth: []
* tags: [Assets]
* summary: Delete the selected item
* description: Delete the selected item
* parameters:
* - in: path
* name: id
* description: Item ID to delete
* required: true
* schema:
* type: string
* responses:
* 200:
* description: The item was successfully deleted
* content:
* application/json:
* schema:
* $ref: "#/components/schemas/Assets"
* 400:
* description: Invalid ID supplied
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Item not found
* 500:
* description: Some server error
*/
router.delete('/:id', wrapAsync(async (req, res) => {
await AssetsService.remove(req.params.id, req.currentUser);
const payload = true;
res.status(200).send(payload);
}));
/**
* @swagger
* /api/assets/deleteByIds:
* post:
* security:
* - bearerAuth: []
* tags: [Assets]
* summary: Delete the selected item list
* description: Delete the selected item list
* requestBody:
* required: true
* content:
* application/json:
* schema:
* properties:
* ids:
* description: IDs of the updated items
* type: array
* responses:
* 200:
* description: The items was successfully deleted
* content:
* application/json:
* schema:
* $ref: "#/components/schemas/Assets"
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Items not found
* 500:
* description: Some server error
*/
router.post('/deleteByIds', wrapAsync(async (req, res) => {
await AssetsService.deleteByIds(req.body.data, req.currentUser);
const payload = true;
res.status(200).send(payload);
}));
/**
* @swagger
* /api/assets:
* get:
* security:
* - bearerAuth: []
* tags: [Assets]
* summary: Get all assets
* description: Get all assets
* responses:
* 200:
* description: Assets list successfully received
* content:
* application/json:
* schema:
* type: array
* items:
* $ref: "#/components/schemas/Assets"
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Data not found
* 500:
* description: Some server error
*/
router.get('/', wrapAsync(async (req, res) => {
const filetype = req.query.filetype
const currentUser = req.currentUser;
const payload = await AssetsDBApi.findAll(
req.query, { currentUser }
);
if (filetype && filetype === 'csv') {
const fields = ['id','name','cdn_url','storage_key','mime_type','checksum',
'width_px','height_px',
'size_mb','duration_sec',
'deleted_at_time',
];
const opts = { fields };
try {
const csv = parse(payload.rows, opts);
res.status(200).attachment(csv);
res.send(csv)
} catch (err) {
console.error(err);
}
} else {
res.status(200).send(payload);
}
}));
/** /**
* @swagger * @swagger
* /api/assets/count: * /api/assets:
* get: * post:
* security: * security:
* - bearerAuth: [] * - bearerAuth: []
* tags: [Assets] * tags: [Assets]
* summary: Count all assets * summary: Add new item
* description: Count all assets * requestBody:
* required: true
* content:
* application/json:
* schema:
* properties:
* data:
* type: object
* $ref: "#/components/schemas/Assets"
* responses: * responses:
* 200: * 200:
* description: Assets count successfully received * description: The item was successfully added
* content:
* application/json:
* schema:
* type: array
* items:
* $ref: "#/components/schemas/Assets"
* 401: * 401:
* $ref: "#/components/responses/UnauthorizedError" * $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Data not found
* 500: * 500:
* description: Some server error * description: Some server error
*/
router.get('/count', wrapAsync(async (req, res) => {
const currentUser = req.currentUser;
const payload = await AssetsDBApi.findAll(
req.query,
null,
{ countOnly: true, currentUser }
);
res.status(200).send(payload);
}));
/**
* @swagger
* /api/assets/autocomplete:
* get: * get:
* security: * security:
* - bearerAuth: [] * - bearerAuth: []
* tags: [Assets] * tags: [Assets]
* summary: Find all assets that match search criteria * summary: Get all assets
* description: Find all assets that match search criteria
* responses: * responses:
* 200: * 200:
* description: Assets list successfully received * description: Assets list successfully received
* content:
* application/json:
* schema:
* type: array
* items:
* $ref: "#/components/schemas/Assets"
* 401: * 401:
* $ref: "#/components/responses/UnauthorizedError" * $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Data not found
* 500: * 500:
* description: Some server error * description: Some server error
*/ */
router.get('/autocomplete', async (req, res) => {
const payload = await AssetsDBApi.findAllAutocomplete(
req.query.query,
req.query.limit,
req.query.offset,
);
res.status(200).send(payload);
});
/** /**
* @swagger * @swagger
* /api/assets/{id}: * /api/assets/{id}:
* get: * put:
* security: * security:
* - bearerAuth: [] * - bearerAuth: []
* tags: [Assets] * tags: [Assets]
* summary: Get selected item * summary: Update the selected item
* description: Get selected item * parameters:
* parameters: * - in: path
* - in: path * name: id
* name: id * required: true
* description: ID of item to get * schema:
* required: true * type: string
* schema: * requestBody:
* type: string * required: true
* responses: * content:
* 200: * application/json:
* description: Selected item successfully received * schema:
* content: * properties:
* application/json: * id:
* schema: * type: string
* $ref: "#/components/schemas/Assets" * data:
* 400: * type: object
* description: Invalid ID supplied * $ref: "#/components/schemas/Assets"
* 401: * responses:
* $ref: "#/components/responses/UnauthorizedError" * 200:
* 404: * description: The item was successfully updated
* description: Item not found * 401:
* 500: * $ref: "#/components/responses/UnauthorizedError"
* description: Some server error * 404:
*/ * description: Item not found
router.get('/:id', wrapAsync(async (req, res) => { * 500:
const payload = await AssetsDBApi.findBy( * description: Some server error
{ id: req.params.id }, * delete:
); * security:
* - bearerAuth: []
* tags: [Assets]
* summary: Delete the selected item
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: string
* responses:
* 200:
* description: The item was successfully deleted
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Item not found
* 500:
* description: Some server error
* get:
* security:
* - bearerAuth: []
* tags: [Assets]
* summary: Get selected item
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: string
* responses:
* 200:
* description: Selected item successfully received
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Item not found
* 500:
* description: Some server error
*/
module.exports = createEntityRouter('assets', AssetsService, AssetsDBApi);
res.status(200).send(payload);
}));
router.use('/', require('../helpers').commonErrorHandler);
module.exports = router;

View File

@ -1,22 +1,6 @@
const express = require('express');
const Page_elementsService = require('../services/page_elements'); const Page_elementsService = require('../services/page_elements');
const Page_elementsDBApi = require('../db/api/page_elements'); const Page_elementsDBApi = require('../db/api/page_elements');
const wrapAsync = require('../helpers').wrapAsync; const { createEntityRouter } = require('../factories/router.factory');
const router = express.Router();
const { parse } = require('json2csv');
const {
checkCrudPermissions,
} = require('../middlewares/check-permissions');
router.use(checkCrudPermissions('page_elements'));
/** /**
* @swagger * @swagger
@ -25,38 +9,24 @@ router.use(checkCrudPermissions('page_elements'));
* Page_elements: * Page_elements:
* type: object * type: object
* properties: * properties:
* name: * name:
* type: string * type: string
* default: name
* style_json: * style_json:
* type: string * type: string
* default: style_json
* content_json: * content_json:
* type: string * type: string
* default: content_json
* sort_order: * sort_order:
* type: integer * type: integer
* format: int64
* x_percent: * x_percent:
* type: integer * type: number
* format: int64
* y_percent: * y_percent:
* type: integer * type: number
* format: int64
* width_percent: * width_percent:
* type: integer * type: number
* format: int64
* height_percent: * height_percent:
* type: integer * type: number
* format: int64
* rotation_deg: * rotation_deg:
* type: integer * type: number
* format: int64
*
*/ */
/** /**
@ -66,393 +36,118 @@ router.use(checkCrudPermissions('page_elements'));
* description: The Page_elements managing API * description: The Page_elements managing API
*/ */
/**
* @swagger
* /api/page_elements:
* post:
* security:
* - bearerAuth: []
* tags: [Page_elements]
* summary: Add new item
* description: Add new item
* requestBody:
* required: true
* content:
* application/json:
* schema:
* properties:
* data:
* description: Data of the updated item
* type: object
* $ref: "#/components/schemas/Page_elements"
* responses:
* 200:
* description: The item was successfully added
* content:
* application/json:
* schema:
* $ref: "#/components/schemas/Page_elements"
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 405:
* description: Invalid input data
* 500:
* description: Some server error
*/
router.post('/', wrapAsync(async (req, res) => {
const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`;
const link = new URL(referer);
await Page_elementsService.create(req.body.data, req.currentUser, true, link.host);
const payload = true;
res.status(200).send(payload);
}));
/**
* @swagger
* /api/budgets/bulk-import:
* post:
* security:
* - bearerAuth: []
* tags: [Page_elements]
* summary: Bulk import items
* description: Bulk import items
* requestBody:
* required: true
* content:
* application/json:
* schema:
* properties:
* data:
* description: Data of the updated items
* type: array
* items:
* $ref: "#/components/schemas/Page_elements"
* responses:
* 200:
* description: The items were successfully imported
* content:
* application/json:
* schema:
* $ref: "#/components/schemas/Page_elements"
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 405:
* description: Invalid input data
* 500:
* description: Some server error
*
*/
router.post('/bulk-import', wrapAsync(async (req, res) => {
const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`;
const link = new URL(referer);
await Page_elementsService.bulkImport(req, res, true, link.host);
const payload = true;
res.status(200).send(payload);
}));
/**
* @swagger
* /api/page_elements/{id}:
* put:
* security:
* - bearerAuth: []
* tags: [Page_elements]
* summary: Update the data of the selected item
* description: Update the data of the selected item
* parameters:
* - in: path
* name: id
* description: Item ID to update
* required: true
* schema:
* type: string
* requestBody:
* description: Set new item data
* required: true
* content:
* application/json:
* schema:
* properties:
* id:
* description: ID of the updated item
* type: string
* data:
* description: Data of the updated item
* type: object
* $ref: "#/components/schemas/Page_elements"
* required:
* - id
* responses:
* 200:
* description: The item data was successfully updated
* content:
* application/json:
* schema:
* $ref: "#/components/schemas/Page_elements"
* 400:
* description: Invalid ID supplied
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Item not found
* 500:
* description: Some server error
*/
router.put('/:id', wrapAsync(async (req, res) => {
await Page_elementsService.update(req.body.data, req.body.id, req.currentUser);
const payload = true;
res.status(200).send(payload);
}));
/**
* @swagger
* /api/page_elements/{id}:
* delete:
* security:
* - bearerAuth: []
* tags: [Page_elements]
* summary: Delete the selected item
* description: Delete the selected item
* parameters:
* - in: path
* name: id
* description: Item ID to delete
* required: true
* schema:
* type: string
* responses:
* 200:
* description: The item was successfully deleted
* content:
* application/json:
* schema:
* $ref: "#/components/schemas/Page_elements"
* 400:
* description: Invalid ID supplied
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Item not found
* 500:
* description: Some server error
*/
router.delete('/:id', wrapAsync(async (req, res) => {
await Page_elementsService.remove(req.params.id, req.currentUser);
const payload = true;
res.status(200).send(payload);
}));
/**
* @swagger
* /api/page_elements/deleteByIds:
* post:
* security:
* - bearerAuth: []
* tags: [Page_elements]
* summary: Delete the selected item list
* description: Delete the selected item list
* requestBody:
* required: true
* content:
* application/json:
* schema:
* properties:
* ids:
* description: IDs of the updated items
* type: array
* responses:
* 200:
* description: The items was successfully deleted
* content:
* application/json:
* schema:
* $ref: "#/components/schemas/Page_elements"
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Items not found
* 500:
* description: Some server error
*/
router.post('/deleteByIds', wrapAsync(async (req, res) => {
await Page_elementsService.deleteByIds(req.body.data, req.currentUser);
const payload = true;
res.status(200).send(payload);
}));
/**
* @swagger
* /api/page_elements:
* get:
* security:
* - bearerAuth: []
* tags: [Page_elements]
* summary: Get all page_elements
* description: Get all page_elements
* responses:
* 200:
* description: Page_elements list successfully received
* content:
* application/json:
* schema:
* type: array
* items:
* $ref: "#/components/schemas/Page_elements"
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Data not found
* 500:
* description: Some server error
*/
router.get('/', wrapAsync(async (req, res) => {
const filetype = req.query.filetype
const currentUser = req.currentUser;
const runtimeContext = req.runtimeContext;
const payload = await Page_elementsDBApi.findAll(
req.query, { currentUser, runtimeContext }
);
if (filetype && filetype === 'csv') {
const fields = ['id','name','style_json','content_json',
'sort_order',
'x_percent','y_percent','width_percent','height_percent','rotation_deg',
];
const opts = { fields };
try {
const csv = parse(payload.rows, opts);
res.status(200).attachment(csv);
res.send(csv)
} catch (err) {
console.error(err);
}
} else {
res.status(200).send(payload);
}
}));
/** /**
* @swagger * @swagger
* /api/page_elements/count: * /api/page_elements:
* get: * post:
* security: * security:
* - bearerAuth: [] * - bearerAuth: []
* tags: [Page_elements] * tags: [Page_elements]
* summary: Count all page_elements * summary: Add new item
* description: Count all page_elements * requestBody:
* required: true
* content:
* application/json:
* schema:
* properties:
* data:
* type: object
* $ref: "#/components/schemas/Page_elements"
* responses: * responses:
* 200: * 200:
* description: Page_elements count successfully received * description: The item was successfully added
* content:
* application/json:
* schema:
* type: array
* items:
* $ref: "#/components/schemas/Page_elements"
* 401: * 401:
* $ref: "#/components/responses/UnauthorizedError" * $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Data not found
* 500: * 500:
* description: Some server error * description: Some server error
*/
router.get('/count', wrapAsync(async (req, res) => {
const currentUser = req.currentUser;
const runtimeContext = req.runtimeContext;
const payload = await Page_elementsDBApi.findAll(
req.query,
null,
{ countOnly: true, currentUser, runtimeContext }
);
res.status(200).send(payload);
}));
/**
* @swagger
* /api/page_elements/autocomplete:
* get: * get:
* security: * security:
* - bearerAuth: [] * - bearerAuth: []
* tags: [Page_elements] * tags: [Page_elements]
* summary: Find all page_elements that match search criteria * summary: Get all page_elements
* description: Find all page_elements that match search criteria
* responses: * responses:
* 200: * 200:
* description: Page_elements list successfully received * description: Page_elements list successfully received
* content:
* application/json:
* schema:
* type: array
* items:
* $ref: "#/components/schemas/Page_elements"
* 401: * 401:
* $ref: "#/components/responses/UnauthorizedError" * $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Data not found
* 500: * 500:
* description: Some server error * description: Some server error
*/ */
router.get('/autocomplete', async (req, res) => {
const payload = await Page_elementsDBApi.findAllAutocomplete(
req.query.query,
req.query.limit,
req.query.offset,
);
res.status(200).send(payload);
});
/** /**
* @swagger * @swagger
* /api/page_elements/{id}: * /api/page_elements/{id}:
* get: * put:
* security: * security:
* - bearerAuth: [] * - bearerAuth: []
* tags: [Page_elements] * tags: [Page_elements]
* summary: Get selected item * summary: Update the selected item
* description: Get selected item * parameters:
* parameters: * - in: path
* - in: path * name: id
* name: id * required: true
* description: ID of item to get * schema:
* required: true * type: string
* schema: * requestBody:
* type: string * required: true
* responses: * content:
* 200: * application/json:
* description: Selected item successfully received * schema:
* content: * properties:
* application/json: * id:
* schema: * type: string
* $ref: "#/components/schemas/Page_elements" * data:
* 400: * type: object
* description: Invalid ID supplied * $ref: "#/components/schemas/Page_elements"
* 401: * responses:
* $ref: "#/components/responses/UnauthorizedError" * 200:
* 404: * description: The item was successfully updated
* description: Item not found * 401:
* 500: * $ref: "#/components/responses/UnauthorizedError"
* description: Some server error * 404:
*/ * description: Item not found
router.get('/:id', wrapAsync(async (req, res) => { * 500:
const runtimeContext = req.runtimeContext; * description: Some server error
const payload = await Page_elementsDBApi.findBy( * delete:
{ id: req.params.id }, * security:
{ runtimeContext }, * - bearerAuth: []
); * tags: [Page_elements]
* summary: Delete the selected item
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: string
* responses:
* 200:
* description: The item was successfully deleted
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Item not found
* 500:
* description: Some server error
* get:
* security:
* - bearerAuth: []
* tags: [Page_elements]
* summary: Get selected item
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: string
* responses:
* 200:
* description: Selected item successfully received
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Item not found
* 500:
* description: Some server error
*/
module.exports = createEntityRouter('page_elements', Page_elementsService, Page_elementsDBApi);
res.status(200).send(payload);
}));
router.use('/', require('../helpers').commonErrorHandler);
module.exports = router;

View File

@ -1,22 +1,6 @@
const express = require('express');
const Page_linksService = require('../services/page_links'); const Page_linksService = require('../services/page_links');
const Page_linksDBApi = require('../db/api/page_links'); const Page_linksDBApi = require('../db/api/page_links');
const wrapAsync = require('../helpers').wrapAsync; const { createEntityRouter } = require('../factories/router.factory');
const router = express.Router();
const { parse } = require('json2csv');
const {
checkCrudPermissions,
} = require('../middlewares/check-permissions');
router.use(checkCrudPermissions('page_links'));
/** /**
* @swagger * @swagger
@ -25,17 +9,10 @@ router.use(checkCrudPermissions('page_links'));
* Page_links: * Page_links:
* type: object * type: object
* properties: * properties:
* external_url: * external_url:
* type: string * type: string
* default: external_url
* trigger_selector: * trigger_selector:
* type: string * type: string
* default: trigger_selector
*
*/ */
/** /**
@ -45,393 +22,118 @@ router.use(checkCrudPermissions('page_links'));
* description: The Page_links managing API * description: The Page_links managing API
*/ */
/**
* @swagger
* /api/page_links:
* post:
* security:
* - bearerAuth: []
* tags: [Page_links]
* summary: Add new item
* description: Add new item
* requestBody:
* required: true
* content:
* application/json:
* schema:
* properties:
* data:
* description: Data of the updated item
* type: object
* $ref: "#/components/schemas/Page_links"
* responses:
* 200:
* description: The item was successfully added
* content:
* application/json:
* schema:
* $ref: "#/components/schemas/Page_links"
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 405:
* description: Invalid input data
* 500:
* description: Some server error
*/
router.post('/', wrapAsync(async (req, res) => {
const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`;
const link = new URL(referer);
await Page_linksService.create(req.body.data, req.currentUser, true, link.host);
const payload = true;
res.status(200).send(payload);
}));
/**
* @swagger
* /api/budgets/bulk-import:
* post:
* security:
* - bearerAuth: []
* tags: [Page_links]
* summary: Bulk import items
* description: Bulk import items
* requestBody:
* required: true
* content:
* application/json:
* schema:
* properties:
* data:
* description: Data of the updated items
* type: array
* items:
* $ref: "#/components/schemas/Page_links"
* responses:
* 200:
* description: The items were successfully imported
* content:
* application/json:
* schema:
* $ref: "#/components/schemas/Page_links"
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 405:
* description: Invalid input data
* 500:
* description: Some server error
*
*/
router.post('/bulk-import', wrapAsync(async (req, res) => {
const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`;
const link = new URL(referer);
await Page_linksService.bulkImport(req, res, true, link.host);
const payload = true;
res.status(200).send(payload);
}));
/**
* @swagger
* /api/page_links/{id}:
* put:
* security:
* - bearerAuth: []
* tags: [Page_links]
* summary: Update the data of the selected item
* description: Update the data of the selected item
* parameters:
* - in: path
* name: id
* description: Item ID to update
* required: true
* schema:
* type: string
* requestBody:
* description: Set new item data
* required: true
* content:
* application/json:
* schema:
* properties:
* id:
* description: ID of the updated item
* type: string
* data:
* description: Data of the updated item
* type: object
* $ref: "#/components/schemas/Page_links"
* required:
* - id
* responses:
* 200:
* description: The item data was successfully updated
* content:
* application/json:
* schema:
* $ref: "#/components/schemas/Page_links"
* 400:
* description: Invalid ID supplied
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Item not found
* 500:
* description: Some server error
*/
router.put('/:id', wrapAsync(async (req, res) => {
await Page_linksService.update(req.body.data, req.body.id, req.currentUser);
const payload = true;
res.status(200).send(payload);
}));
/**
* @swagger
* /api/page_links/{id}:
* delete:
* security:
* - bearerAuth: []
* tags: [Page_links]
* summary: Delete the selected item
* description: Delete the selected item
* parameters:
* - in: path
* name: id
* description: Item ID to delete
* required: true
* schema:
* type: string
* responses:
* 200:
* description: The item was successfully deleted
* content:
* application/json:
* schema:
* $ref: "#/components/schemas/Page_links"
* 400:
* description: Invalid ID supplied
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Item not found
* 500:
* description: Some server error
*/
router.delete('/:id', wrapAsync(async (req, res) => {
await Page_linksService.remove(req.params.id, req.currentUser);
const payload = true;
res.status(200).send(payload);
}));
/**
* @swagger
* /api/page_links/deleteByIds:
* post:
* security:
* - bearerAuth: []
* tags: [Page_links]
* summary: Delete the selected item list
* description: Delete the selected item list
* requestBody:
* required: true
* content:
* application/json:
* schema:
* properties:
* ids:
* description: IDs of the updated items
* type: array
* responses:
* 200:
* description: The items was successfully deleted
* content:
* application/json:
* schema:
* $ref: "#/components/schemas/Page_links"
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Items not found
* 500:
* description: Some server error
*/
router.post('/deleteByIds', wrapAsync(async (req, res) => {
await Page_linksService.deleteByIds(req.body.data, req.currentUser);
const payload = true;
res.status(200).send(payload);
}));
/**
* @swagger
* /api/page_links:
* get:
* security:
* - bearerAuth: []
* tags: [Page_links]
* summary: Get all page_links
* description: Get all page_links
* responses:
* 200:
* description: Page_links list successfully received
* content:
* application/json:
* schema:
* type: array
* items:
* $ref: "#/components/schemas/Page_links"
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Data not found
* 500:
* description: Some server error
*/
router.get('/', wrapAsync(async (req, res) => {
const filetype = req.query.filetype
const currentUser = req.currentUser;
const runtimeContext = req.runtimeContext;
const payload = await Page_linksDBApi.findAll(
req.query, { currentUser, runtimeContext }
);
if (filetype && filetype === 'csv') {
const fields = ['id','external_url','trigger_selector',
];
const opts = { fields };
try {
const csv = parse(payload.rows, opts);
res.status(200).attachment(csv);
res.send(csv)
} catch (err) {
console.error(err);
}
} else {
res.status(200).send(payload);
}
}));
/** /**
* @swagger * @swagger
* /api/page_links/count: * /api/page_links:
* get: * post:
* security: * security:
* - bearerAuth: [] * - bearerAuth: []
* tags: [Page_links] * tags: [Page_links]
* summary: Count all page_links * summary: Add new item
* description: Count all page_links * requestBody:
* required: true
* content:
* application/json:
* schema:
* properties:
* data:
* type: object
* $ref: "#/components/schemas/Page_links"
* responses: * responses:
* 200: * 200:
* description: Page_links count successfully received * description: The item was successfully added
* content:
* application/json:
* schema:
* type: array
* items:
* $ref: "#/components/schemas/Page_links"
* 401: * 401:
* $ref: "#/components/responses/UnauthorizedError" * $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Data not found
* 500: * 500:
* description: Some server error * description: Some server error
*/
router.get('/count', wrapAsync(async (req, res) => {
const currentUser = req.currentUser;
const runtimeContext = req.runtimeContext;
const payload = await Page_linksDBApi.findAll(
req.query,
null,
{ countOnly: true, currentUser, runtimeContext }
);
res.status(200).send(payload);
}));
/**
* @swagger
* /api/page_links/autocomplete:
* get: * get:
* security: * security:
* - bearerAuth: [] * - bearerAuth: []
* tags: [Page_links] * tags: [Page_links]
* summary: Find all page_links that match search criteria * summary: Get all page_links
* description: Find all page_links that match search criteria
* responses: * responses:
* 200: * 200:
* description: Page_links list successfully received * description: Page_links list successfully received
* content:
* application/json:
* schema:
* type: array
* items:
* $ref: "#/components/schemas/Page_links"
* 401: * 401:
* $ref: "#/components/responses/UnauthorizedError" * $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Data not found
* 500: * 500:
* description: Some server error * description: Some server error
*/ */
router.get('/autocomplete', async (req, res) => {
const payload = await Page_linksDBApi.findAllAutocomplete(
req.query.query,
req.query.limit,
req.query.offset,
);
res.status(200).send(payload);
});
/** /**
* @swagger * @swagger
* /api/page_links/{id}: * /api/page_links/{id}:
* get: * put:
* security: * security:
* - bearerAuth: [] * - bearerAuth: []
* tags: [Page_links] * tags: [Page_links]
* summary: Get selected item * summary: Update the selected item
* description: Get selected item * parameters:
* parameters: * - in: path
* - in: path * name: id
* name: id * required: true
* description: ID of item to get * schema:
* required: true * type: string
* schema: * requestBody:
* type: string * required: true
* responses: * content:
* 200: * application/json:
* description: Selected item successfully received * schema:
* content: * properties:
* application/json: * id:
* schema: * type: string
* $ref: "#/components/schemas/Page_links" * data:
* 400: * type: object
* description: Invalid ID supplied * $ref: "#/components/schemas/Page_links"
* 401: * responses:
* $ref: "#/components/responses/UnauthorizedError" * 200:
* 404: * description: The item was successfully updated
* description: Item not found * 401:
* 500: * $ref: "#/components/responses/UnauthorizedError"
* description: Some server error * 404:
*/ * description: Item not found
router.get('/:id', wrapAsync(async (req, res) => { * 500:
const runtimeContext = req.runtimeContext; * description: Some server error
const payload = await Page_linksDBApi.findBy( * delete:
{ id: req.params.id }, * security:
{ runtimeContext }, * - bearerAuth: []
); * tags: [Page_links]
* summary: Delete the selected item
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: string
* responses:
* 200:
* description: The item was successfully deleted
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Item not found
* 500:
* description: Some server error
* get:
* security:
* - bearerAuth: []
* tags: [Page_links]
* summary: Get selected item
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: string
* responses:
* 200:
* description: Selected item successfully received
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Item not found
* 500:
* description: Some server error
*/
module.exports = createEntityRouter('page_links', Page_linksService, Page_linksDBApi);
res.status(200).send(payload);
}));
router.use('/', require('../helpers').commonErrorHandler);
module.exports = router;

View File

@ -1,22 +1,6 @@
const express = require('express');
const PermissionsService = require('../services/permissions'); const PermissionsService = require('../services/permissions');
const PermissionsDBApi = require('../db/api/permissions'); const PermissionsDBApi = require('../db/api/permissions');
const wrapAsync = require('../helpers').wrapAsync; const { createEntityRouter } = require('../factories/router.factory');
const router = express.Router();
const { parse } = require('json2csv');
const {
checkCrudPermissions,
} = require('../middlewares/check-permissions');
router.use(checkCrudPermissions('permissions'));
/** /**
* @swagger * @swagger
@ -25,13 +9,9 @@ router.use(checkCrudPermissions('permissions'));
* Permissions: * Permissions:
* type: object * type: object
* properties: * properties:
* name: * name:
* type: string * type: string
* default: name * default: name
*/ */
/** /**
@ -41,319 +21,44 @@ router.use(checkCrudPermissions('permissions'));
* description: The Permissions managing API * description: The Permissions managing API
*/ */
/**
* @swagger
* /api/permissions:
* post:
* security:
* - bearerAuth: []
* tags: [Permissions]
* summary: Add new item
* description: Add new item
* requestBody:
* required: true
* content:
* application/json:
* schema:
* properties:
* data:
* description: Data of the updated item
* type: object
* $ref: "#/components/schemas/Permissions"
* responses:
* 200:
* description: The item was successfully added
* content:
* application/json:
* schema:
* $ref: "#/components/schemas/Permissions"
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 405:
* description: Invalid input data
* 500:
* description: Some server error
*/
router.post('/', wrapAsync(async (req, res) => {
const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`;
const link = new URL(referer);
await PermissionsService.create(req.body.data, req.currentUser, true, link.host);
const payload = true;
res.status(200).send(payload);
}));
/**
* @swagger
* /api/budgets/bulk-import:
* post:
* security:
* - bearerAuth: []
* tags: [Permissions]
* summary: Bulk import items
* description: Bulk import items
* requestBody:
* required: true
* content:
* application/json:
* schema:
* properties:
* data:
* description: Data of the updated items
* type: array
* items:
* $ref: "#/components/schemas/Permissions"
* responses:
* 200:
* description: The items were successfully imported
* content:
* application/json:
* schema:
* $ref: "#/components/schemas/Permissions"
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 405:
* description: Invalid input data
* 500:
* description: Some server error
*
*/
router.post('/bulk-import', wrapAsync(async (req, res) => {
const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`;
const link = new URL(referer);
await PermissionsService.bulkImport(req, res, true, link.host);
const payload = true;
res.status(200).send(payload);
}));
/**
* @swagger
* /api/permissions/{id}:
* put:
* security:
* - bearerAuth: []
* tags: [Permissions]
* summary: Update the data of the selected item
* description: Update the data of the selected item
* parameters:
* - in: path
* name: id
* description: Item ID to update
* required: true
* schema:
* type: string
* requestBody:
* description: Set new item data
* required: true
* content:
* application/json:
* schema:
* properties:
* id:
* description: ID of the updated item
* type: string
* data:
* description: Data of the updated item
* type: object
* $ref: "#/components/schemas/Permissions"
* required:
* - id
* responses:
* 200:
* description: The item data was successfully updated
* content:
* application/json:
* schema:
* $ref: "#/components/schemas/Permissions"
* 400:
* description: Invalid ID supplied
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Item not found
* 500:
* description: Some server error
*/
router.put('/:id', wrapAsync(async (req, res) => {
await PermissionsService.update(req.body.data, req.body.id, req.currentUser);
const payload = true;
res.status(200).send(payload);
}));
/**
* @swagger
* /api/permissions/{id}:
* delete:
* security:
* - bearerAuth: []
* tags: [Permissions]
* summary: Delete the selected item
* description: Delete the selected item
* parameters:
* - in: path
* name: id
* description: Item ID to delete
* required: true
* schema:
* type: string
* responses:
* 200:
* description: The item was successfully deleted
* content:
* application/json:
* schema:
* $ref: "#/components/schemas/Permissions"
* 400:
* description: Invalid ID supplied
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Item not found
* 500:
* description: Some server error
*/
router.delete('/:id', wrapAsync(async (req, res) => {
await PermissionsService.remove(req.params.id, req.currentUser);
const payload = true;
res.status(200).send(payload);
}));
/**
* @swagger
* /api/permissions/deleteByIds:
* post:
* security:
* - bearerAuth: []
* tags: [Permissions]
* summary: Delete the selected item list
* description: Delete the selected item list
* requestBody:
* required: true
* content:
* application/json:
* schema:
* properties:
* ids:
* description: IDs of the updated items
* type: array
* responses:
* 200:
* description: The items was successfully deleted
* content:
* application/json:
* schema:
* $ref: "#/components/schemas/Permissions"
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Items not found
* 500:
* description: Some server error
*/
router.post('/deleteByIds', wrapAsync(async (req, res) => {
await PermissionsService.deleteByIds(req.body.data, req.currentUser);
const payload = true;
res.status(200).send(payload);
}));
/**
* @swagger
* /api/permissions:
* get:
* security:
* - bearerAuth: []
* tags: [Permissions]
* summary: Get all permissions
* description: Get all permissions
* responses:
* 200:
* description: Permissions list successfully received
* content:
* application/json:
* schema:
* type: array
* items:
* $ref: "#/components/schemas/Permissions"
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Data not found
* 500:
* description: Some server error
*/
router.get('/', wrapAsync(async (req, res) => {
const filetype = req.query.filetype
const currentUser = req.currentUser;
const payload = await PermissionsDBApi.findAll(
req.query, { currentUser }
);
if (filetype && filetype === 'csv') {
const fields = ['id','name',
];
const opts = { fields };
try {
const csv = parse(payload.rows, opts);
res.status(200).attachment(csv);
res.send(csv)
} catch (err) {
console.error(err);
}
} else {
res.status(200).send(payload);
}
}));
/** /**
* @swagger * @swagger
* /api/permissions/count: * /api/permissions:
* get: * post:
* security: * security:
* - bearerAuth: [] * - bearerAuth: []
* tags: [Permissions] * tags: [Permissions]
* summary: Count all permissions * summary: Add new item
* description: Count all permissions * description: Add new item
* requestBody:
* required: true
* content:
* application/json:
* schema:
* properties:
* data:
* description: Data of the updated item
* type: object
* $ref: "#/components/schemas/Permissions"
* responses: * responses:
* 200: * 200:
* description: Permissions count successfully received * description: The item was successfully added
* content: * content:
* application/json: * application/json:
* schema: * schema:
* type: array * $ref: "#/components/schemas/Permissions"
* items:
* $ref: "#/components/schemas/Permissions"
* 401: * 401:
* $ref: "#/components/responses/UnauthorizedError" * $ref: "#/components/responses/UnauthorizedError"
* 404: * 405:
* description: Data not found * description: Invalid input data
* 500: * 500:
* description: Some server error * description: Some server error
*/
router.get('/count', wrapAsync(async (req, res) => {
const currentUser = req.currentUser;
const payload = await PermissionsDBApi.findAll(
req.query,
null,
{ countOnly: true, currentUser }
);
res.status(200).send(payload);
}));
/**
* @swagger
* /api/permissions/autocomplete:
* get: * get:
* security: * security:
* - bearerAuth: [] * - bearerAuth: []
* tags: [Permissions] * tags: [Permissions]
* summary: Find all permissions that match search criteria * summary: Get all permissions
* description: Find all permissions that match search criteria * description: Get all permissions
* responses: * responses:
* 200: * 200:
* description: Permissions list successfully received * description: Permissions list successfully received
@ -370,60 +75,110 @@ router.get('/count', wrapAsync(async (req, res) => {
* 500: * 500:
* description: Some server error * description: Some server error
*/ */
router.get('/autocomplete', async (req, res) => {
const payload = await PermissionsDBApi.findAllAutocomplete(
req.query.query,
req.query.limit,
req.query.offset,
);
res.status(200).send(payload);
});
/** /**
* @swagger * @swagger
* /api/permissions/{id}: * /api/permissions/{id}:
* get: * put:
* security: * security:
* - bearerAuth: [] * - bearerAuth: []
* tags: [Permissions] * tags: [Permissions]
* summary: Get selected item * summary: Update the data of the selected item
* description: Get selected item * description: Update the data of the selected item
* parameters: * parameters:
* - in: path * - in: path
* name: id * name: id
* description: ID of item to get * description: Item ID to update
* required: true * required: true
* schema: * schema:
* type: string * type: string
* responses: * requestBody:
* 200: * description: Set new item data
* description: Selected item successfully received * required: true
* content: * content:
* application/json: * application/json:
* schema: * schema:
* $ref: "#/components/schemas/Permissions" * properties:
* 400: * id:
* description: Invalid ID supplied * description: ID of the updated item
* 401: * type: string
* $ref: "#/components/responses/UnauthorizedError" * data:
* 404: * description: Data of the updated item
* description: Item not found * type: object
* 500: * $ref: "#/components/schemas/Permissions"
* description: Some server error * required:
*/ * - id
router.get('/:id', wrapAsync(async (req, res) => { * responses:
const payload = await PermissionsDBApi.findBy( * 200:
{ id: req.params.id }, * description: The item data was successfully updated
); * content:
* application/json:
* schema:
* $ref: "#/components/schemas/Permissions"
* 400:
* description: Invalid ID supplied
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Item not found
* 500:
* description: Some server error
* delete:
* security:
* - bearerAuth: []
* tags: [Permissions]
* summary: Delete the selected item
* description: Delete the selected item
* parameters:
* - in: path
* name: id
* description: Item ID to delete
* required: true
* schema:
* type: string
* responses:
* 200:
* description: The item was successfully deleted
* content:
* application/json:
* schema:
* $ref: "#/components/schemas/Permissions"
* 400:
* description: Invalid ID supplied
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Item not found
* 500:
* description: Some server error
* get:
* security:
* - bearerAuth: []
* tags: [Permissions]
* summary: Get selected item
* description: Get selected item
* parameters:
* - in: path
* name: id
* description: ID of item to get
* required: true
* schema:
* type: string
* responses:
* 200:
* description: Selected item successfully received
* content:
* application/json:
* schema:
* $ref: "#/components/schemas/Permissions"
* 400:
* description: Invalid ID supplied
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Item not found
* 500:
* description: Some server error
*/
module.exports = createEntityRouter('permissions', PermissionsService, PermissionsDBApi);
res.status(200).send(payload);
}));
router.use('/', require('../helpers').commonErrorHandler);
module.exports = router;

View File

@ -1,22 +1,6 @@
const express = require('express');
const Presigned_url_requestsService = require('../services/presigned_url_requests'); const Presigned_url_requestsService = require('../services/presigned_url_requests');
const Presigned_url_requestsDBApi = require('../db/api/presigned_url_requests'); const Presigned_url_requestsDBApi = require('../db/api/presigned_url_requests');
const wrapAsync = require('../helpers').wrapAsync; const { createEntityRouter } = require('../factories/router.factory');
const router = express.Router();
const { parse } = require('json2csv');
const {
checkCrudPermissions,
} = require('../middlewares/check-permissions');
router.use(checkCrudPermissions('presigned_url_requests'));
/** /**
* @swagger * @swagger
@ -25,24 +9,17 @@ router.use(checkCrudPermissions('presigned_url_requests'));
* Presigned_url_requests: * Presigned_url_requests:
* type: object * type: object
* properties: * properties:
* requested_key: * requested_key:
* type: string * type: string
* default: requested_key
* mime_type: * mime_type:
* type: string * type: string
* default: mime_type
* status: * status:
* type: string * type: string
* default: status
* requested_size_mb: * requested_size_mb:
* type: integer * type: number
* format: int64 * expires_at:
* type: string
* * format: date-time
*
*/ */
/** /**
@ -52,389 +29,118 @@ router.use(checkCrudPermissions('presigned_url_requests'));
* description: The Presigned_url_requests managing API * description: The Presigned_url_requests managing API
*/ */
/**
* @swagger
* /api/presigned_url_requests:
* post:
* security:
* - bearerAuth: []
* tags: [Presigned_url_requests]
* summary: Add new item
* description: Add new item
* requestBody:
* required: true
* content:
* application/json:
* schema:
* properties:
* data:
* description: Data of the updated item
* type: object
* $ref: "#/components/schemas/Presigned_url_requests"
* responses:
* 200:
* description: The item was successfully added
* content:
* application/json:
* schema:
* $ref: "#/components/schemas/Presigned_url_requests"
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 405:
* description: Invalid input data
* 500:
* description: Some server error
*/
router.post('/', wrapAsync(async (req, res) => {
const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`;
const link = new URL(referer);
await Presigned_url_requestsService.create(req.body.data, req.currentUser, true, link.host);
const payload = true;
res.status(200).send(payload);
}));
/**
* @swagger
* /api/budgets/bulk-import:
* post:
* security:
* - bearerAuth: []
* tags: [Presigned_url_requests]
* summary: Bulk import items
* description: Bulk import items
* requestBody:
* required: true
* content:
* application/json:
* schema:
* properties:
* data:
* description: Data of the updated items
* type: array
* items:
* $ref: "#/components/schemas/Presigned_url_requests"
* responses:
* 200:
* description: The items were successfully imported
* content:
* application/json:
* schema:
* $ref: "#/components/schemas/Presigned_url_requests"
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 405:
* description: Invalid input data
* 500:
* description: Some server error
*
*/
router.post('/bulk-import', wrapAsync(async (req, res) => {
const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`;
const link = new URL(referer);
await Presigned_url_requestsService.bulkImport(req, res, true, link.host);
const payload = true;
res.status(200).send(payload);
}));
/**
* @swagger
* /api/presigned_url_requests/{id}:
* put:
* security:
* - bearerAuth: []
* tags: [Presigned_url_requests]
* summary: Update the data of the selected item
* description: Update the data of the selected item
* parameters:
* - in: path
* name: id
* description: Item ID to update
* required: true
* schema:
* type: string
* requestBody:
* description: Set new item data
* required: true
* content:
* application/json:
* schema:
* properties:
* id:
* description: ID of the updated item
* type: string
* data:
* description: Data of the updated item
* type: object
* $ref: "#/components/schemas/Presigned_url_requests"
* required:
* - id
* responses:
* 200:
* description: The item data was successfully updated
* content:
* application/json:
* schema:
* $ref: "#/components/schemas/Presigned_url_requests"
* 400:
* description: Invalid ID supplied
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Item not found
* 500:
* description: Some server error
*/
router.put('/:id', wrapAsync(async (req, res) => {
await Presigned_url_requestsService.update(req.body.data, req.body.id, req.currentUser);
const payload = true;
res.status(200).send(payload);
}));
/**
* @swagger
* /api/presigned_url_requests/{id}:
* delete:
* security:
* - bearerAuth: []
* tags: [Presigned_url_requests]
* summary: Delete the selected item
* description: Delete the selected item
* parameters:
* - in: path
* name: id
* description: Item ID to delete
* required: true
* schema:
* type: string
* responses:
* 200:
* description: The item was successfully deleted
* content:
* application/json:
* schema:
* $ref: "#/components/schemas/Presigned_url_requests"
* 400:
* description: Invalid ID supplied
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Item not found
* 500:
* description: Some server error
*/
router.delete('/:id', wrapAsync(async (req, res) => {
await Presigned_url_requestsService.remove(req.params.id, req.currentUser);
const payload = true;
res.status(200).send(payload);
}));
/**
* @swagger
* /api/presigned_url_requests/deleteByIds:
* post:
* security:
* - bearerAuth: []
* tags: [Presigned_url_requests]
* summary: Delete the selected item list
* description: Delete the selected item list
* requestBody:
* required: true
* content:
* application/json:
* schema:
* properties:
* ids:
* description: IDs of the updated items
* type: array
* responses:
* 200:
* description: The items was successfully deleted
* content:
* application/json:
* schema:
* $ref: "#/components/schemas/Presigned_url_requests"
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Items not found
* 500:
* description: Some server error
*/
router.post('/deleteByIds', wrapAsync(async (req, res) => {
await Presigned_url_requestsService.deleteByIds(req.body.data, req.currentUser);
const payload = true;
res.status(200).send(payload);
}));
/**
* @swagger
* /api/presigned_url_requests:
* get:
* security:
* - bearerAuth: []
* tags: [Presigned_url_requests]
* summary: Get all presigned_url_requests
* description: Get all presigned_url_requests
* responses:
* 200:
* description: Presigned_url_requests list successfully received
* content:
* application/json:
* schema:
* type: array
* items:
* $ref: "#/components/schemas/Presigned_url_requests"
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Data not found
* 500:
* description: Some server error
*/
router.get('/', wrapAsync(async (req, res) => {
const filetype = req.query.filetype
const currentUser = req.currentUser;
const payload = await Presigned_url_requestsDBApi.findAll(
req.query, { currentUser }
);
if (filetype && filetype === 'csv') {
const fields = ['id','requested_key','mime_type','status',
'requested_size_mb',
'expires_at',
];
const opts = { fields };
try {
const csv = parse(payload.rows, opts);
res.status(200).attachment(csv);
res.send(csv)
} catch (err) {
console.error(err);
}
} else {
res.status(200).send(payload);
}
}));
/** /**
* @swagger * @swagger
* /api/presigned_url_requests/count: * /api/presigned_url_requests:
* get: * post:
* security: * security:
* - bearerAuth: [] * - bearerAuth: []
* tags: [Presigned_url_requests] * tags: [Presigned_url_requests]
* summary: Count all presigned_url_requests * summary: Add new item
* description: Count all presigned_url_requests * requestBody:
* required: true
* content:
* application/json:
* schema:
* properties:
* data:
* type: object
* $ref: "#/components/schemas/Presigned_url_requests"
* responses: * responses:
* 200: * 200:
* description: Presigned_url_requests count successfully received * description: The item was successfully added
* content:
* application/json:
* schema:
* type: array
* items:
* $ref: "#/components/schemas/Presigned_url_requests"
* 401: * 401:
* $ref: "#/components/responses/UnauthorizedError" * $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Data not found
* 500: * 500:
* description: Some server error * description: Some server error
*/
router.get('/count', wrapAsync(async (req, res) => {
const currentUser = req.currentUser;
const payload = await Presigned_url_requestsDBApi.findAll(
req.query,
null,
{ countOnly: true, currentUser }
);
res.status(200).send(payload);
}));
/**
* @swagger
* /api/presigned_url_requests/autocomplete:
* get: * get:
* security: * security:
* - bearerAuth: [] * - bearerAuth: []
* tags: [Presigned_url_requests] * tags: [Presigned_url_requests]
* summary: Find all presigned_url_requests that match search criteria * summary: Get all presigned_url_requests
* description: Find all presigned_url_requests that match search criteria
* responses: * responses:
* 200: * 200:
* description: Presigned_url_requests list successfully received * description: Presigned_url_requests list successfully received
* content:
* application/json:
* schema:
* type: array
* items:
* $ref: "#/components/schemas/Presigned_url_requests"
* 401: * 401:
* $ref: "#/components/responses/UnauthorizedError" * $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Data not found
* 500: * 500:
* description: Some server error * description: Some server error
*/ */
router.get('/autocomplete', async (req, res) => {
const payload = await Presigned_url_requestsDBApi.findAllAutocomplete(
req.query.query,
req.query.limit,
req.query.offset,
);
res.status(200).send(payload);
});
/** /**
* @swagger * @swagger
* /api/presigned_url_requests/{id}: * /api/presigned_url_requests/{id}:
* get: * put:
* security: * security:
* - bearerAuth: [] * - bearerAuth: []
* tags: [Presigned_url_requests] * tags: [Presigned_url_requests]
* summary: Get selected item * summary: Update the selected item
* description: Get selected item * parameters:
* parameters: * - in: path
* - in: path * name: id
* name: id * required: true
* description: ID of item to get * schema:
* required: true * type: string
* schema: * requestBody:
* type: string * required: true
* responses: * content:
* 200: * application/json:
* description: Selected item successfully received * schema:
* content: * properties:
* application/json: * id:
* schema: * type: string
* $ref: "#/components/schemas/Presigned_url_requests" * data:
* 400: * type: object
* description: Invalid ID supplied * $ref: "#/components/schemas/Presigned_url_requests"
* 401: * responses:
* $ref: "#/components/responses/UnauthorizedError" * 200:
* 404: * description: The item was successfully updated
* description: Item not found * 401:
* 500: * $ref: "#/components/responses/UnauthorizedError"
* description: Some server error * 404:
*/ * description: Item not found
router.get('/:id', wrapAsync(async (req, res) => { * 500:
const payload = await Presigned_url_requestsDBApi.findBy( * description: Some server error
{ id: req.params.id }, * delete:
); * security:
* - bearerAuth: []
* tags: [Presigned_url_requests]
* summary: Delete the selected item
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: string
* responses:
* 200:
* description: The item was successfully deleted
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Item not found
* 500:
* description: Some server error
* get:
* security:
* - bearerAuth: []
* tags: [Presigned_url_requests]
* summary: Get selected item
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: string
* responses:
* 200:
* description: Selected item successfully received
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Item not found
* 500:
* description: Some server error
*/
module.exports = createEntityRouter('presigned_url_requests', Presigned_url_requestsService, Presigned_url_requestsDBApi);
res.status(200).send(payload);
}));
router.use('/', require('../helpers').commonErrorHandler);
module.exports = router;

View File

@ -1,22 +1,6 @@
const express = require('express');
const Project_audio_tracksService = require('../services/project_audio_tracks'); const Project_audio_tracksService = require('../services/project_audio_tracks');
const Project_audio_tracksDBApi = require('../db/api/project_audio_tracks'); const Project_audio_tracksDBApi = require('../db/api/project_audio_tracks');
const wrapAsync = require('../helpers').wrapAsync; const { createEntityRouter } = require('../factories/router.factory');
const router = express.Router();
const { parse } = require('json2csv');
const {
checkCrudPermissions,
} = require('../middlewares/check-permissions');
router.use(checkCrudPermissions('project_audio_tracks'));
/** /**
* @swagger * @swagger
@ -25,29 +9,18 @@ router.use(checkCrudPermissions('project_audio_tracks'));
* Project_audio_tracks: * Project_audio_tracks:
* type: object * type: object
* properties: * properties:
* source_key: * source_key:
* type: string * type: string
* default: source_key
* name: * name:
* type: string * type: string
* default: name
* slug: * slug:
* type: string * type: string
* default: slug
* url: * url:
* type: string * type: string
* default: url
* sort_order: * sort_order:
* type: integer * type: integer
* format: int64
* volume: * volume:
* type: integer * type: number
* format: int64
*
*/ */
/** /**
@ -57,393 +30,118 @@ router.use(checkCrudPermissions('project_audio_tracks'));
* description: The Project_audio_tracks managing API * description: The Project_audio_tracks managing API
*/ */
/**
* @swagger
* /api/project_audio_tracks:
* post:
* security:
* - bearerAuth: []
* tags: [Project_audio_tracks]
* summary: Add new item
* description: Add new item
* requestBody:
* required: true
* content:
* application/json:
* schema:
* properties:
* data:
* description: Data of the updated item
* type: object
* $ref: "#/components/schemas/Project_audio_tracks"
* responses:
* 200:
* description: The item was successfully added
* content:
* application/json:
* schema:
* $ref: "#/components/schemas/Project_audio_tracks"
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 405:
* description: Invalid input data
* 500:
* description: Some server error
*/
router.post('/', wrapAsync(async (req, res) => {
const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`;
const link = new URL(referer);
await Project_audio_tracksService.create(req.body.data, req.currentUser, true, link.host);
const payload = true;
res.status(200).send(payload);
}));
/**
* @swagger
* /api/budgets/bulk-import:
* post:
* security:
* - bearerAuth: []
* tags: [Project_audio_tracks]
* summary: Bulk import items
* description: Bulk import items
* requestBody:
* required: true
* content:
* application/json:
* schema:
* properties:
* data:
* description: Data of the updated items
* type: array
* items:
* $ref: "#/components/schemas/Project_audio_tracks"
* responses:
* 200:
* description: The items were successfully imported
* content:
* application/json:
* schema:
* $ref: "#/components/schemas/Project_audio_tracks"
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 405:
* description: Invalid input data
* 500:
* description: Some server error
*
*/
router.post('/bulk-import', wrapAsync(async (req, res) => {
const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`;
const link = new URL(referer);
await Project_audio_tracksService.bulkImport(req, res, true, link.host);
const payload = true;
res.status(200).send(payload);
}));
/**
* @swagger
* /api/project_audio_tracks/{id}:
* put:
* security:
* - bearerAuth: []
* tags: [Project_audio_tracks]
* summary: Update the data of the selected item
* description: Update the data of the selected item
* parameters:
* - in: path
* name: id
* description: Item ID to update
* required: true
* schema:
* type: string
* requestBody:
* description: Set new item data
* required: true
* content:
* application/json:
* schema:
* properties:
* id:
* description: ID of the updated item
* type: string
* data:
* description: Data of the updated item
* type: object
* $ref: "#/components/schemas/Project_audio_tracks"
* required:
* - id
* responses:
* 200:
* description: The item data was successfully updated
* content:
* application/json:
* schema:
* $ref: "#/components/schemas/Project_audio_tracks"
* 400:
* description: Invalid ID supplied
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Item not found
* 500:
* description: Some server error
*/
router.put('/:id', wrapAsync(async (req, res) => {
await Project_audio_tracksService.update(req.body.data, req.body.id, req.currentUser);
const payload = true;
res.status(200).send(payload);
}));
/**
* @swagger
* /api/project_audio_tracks/{id}:
* delete:
* security:
* - bearerAuth: []
* tags: [Project_audio_tracks]
* summary: Delete the selected item
* description: Delete the selected item
* parameters:
* - in: path
* name: id
* description: Item ID to delete
* required: true
* schema:
* type: string
* responses:
* 200:
* description: The item was successfully deleted
* content:
* application/json:
* schema:
* $ref: "#/components/schemas/Project_audio_tracks"
* 400:
* description: Invalid ID supplied
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Item not found
* 500:
* description: Some server error
*/
router.delete('/:id', wrapAsync(async (req, res) => {
await Project_audio_tracksService.remove(req.params.id, req.currentUser);
const payload = true;
res.status(200).send(payload);
}));
/**
* @swagger
* /api/project_audio_tracks/deleteByIds:
* post:
* security:
* - bearerAuth: []
* tags: [Project_audio_tracks]
* summary: Delete the selected item list
* description: Delete the selected item list
* requestBody:
* required: true
* content:
* application/json:
* schema:
* properties:
* ids:
* description: IDs of the updated items
* type: array
* responses:
* 200:
* description: The items was successfully deleted
* content:
* application/json:
* schema:
* $ref: "#/components/schemas/Project_audio_tracks"
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Items not found
* 500:
* description: Some server error
*/
router.post('/deleteByIds', wrapAsync(async (req, res) => {
await Project_audio_tracksService.deleteByIds(req.body.data, req.currentUser);
const payload = true;
res.status(200).send(payload);
}));
/**
* @swagger
* /api/project_audio_tracks:
* get:
* security:
* - bearerAuth: []
* tags: [Project_audio_tracks]
* summary: Get all project_audio_tracks
* description: Get all project_audio_tracks
* responses:
* 200:
* description: Project_audio_tracks list successfully received
* content:
* application/json:
* schema:
* type: array
* items:
* $ref: "#/components/schemas/Project_audio_tracks"
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Data not found
* 500:
* description: Some server error
*/
router.get('/', wrapAsync(async (req, res) => {
const filetype = req.query.filetype
const currentUser = req.currentUser;
const runtimeContext = req.runtimeContext;
const payload = await Project_audio_tracksDBApi.findAll(
req.query, { currentUser, runtimeContext }
);
if (filetype && filetype === 'csv') {
const fields = ['id','source_key','name','slug','url',
'sort_order',
'volume',
];
const opts = { fields };
try {
const csv = parse(payload.rows, opts);
res.status(200).attachment(csv);
res.send(csv)
} catch (err) {
console.error(err);
}
} else {
res.status(200).send(payload);
}
}));
/** /**
* @swagger * @swagger
* /api/project_audio_tracks/count: * /api/project_audio_tracks:
* get: * post:
* security: * security:
* - bearerAuth: [] * - bearerAuth: []
* tags: [Project_audio_tracks] * tags: [Project_audio_tracks]
* summary: Count all project_audio_tracks * summary: Add new item
* description: Count all project_audio_tracks * requestBody:
* required: true
* content:
* application/json:
* schema:
* properties:
* data:
* type: object
* $ref: "#/components/schemas/Project_audio_tracks"
* responses: * responses:
* 200: * 200:
* description: Project_audio_tracks count successfully received * description: The item was successfully added
* content:
* application/json:
* schema:
* type: array
* items:
* $ref: "#/components/schemas/Project_audio_tracks"
* 401: * 401:
* $ref: "#/components/responses/UnauthorizedError" * $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Data not found
* 500: * 500:
* description: Some server error * description: Some server error
*/
router.get('/count', wrapAsync(async (req, res) => {
const currentUser = req.currentUser;
const runtimeContext = req.runtimeContext;
const payload = await Project_audio_tracksDBApi.findAll(
req.query,
null,
{ countOnly: true, currentUser, runtimeContext }
);
res.status(200).send(payload);
}));
/**
* @swagger
* /api/project_audio_tracks/autocomplete:
* get: * get:
* security: * security:
* - bearerAuth: [] * - bearerAuth: []
* tags: [Project_audio_tracks] * tags: [Project_audio_tracks]
* summary: Find all project_audio_tracks that match search criteria * summary: Get all project_audio_tracks
* description: Find all project_audio_tracks that match search criteria
* responses: * responses:
* 200: * 200:
* description: Project_audio_tracks list successfully received * description: Project_audio_tracks list successfully received
* content:
* application/json:
* schema:
* type: array
* items:
* $ref: "#/components/schemas/Project_audio_tracks"
* 401: * 401:
* $ref: "#/components/responses/UnauthorizedError" * $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Data not found
* 500: * 500:
* description: Some server error * description: Some server error
*/ */
router.get('/autocomplete', async (req, res) => {
const payload = await Project_audio_tracksDBApi.findAllAutocomplete(
req.query.query,
req.query.limit,
req.query.offset,
);
res.status(200).send(payload);
});
/** /**
* @swagger * @swagger
* /api/project_audio_tracks/{id}: * /api/project_audio_tracks/{id}:
* get: * put:
* security: * security:
* - bearerAuth: [] * - bearerAuth: []
* tags: [Project_audio_tracks] * tags: [Project_audio_tracks]
* summary: Get selected item * summary: Update the selected item
* description: Get selected item * parameters:
* parameters: * - in: path
* - in: path * name: id
* name: id * required: true
* description: ID of item to get * schema:
* required: true * type: string
* schema: * requestBody:
* type: string * required: true
* responses: * content:
* 200: * application/json:
* description: Selected item successfully received * schema:
* content: * properties:
* application/json: * id:
* schema: * type: string
* $ref: "#/components/schemas/Project_audio_tracks" * data:
* 400: * type: object
* description: Invalid ID supplied * $ref: "#/components/schemas/Project_audio_tracks"
* 401: * responses:
* $ref: "#/components/responses/UnauthorizedError" * 200:
* 404: * description: The item was successfully updated
* description: Item not found * 401:
* 500: * $ref: "#/components/responses/UnauthorizedError"
* description: Some server error * 404:
*/ * description: Item not found
router.get('/:id', wrapAsync(async (req, res) => { * 500:
const runtimeContext = req.runtimeContext; * description: Some server error
const payload = await Project_audio_tracksDBApi.findBy( * delete:
{ id: req.params.id }, * security:
{ runtimeContext }, * - bearerAuth: []
); * tags: [Project_audio_tracks]
* summary: Delete the selected item
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: string
* responses:
* 200:
* description: The item was successfully deleted
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Item not found
* 500:
* description: Some server error
* get:
* security:
* - bearerAuth: []
* tags: [Project_audio_tracks]
* summary: Get selected item
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: string
* responses:
* 200:
* description: Selected item successfully received
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Item not found
* 500:
* description: Some server error
*/
module.exports = createEntityRouter('project_audio_tracks', Project_audio_tracksService, Project_audio_tracksDBApi);
res.status(200).send(payload);
}));
router.use('/', require('../helpers').commonErrorHandler);
module.exports = router;

View File

@ -1,22 +1,6 @@
const express = require('express');
const Project_membershipsService = require('../services/project_memberships'); const Project_membershipsService = require('../services/project_memberships');
const Project_membershipsDBApi = require('../db/api/project_memberships'); const Project_membershipsDBApi = require('../db/api/project_memberships');
const wrapAsync = require('../helpers').wrapAsync; const { createEntityRouter } = require('../factories/router.factory');
const router = express.Router();
const { parse } = require('json2csv');
const {
checkCrudPermissions,
} = require('../middlewares/check-permissions');
router.use(checkCrudPermissions('project_memberships'));
/** /**
* @swagger * @swagger
@ -25,11 +9,12 @@ router.use(checkCrudPermissions('project_memberships'));
* Project_memberships: * Project_memberships:
* type: object * type: object
* properties: * properties:
* invited_at:
* type: string
* format: date-time
* accepted_at:
* * type: string
* format: date-time
*/ */
/** /**
@ -39,389 +24,118 @@ router.use(checkCrudPermissions('project_memberships'));
* description: The Project_memberships managing API * description: The Project_memberships managing API
*/ */
/**
* @swagger
* /api/project_memberships:
* post:
* security:
* - bearerAuth: []
* tags: [Project_memberships]
* summary: Add new item
* description: Add new item
* requestBody:
* required: true
* content:
* application/json:
* schema:
* properties:
* data:
* description: Data of the updated item
* type: object
* $ref: "#/components/schemas/Project_memberships"
* responses:
* 200:
* description: The item was successfully added
* content:
* application/json:
* schema:
* $ref: "#/components/schemas/Project_memberships"
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 405:
* description: Invalid input data
* 500:
* description: Some server error
*/
router.post('/', wrapAsync(async (req, res) => {
const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`;
const link = new URL(referer);
await Project_membershipsService.create(req.body.data, req.currentUser, true, link.host);
const payload = true;
res.status(200).send(payload);
}));
/**
* @swagger
* /api/budgets/bulk-import:
* post:
* security:
* - bearerAuth: []
* tags: [Project_memberships]
* summary: Bulk import items
* description: Bulk import items
* requestBody:
* required: true
* content:
* application/json:
* schema:
* properties:
* data:
* description: Data of the updated items
* type: array
* items:
* $ref: "#/components/schemas/Project_memberships"
* responses:
* 200:
* description: The items were successfully imported
* content:
* application/json:
* schema:
* $ref: "#/components/schemas/Project_memberships"
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 405:
* description: Invalid input data
* 500:
* description: Some server error
*
*/
router.post('/bulk-import', wrapAsync(async (req, res) => {
const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`;
const link = new URL(referer);
await Project_membershipsService.bulkImport(req, res, true, link.host);
const payload = true;
res.status(200).send(payload);
}));
/**
* @swagger
* /api/project_memberships/{id}:
* put:
* security:
* - bearerAuth: []
* tags: [Project_memberships]
* summary: Update the data of the selected item
* description: Update the data of the selected item
* parameters:
* - in: path
* name: id
* description: Item ID to update
* required: true
* schema:
* type: string
* requestBody:
* description: Set new item data
* required: true
* content:
* application/json:
* schema:
* properties:
* id:
* description: ID of the updated item
* type: string
* data:
* description: Data of the updated item
* type: object
* $ref: "#/components/schemas/Project_memberships"
* required:
* - id
* responses:
* 200:
* description: The item data was successfully updated
* content:
* application/json:
* schema:
* $ref: "#/components/schemas/Project_memberships"
* 400:
* description: Invalid ID supplied
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Item not found
* 500:
* description: Some server error
*/
router.put('/:id', wrapAsync(async (req, res) => {
await Project_membershipsService.update(req.body.data, req.body.id, req.currentUser);
const payload = true;
res.status(200).send(payload);
}));
/**
* @swagger
* /api/project_memberships/{id}:
* delete:
* security:
* - bearerAuth: []
* tags: [Project_memberships]
* summary: Delete the selected item
* description: Delete the selected item
* parameters:
* - in: path
* name: id
* description: Item ID to delete
* required: true
* schema:
* type: string
* responses:
* 200:
* description: The item was successfully deleted
* content:
* application/json:
* schema:
* $ref: "#/components/schemas/Project_memberships"
* 400:
* description: Invalid ID supplied
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Item not found
* 500:
* description: Some server error
*/
router.delete('/:id', wrapAsync(async (req, res) => {
await Project_membershipsService.remove(req.params.id, req.currentUser);
const payload = true;
res.status(200).send(payload);
}));
/**
* @swagger
* /api/project_memberships/deleteByIds:
* post:
* security:
* - bearerAuth: []
* tags: [Project_memberships]
* summary: Delete the selected item list
* description: Delete the selected item list
* requestBody:
* required: true
* content:
* application/json:
* schema:
* properties:
* ids:
* description: IDs of the updated items
* type: array
* responses:
* 200:
* description: The items was successfully deleted
* content:
* application/json:
* schema:
* $ref: "#/components/schemas/Project_memberships"
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Items not found
* 500:
* description: Some server error
*/
router.post('/deleteByIds', wrapAsync(async (req, res) => {
await Project_membershipsService.deleteByIds(req.body.data, req.currentUser);
const payload = true;
res.status(200).send(payload);
}));
/**
* @swagger
* /api/project_memberships:
* get:
* security:
* - bearerAuth: []
* tags: [Project_memberships]
* summary: Get all project_memberships
* description: Get all project_memberships
* responses:
* 200:
* description: Project_memberships list successfully received
* content:
* application/json:
* schema:
* type: array
* items:
* $ref: "#/components/schemas/Project_memberships"
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Data not found
* 500:
* description: Some server error
*/
router.get('/', wrapAsync(async (req, res) => {
const filetype = req.query.filetype
const currentUser = req.currentUser;
const payload = await Project_membershipsDBApi.findAll(
req.query, { currentUser }
);
if (filetype && filetype === 'csv') {
const fields = ['id',
'invited_at','accepted_at',
];
const opts = { fields };
try {
const csv = parse(payload.rows, opts);
res.status(200).attachment(csv);
res.send(csv)
} catch (err) {
console.error(err);
}
} else {
res.status(200).send(payload);
}
}));
/** /**
* @swagger * @swagger
* /api/project_memberships/count: * /api/project_memberships:
* get: * post:
* security: * security:
* - bearerAuth: [] * - bearerAuth: []
* tags: [Project_memberships] * tags: [Project_memberships]
* summary: Count all project_memberships * summary: Add new item
* description: Count all project_memberships * requestBody:
* required: true
* content:
* application/json:
* schema:
* properties:
* data:
* type: object
* $ref: "#/components/schemas/Project_memberships"
* responses: * responses:
* 200: * 200:
* description: Project_memberships count successfully received * description: The item was successfully added
* content:
* application/json:
* schema:
* type: array
* items:
* $ref: "#/components/schemas/Project_memberships"
* 401: * 401:
* $ref: "#/components/responses/UnauthorizedError" * $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Data not found
* 500: * 500:
* description: Some server error * description: Some server error
*/
router.get('/count', wrapAsync(async (req, res) => {
const currentUser = req.currentUser;
const payload = await Project_membershipsDBApi.findAll(
req.query,
null,
{ countOnly: true, currentUser }
);
res.status(200).send(payload);
}));
/**
* @swagger
* /api/project_memberships/autocomplete:
* get: * get:
* security: * security:
* - bearerAuth: [] * - bearerAuth: []
* tags: [Project_memberships] * tags: [Project_memberships]
* summary: Find all project_memberships that match search criteria * summary: Get all project_memberships
* description: Find all project_memberships that match search criteria
* responses: * responses:
* 200: * 200:
* description: Project_memberships list successfully received * description: Project_memberships list successfully received
* content:
* application/json:
* schema:
* type: array
* items:
* $ref: "#/components/schemas/Project_memberships"
* 401: * 401:
* $ref: "#/components/responses/UnauthorizedError" * $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Data not found
* 500: * 500:
* description: Some server error * description: Some server error
*/ */
router.get('/autocomplete', async (req, res) => {
const payload = await Project_membershipsDBApi.findAllAutocomplete(
req.query.query,
req.query.limit,
req.query.offset,
);
res.status(200).send(payload);
});
/** /**
* @swagger * @swagger
* /api/project_memberships/{id}: * /api/project_memberships/{id}:
* get: * put:
* security: * security:
* - bearerAuth: [] * - bearerAuth: []
* tags: [Project_memberships] * tags: [Project_memberships]
* summary: Get selected item * summary: Update the selected item
* description: Get selected item * parameters:
* parameters: * - in: path
* - in: path * name: id
* name: id * required: true
* description: ID of item to get * schema:
* required: true * type: string
* schema: * requestBody:
* type: string * required: true
* responses: * content:
* 200: * application/json:
* description: Selected item successfully received * schema:
* content: * properties:
* application/json: * id:
* schema: * type: string
* $ref: "#/components/schemas/Project_memberships" * data:
* 400: * type: object
* description: Invalid ID supplied * $ref: "#/components/schemas/Project_memberships"
* 401: * responses:
* $ref: "#/components/responses/UnauthorizedError" * 200:
* 404: * description: The item was successfully updated
* description: Item not found * 401:
* 500: * $ref: "#/components/responses/UnauthorizedError"
* description: Some server error * 404:
*/ * description: Item not found
router.get('/:id', wrapAsync(async (req, res) => { * 500:
const payload = await Project_membershipsDBApi.findBy( * description: Some server error
{ id: req.params.id }, * delete:
); * security:
* - bearerAuth: []
* tags: [Project_memberships]
* summary: Delete the selected item
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: string
* responses:
* 200:
* description: The item was successfully deleted
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Item not found
* 500:
* description: Some server error
* get:
* security:
* - bearerAuth: []
* tags: [Project_memberships]
* summary: Get selected item
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: string
* responses:
* 200:
* description: Selected item successfully received
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Item not found
* 500:
* description: Some server error
*/
module.exports = createEntityRouter('project_memberships', Project_membershipsService, Project_membershipsDBApi);
res.status(200).send(payload);
}));
router.use('/', require('../helpers').commonErrorHandler);
module.exports = router;

View File

@ -1,22 +1,6 @@
const express = require('express');
const Publish_eventsService = require('../services/publish_events'); const Publish_eventsService = require('../services/publish_events');
const Publish_eventsDBApi = require('../db/api/publish_events'); const Publish_eventsDBApi = require('../db/api/publish_events');
const wrapAsync = require('../helpers').wrapAsync; const { createEntityRouter } = require('../factories/router.factory');
const router = express.Router();
const { parse } = require('json2csv');
const {
checkCrudPermissions,
} = require('../middlewares/check-permissions');
router.use(checkCrudPermissions('publish_events'));
/** /**
* @swagger * @swagger
@ -25,25 +9,24 @@ router.use(checkCrudPermissions('publish_events'));
* Publish_events: * Publish_events:
* type: object * type: object
* properties: * properties:
* title:
* type: string
* description:
* type: string
* error_message: * error_message:
* type: string * type: string
* default: error_message
* pages_copied: * pages_copied:
* type: integer * type: integer
* format: int64
* transitions_copied: * transitions_copied:
* type: integer * type: integer
* format: int64
* audios_copied: * audios_copied:
* type: integer * type: integer
* format: int64 * started_at:
* type: string
* format: date-time
* * finished_at:
* * type: string
* * format: date-time
*/ */
/** /**
@ -53,389 +36,118 @@ router.use(checkCrudPermissions('publish_events'));
* description: The Publish_events managing API * description: The Publish_events managing API
*/ */
/**
* @swagger
* /api/publish_events:
* post:
* security:
* - bearerAuth: []
* tags: [Publish_events]
* summary: Add new item
* description: Add new item
* requestBody:
* required: true
* content:
* application/json:
* schema:
* properties:
* data:
* description: Data of the updated item
* type: object
* $ref: "#/components/schemas/Publish_events"
* responses:
* 200:
* description: The item was successfully added
* content:
* application/json:
* schema:
* $ref: "#/components/schemas/Publish_events"
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 405:
* description: Invalid input data
* 500:
* description: Some server error
*/
router.post('/', wrapAsync(async (req, res) => {
const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`;
const link = new URL(referer);
await Publish_eventsService.create(req.body.data, req.currentUser, true, link.host);
const payload = true;
res.status(200).send(payload);
}));
/**
* @swagger
* /api/budgets/bulk-import:
* post:
* security:
* - bearerAuth: []
* tags: [Publish_events]
* summary: Bulk import items
* description: Bulk import items
* requestBody:
* required: true
* content:
* application/json:
* schema:
* properties:
* data:
* description: Data of the updated items
* type: array
* items:
* $ref: "#/components/schemas/Publish_events"
* responses:
* 200:
* description: The items were successfully imported
* content:
* application/json:
* schema:
* $ref: "#/components/schemas/Publish_events"
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 405:
* description: Invalid input data
* 500:
* description: Some server error
*
*/
router.post('/bulk-import', wrapAsync(async (req, res) => {
const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`;
const link = new URL(referer);
await Publish_eventsService.bulkImport(req, res, true, link.host);
const payload = true;
res.status(200).send(payload);
}));
/**
* @swagger
* /api/publish_events/{id}:
* put:
* security:
* - bearerAuth: []
* tags: [Publish_events]
* summary: Update the data of the selected item
* description: Update the data of the selected item
* parameters:
* - in: path
* name: id
* description: Item ID to update
* required: true
* schema:
* type: string
* requestBody:
* description: Set new item data
* required: true
* content:
* application/json:
* schema:
* properties:
* id:
* description: ID of the updated item
* type: string
* data:
* description: Data of the updated item
* type: object
* $ref: "#/components/schemas/Publish_events"
* required:
* - id
* responses:
* 200:
* description: The item data was successfully updated
* content:
* application/json:
* schema:
* $ref: "#/components/schemas/Publish_events"
* 400:
* description: Invalid ID supplied
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Item not found
* 500:
* description: Some server error
*/
router.put('/:id', wrapAsync(async (req, res) => {
await Publish_eventsService.update(req.body.data, req.body.id, req.currentUser);
const payload = true;
res.status(200).send(payload);
}));
/**
* @swagger
* /api/publish_events/{id}:
* delete:
* security:
* - bearerAuth: []
* tags: [Publish_events]
* summary: Delete the selected item
* description: Delete the selected item
* parameters:
* - in: path
* name: id
* description: Item ID to delete
* required: true
* schema:
* type: string
* responses:
* 200:
* description: The item was successfully deleted
* content:
* application/json:
* schema:
* $ref: "#/components/schemas/Publish_events"
* 400:
* description: Invalid ID supplied
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Item not found
* 500:
* description: Some server error
*/
router.delete('/:id', wrapAsync(async (req, res) => {
await Publish_eventsService.remove(req.params.id, req.currentUser);
const payload = true;
res.status(200).send(payload);
}));
/**
* @swagger
* /api/publish_events/deleteByIds:
* post:
* security:
* - bearerAuth: []
* tags: [Publish_events]
* summary: Delete the selected item list
* description: Delete the selected item list
* requestBody:
* required: true
* content:
* application/json:
* schema:
* properties:
* ids:
* description: IDs of the updated items
* type: array
* responses:
* 200:
* description: The items was successfully deleted
* content:
* application/json:
* schema:
* $ref: "#/components/schemas/Publish_events"
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Items not found
* 500:
* description: Some server error
*/
router.post('/deleteByIds', wrapAsync(async (req, res) => {
await Publish_eventsService.deleteByIds(req.body.data, req.currentUser);
const payload = true;
res.status(200).send(payload);
}));
/**
* @swagger
* /api/publish_events:
* get:
* security:
* - bearerAuth: []
* tags: [Publish_events]
* summary: Get all publish_events
* description: Get all publish_events
* responses:
* 200:
* description: Publish_events list successfully received
* content:
* application/json:
* schema:
* type: array
* items:
* $ref: "#/components/schemas/Publish_events"
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Data not found
* 500:
* description: Some server error
*/
router.get('/', wrapAsync(async (req, res) => {
const filetype = req.query.filetype
const currentUser = req.currentUser;
const payload = await Publish_eventsDBApi.findAll(
req.query, { currentUser }
);
if (filetype && filetype === 'csv') {
const fields = ['id','title','description','error_message',
'pages_copied','transitions_copied','audios_copied',
'started_at','finished_at',
];
const opts = { fields };
try {
const csv = parse(payload.rows, opts);
res.status(200).attachment(csv);
res.send(csv)
} catch (err) {
console.error(err);
}
} else {
res.status(200).send(payload);
}
}));
/** /**
* @swagger * @swagger
* /api/publish_events/count: * /api/publish_events:
* get: * post:
* security: * security:
* - bearerAuth: [] * - bearerAuth: []
* tags: [Publish_events] * tags: [Publish_events]
* summary: Count all publish_events * summary: Add new item
* description: Count all publish_events * requestBody:
* required: true
* content:
* application/json:
* schema:
* properties:
* data:
* type: object
* $ref: "#/components/schemas/Publish_events"
* responses: * responses:
* 200: * 200:
* description: Publish_events count successfully received * description: The item was successfully added
* content:
* application/json:
* schema:
* type: array
* items:
* $ref: "#/components/schemas/Publish_events"
* 401: * 401:
* $ref: "#/components/responses/UnauthorizedError" * $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Data not found
* 500: * 500:
* description: Some server error * description: Some server error
*/
router.get('/count', wrapAsync(async (req, res) => {
const currentUser = req.currentUser;
const payload = await Publish_eventsDBApi.findAll(
req.query,
null,
{ countOnly: true, currentUser }
);
res.status(200).send(payload);
}));
/**
* @swagger
* /api/publish_events/autocomplete:
* get: * get:
* security: * security:
* - bearerAuth: [] * - bearerAuth: []
* tags: [Publish_events] * tags: [Publish_events]
* summary: Find all publish_events that match search criteria * summary: Get all publish_events
* description: Find all publish_events that match search criteria
* responses: * responses:
* 200: * 200:
* description: Publish_events list successfully received * description: Publish_events list successfully received
* content:
* application/json:
* schema:
* type: array
* items:
* $ref: "#/components/schemas/Publish_events"
* 401: * 401:
* $ref: "#/components/responses/UnauthorizedError" * $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Data not found
* 500: * 500:
* description: Some server error * description: Some server error
*/ */
router.get('/autocomplete', async (req, res) => {
const payload = await Publish_eventsDBApi.findAllAutocomplete(
req.query.query,
req.query.limit,
req.query.offset,
);
res.status(200).send(payload);
});
/** /**
* @swagger * @swagger
* /api/publish_events/{id}: * /api/publish_events/{id}:
* get: * put:
* security: * security:
* - bearerAuth: [] * - bearerAuth: []
* tags: [Publish_events] * tags: [Publish_events]
* summary: Get selected item * summary: Update the selected item
* description: Get selected item * parameters:
* parameters: * - in: path
* - in: path * name: id
* name: id * required: true
* description: ID of item to get * schema:
* required: true * type: string
* schema: * requestBody:
* type: string * required: true
* responses: * content:
* 200: * application/json:
* description: Selected item successfully received * schema:
* content: * properties:
* application/json: * id:
* schema: * type: string
* $ref: "#/components/schemas/Publish_events" * data:
* 400: * type: object
* description: Invalid ID supplied * $ref: "#/components/schemas/Publish_events"
* 401: * responses:
* $ref: "#/components/responses/UnauthorizedError" * 200:
* 404: * description: The item was successfully updated
* description: Item not found * 401:
* 500: * $ref: "#/components/responses/UnauthorizedError"
* description: Some server error * 404:
*/ * description: Item not found
router.get('/:id', wrapAsync(async (req, res) => { * 500:
const payload = await Publish_eventsDBApi.findBy( * description: Some server error
{ id: req.params.id }, * delete:
); * security:
* - bearerAuth: []
* tags: [Publish_events]
* summary: Delete the selected item
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: string
* responses:
* 200:
* description: The item was successfully deleted
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Item not found
* 500:
* description: Some server error
* get:
* security:
* - bearerAuth: []
* tags: [Publish_events]
* summary: Get selected item
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: string
* responses:
* 200:
* description: Selected item successfully received
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Item not found
* 500:
* description: Some server error
*/
module.exports = createEntityRouter('publish_events', Publish_eventsService, Publish_eventsDBApi);
res.status(200).send(payload);
}));
router.use('/', require('../helpers').commonErrorHandler);
module.exports = router;

View File

@ -1,22 +1,6 @@
const express = require('express');
const Pwa_cachesService = require('../services/pwa_caches'); const Pwa_cachesService = require('../services/pwa_caches');
const Pwa_cachesDBApi = require('../db/api/pwa_caches'); const Pwa_cachesDBApi = require('../db/api/pwa_caches');
const wrapAsync = require('../helpers').wrapAsync; const { createEntityRouter } = require('../factories/router.factory');
const router = express.Router();
const { parse } = require('json2csv');
const {
checkCrudPermissions,
} = require('../middlewares/check-permissions');
router.use(checkCrudPermissions('pwa_caches'));
/** /**
* @swagger * @swagger
@ -25,20 +9,15 @@ router.use(checkCrudPermissions('pwa_caches'));
* Pwa_caches: * Pwa_caches:
* type: object * type: object
* properties: * properties:
* cache_version: * cache_version:
* type: string * type: string
* default: cache_version
* manifest_json: * manifest_json:
* type: string * type: string
* default: manifest_json
* asset_list_json: * asset_list_json:
* type: string * type: string
* default: asset_list_json * generated_at:
* type: string
* format: date-time
*
*/ */
/** /**
@ -48,389 +27,118 @@ router.use(checkCrudPermissions('pwa_caches'));
* description: The Pwa_caches managing API * description: The Pwa_caches managing API
*/ */
/**
* @swagger
* /api/pwa_caches:
* post:
* security:
* - bearerAuth: []
* tags: [Pwa_caches]
* summary: Add new item
* description: Add new item
* requestBody:
* required: true
* content:
* application/json:
* schema:
* properties:
* data:
* description: Data of the updated item
* type: object
* $ref: "#/components/schemas/Pwa_caches"
* responses:
* 200:
* description: The item was successfully added
* content:
* application/json:
* schema:
* $ref: "#/components/schemas/Pwa_caches"
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 405:
* description: Invalid input data
* 500:
* description: Some server error
*/
router.post('/', wrapAsync(async (req, res) => {
const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`;
const link = new URL(referer);
await Pwa_cachesService.create(req.body.data, req.currentUser, true, link.host);
const payload = true;
res.status(200).send(payload);
}));
/**
* @swagger
* /api/budgets/bulk-import:
* post:
* security:
* - bearerAuth: []
* tags: [Pwa_caches]
* summary: Bulk import items
* description: Bulk import items
* requestBody:
* required: true
* content:
* application/json:
* schema:
* properties:
* data:
* description: Data of the updated items
* type: array
* items:
* $ref: "#/components/schemas/Pwa_caches"
* responses:
* 200:
* description: The items were successfully imported
* content:
* application/json:
* schema:
* $ref: "#/components/schemas/Pwa_caches"
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 405:
* description: Invalid input data
* 500:
* description: Some server error
*
*/
router.post('/bulk-import', wrapAsync(async (req, res) => {
const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`;
const link = new URL(referer);
await Pwa_cachesService.bulkImport(req, res, true, link.host);
const payload = true;
res.status(200).send(payload);
}));
/**
* @swagger
* /api/pwa_caches/{id}:
* put:
* security:
* - bearerAuth: []
* tags: [Pwa_caches]
* summary: Update the data of the selected item
* description: Update the data of the selected item
* parameters:
* - in: path
* name: id
* description: Item ID to update
* required: true
* schema:
* type: string
* requestBody:
* description: Set new item data
* required: true
* content:
* application/json:
* schema:
* properties:
* id:
* description: ID of the updated item
* type: string
* data:
* description: Data of the updated item
* type: object
* $ref: "#/components/schemas/Pwa_caches"
* required:
* - id
* responses:
* 200:
* description: The item data was successfully updated
* content:
* application/json:
* schema:
* $ref: "#/components/schemas/Pwa_caches"
* 400:
* description: Invalid ID supplied
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Item not found
* 500:
* description: Some server error
*/
router.put('/:id', wrapAsync(async (req, res) => {
await Pwa_cachesService.update(req.body.data, req.body.id, req.currentUser);
const payload = true;
res.status(200).send(payload);
}));
/**
* @swagger
* /api/pwa_caches/{id}:
* delete:
* security:
* - bearerAuth: []
* tags: [Pwa_caches]
* summary: Delete the selected item
* description: Delete the selected item
* parameters:
* - in: path
* name: id
* description: Item ID to delete
* required: true
* schema:
* type: string
* responses:
* 200:
* description: The item was successfully deleted
* content:
* application/json:
* schema:
* $ref: "#/components/schemas/Pwa_caches"
* 400:
* description: Invalid ID supplied
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Item not found
* 500:
* description: Some server error
*/
router.delete('/:id', wrapAsync(async (req, res) => {
await Pwa_cachesService.remove(req.params.id, req.currentUser);
const payload = true;
res.status(200).send(payload);
}));
/**
* @swagger
* /api/pwa_caches/deleteByIds:
* post:
* security:
* - bearerAuth: []
* tags: [Pwa_caches]
* summary: Delete the selected item list
* description: Delete the selected item list
* requestBody:
* required: true
* content:
* application/json:
* schema:
* properties:
* ids:
* description: IDs of the updated items
* type: array
* responses:
* 200:
* description: The items was successfully deleted
* content:
* application/json:
* schema:
* $ref: "#/components/schemas/Pwa_caches"
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Items not found
* 500:
* description: Some server error
*/
router.post('/deleteByIds', wrapAsync(async (req, res) => {
await Pwa_cachesService.deleteByIds(req.body.data, req.currentUser);
const payload = true;
res.status(200).send(payload);
}));
/**
* @swagger
* /api/pwa_caches:
* get:
* security:
* - bearerAuth: []
* tags: [Pwa_caches]
* summary: Get all pwa_caches
* description: Get all pwa_caches
* responses:
* 200:
* description: Pwa_caches list successfully received
* content:
* application/json:
* schema:
* type: array
* items:
* $ref: "#/components/schemas/Pwa_caches"
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Data not found
* 500:
* description: Some server error
*/
router.get('/', wrapAsync(async (req, res) => {
const filetype = req.query.filetype
const currentUser = req.currentUser;
const payload = await Pwa_cachesDBApi.findAll(
req.query, { currentUser }
);
if (filetype && filetype === 'csv') {
const fields = ['id','cache_version','manifest_json','asset_list_json',
'generated_at',
];
const opts = { fields };
try {
const csv = parse(payload.rows, opts);
res.status(200).attachment(csv);
res.send(csv)
} catch (err) {
console.error(err);
}
} else {
res.status(200).send(payload);
}
}));
/** /**
* @swagger * @swagger
* /api/pwa_caches/count: * /api/pwa_caches:
* get: * post:
* security: * security:
* - bearerAuth: [] * - bearerAuth: []
* tags: [Pwa_caches] * tags: [Pwa_caches]
* summary: Count all pwa_caches * summary: Add new item
* description: Count all pwa_caches * requestBody:
* required: true
* content:
* application/json:
* schema:
* properties:
* data:
* type: object
* $ref: "#/components/schemas/Pwa_caches"
* responses: * responses:
* 200: * 200:
* description: Pwa_caches count successfully received * description: The item was successfully added
* content:
* application/json:
* schema:
* type: array
* items:
* $ref: "#/components/schemas/Pwa_caches"
* 401: * 401:
* $ref: "#/components/responses/UnauthorizedError" * $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Data not found
* 500: * 500:
* description: Some server error * description: Some server error
*/
router.get('/count', wrapAsync(async (req, res) => {
const currentUser = req.currentUser;
const payload = await Pwa_cachesDBApi.findAll(
req.query,
null,
{ countOnly: true, currentUser }
);
res.status(200).send(payload);
}));
/**
* @swagger
* /api/pwa_caches/autocomplete:
* get: * get:
* security: * security:
* - bearerAuth: [] * - bearerAuth: []
* tags: [Pwa_caches] * tags: [Pwa_caches]
* summary: Find all pwa_caches that match search criteria * summary: Get all pwa_caches
* description: Find all pwa_caches that match search criteria
* responses: * responses:
* 200: * 200:
* description: Pwa_caches list successfully received * description: Pwa_caches list successfully received
* content:
* application/json:
* schema:
* type: array
* items:
* $ref: "#/components/schemas/Pwa_caches"
* 401: * 401:
* $ref: "#/components/responses/UnauthorizedError" * $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Data not found
* 500: * 500:
* description: Some server error * description: Some server error
*/ */
router.get('/autocomplete', async (req, res) => {
const payload = await Pwa_cachesDBApi.findAllAutocomplete(
req.query.query,
req.query.limit,
req.query.offset,
);
res.status(200).send(payload);
});
/** /**
* @swagger * @swagger
* /api/pwa_caches/{id}: * /api/pwa_caches/{id}:
* get: * put:
* security: * security:
* - bearerAuth: [] * - bearerAuth: []
* tags: [Pwa_caches] * tags: [Pwa_caches]
* summary: Get selected item * summary: Update the selected item
* description: Get selected item * parameters:
* parameters: * - in: path
* - in: path * name: id
* name: id * required: true
* description: ID of item to get * schema:
* required: true * type: string
* schema: * requestBody:
* type: string * required: true
* responses: * content:
* 200: * application/json:
* description: Selected item successfully received * schema:
* content: * properties:
* application/json: * id:
* schema: * type: string
* $ref: "#/components/schemas/Pwa_caches" * data:
* 400: * type: object
* description: Invalid ID supplied * $ref: "#/components/schemas/Pwa_caches"
* 401: * responses:
* $ref: "#/components/responses/UnauthorizedError" * 200:
* 404: * description: The item was successfully updated
* description: Item not found * 401:
* 500: * $ref: "#/components/responses/UnauthorizedError"
* description: Some server error * 404:
*/ * description: Item not found
router.get('/:id', wrapAsync(async (req, res) => { * 500:
const payload = await Pwa_cachesDBApi.findBy( * description: Some server error
{ id: req.params.id }, * delete:
); * security:
* - bearerAuth: []
* tags: [Pwa_caches]
* summary: Delete the selected item
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: string
* responses:
* 200:
* description: The item was successfully deleted
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Item not found
* 500:
* description: Some server error
* get:
* security:
* - bearerAuth: []
* tags: [Pwa_caches]
* summary: Get selected item
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: string
* responses:
* 200:
* description: Selected item successfully received
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Item not found
* 500:
* description: Some server error
*/
module.exports = createEntityRouter('pwa_caches', Pwa_cachesService, Pwa_cachesDBApi);
res.status(200).send(payload);
}));
router.use('/', require('../helpers').commonErrorHandler);
module.exports = router;

View File

@ -1,22 +1,6 @@
const express = require('express');
const RolesService = require('../services/roles'); const RolesService = require('../services/roles');
const RolesDBApi = require('../db/api/roles'); const RolesDBApi = require('../db/api/roles');
const wrapAsync = require('../helpers').wrapAsync; const { createEntityRouter } = require('../factories/router.factory');
const router = express.Router();
const { parse } = require('json2csv');
const {
checkCrudPermissions,
} = require('../middlewares/check-permissions');
router.use(checkCrudPermissions('roles'));
/** /**
* @swagger * @swagger
@ -25,13 +9,9 @@ router.use(checkCrudPermissions('roles'));
* Roles: * Roles:
* type: object * type: object
* properties: * properties:
* name: * name:
* type: string * type: string
* default: name * default: name
*/ */
/** /**
@ -41,389 +21,121 @@ router.use(checkCrudPermissions('roles'));
* description: The Roles managing API * description: The Roles managing API
*/ */
/**
* @swagger
* /api/roles:
* post:
* security:
* - bearerAuth: []
* tags: [Roles]
* summary: Add new item
* description: Add new item
* requestBody:
* required: true
* content:
* application/json:
* schema:
* properties:
* data:
* description: Data of the updated item
* type: object
* $ref: "#/components/schemas/Roles"
* responses:
* 200:
* description: The item was successfully added
* content:
* application/json:
* schema:
* $ref: "#/components/schemas/Roles"
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 405:
* description: Invalid input data
* 500:
* description: Some server error
*/
router.post('/', wrapAsync(async (req, res) => {
const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`;
const link = new URL(referer);
await RolesService.create(req.body.data, req.currentUser, true, link.host);
const payload = true;
res.status(200).send(payload);
}));
/**
* @swagger
* /api/budgets/bulk-import:
* post:
* security:
* - bearerAuth: []
* tags: [Roles]
* summary: Bulk import items
* description: Bulk import items
* requestBody:
* required: true
* content:
* application/json:
* schema:
* properties:
* data:
* description: Data of the updated items
* type: array
* items:
* $ref: "#/components/schemas/Roles"
* responses:
* 200:
* description: The items were successfully imported
* content:
* application/json:
* schema:
* $ref: "#/components/schemas/Roles"
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 405:
* description: Invalid input data
* 500:
* description: Some server error
*
*/
router.post('/bulk-import', wrapAsync(async (req, res) => {
const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`;
const link = new URL(referer);
await RolesService.bulkImport(req, res, true, link.host);
const payload = true;
res.status(200).send(payload);
}));
/**
* @swagger
* /api/roles/{id}:
* put:
* security:
* - bearerAuth: []
* tags: [Roles]
* summary: Update the data of the selected item
* description: Update the data of the selected item
* parameters:
* - in: path
* name: id
* description: Item ID to update
* required: true
* schema:
* type: string
* requestBody:
* description: Set new item data
* required: true
* content:
* application/json:
* schema:
* properties:
* id:
* description: ID of the updated item
* type: string
* data:
* description: Data of the updated item
* type: object
* $ref: "#/components/schemas/Roles"
* required:
* - id
* responses:
* 200:
* description: The item data was successfully updated
* content:
* application/json:
* schema:
* $ref: "#/components/schemas/Roles"
* 400:
* description: Invalid ID supplied
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Item not found
* 500:
* description: Some server error
*/
router.put('/:id', wrapAsync(async (req, res) => {
await RolesService.update(req.body.data, req.body.id, req.currentUser);
const payload = true;
res.status(200).send(payload);
}));
/**
* @swagger
* /api/roles/{id}:
* delete:
* security:
* - bearerAuth: []
* tags: [Roles]
* summary: Delete the selected item
* description: Delete the selected item
* parameters:
* - in: path
* name: id
* description: Item ID to delete
* required: true
* schema:
* type: string
* responses:
* 200:
* description: The item was successfully deleted
* content:
* application/json:
* schema:
* $ref: "#/components/schemas/Roles"
* 400:
* description: Invalid ID supplied
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Item not found
* 500:
* description: Some server error
*/
router.delete('/:id', wrapAsync(async (req, res) => {
await RolesService.remove(req.params.id, req.currentUser);
const payload = true;
res.status(200).send(payload);
}));
/**
* @swagger
* /api/roles/deleteByIds:
* post:
* security:
* - bearerAuth: []
* tags: [Roles]
* summary: Delete the selected item list
* description: Delete the selected item list
* requestBody:
* required: true
* content:
* application/json:
* schema:
* properties:
* ids:
* description: IDs of the updated items
* type: array
* responses:
* 200:
* description: The items was successfully deleted
* content:
* application/json:
* schema:
* $ref: "#/components/schemas/Roles"
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Items not found
* 500:
* description: Some server error
*/
router.post('/deleteByIds', wrapAsync(async (req, res) => {
await RolesService.deleteByIds(req.body.data, req.currentUser);
const payload = true;
res.status(200).send(payload);
}));
/**
* @swagger
* /api/roles:
* get:
* security:
* - bearerAuth: []
* tags: [Roles]
* summary: Get all roles
* description: Get all roles
* responses:
* 200:
* description: Roles list successfully received
* content:
* application/json:
* schema:
* type: array
* items:
* $ref: "#/components/schemas/Roles"
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Data not found
* 500:
* description: Some server error
*/
router.get('/', wrapAsync(async (req, res) => {
const filetype = req.query.filetype
const currentUser = req.currentUser;
const payload = await RolesDBApi.findAll(
req.query, { currentUser }
);
if (filetype && filetype === 'csv') {
const fields = ['id','name',
];
const opts = { fields };
try {
const csv = parse(payload.rows, opts);
res.status(200).attachment(csv);
res.send(csv)
} catch (err) {
console.error(err);
}
} else {
res.status(200).send(payload);
}
}));
/** /**
* @swagger * @swagger
* /api/roles/count: * /api/roles:
* get: * post:
* security: * security:
* - bearerAuth: [] * - bearerAuth: []
* tags: [Roles] * tags: [Roles]
* summary: Count all roles * summary: Add new item
* description: Count all roles * description: Add new item
* requestBody:
* required: true
* content:
* application/json:
* schema:
* properties:
* data:
* description: Data of the item
* type: object
* $ref: "#/components/schemas/Roles"
* responses: * responses:
* 200: * 200:
* description: Roles count successfully received * description: The item was successfully added
* content:
* application/json:
* schema:
* type: array
* items:
* $ref: "#/components/schemas/Roles"
* 401: * 401:
* $ref: "#/components/responses/UnauthorizedError" * $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Data not found
* 500: * 500:
* description: Some server error * description: Some server error
*/
router.get('/count', wrapAsync(async (req, res) => {
const currentUser = req.currentUser;
const payload = await RolesDBApi.findAll(
req.query,
null,
{ countOnly: true, currentUser }
);
res.status(200).send(payload);
}));
/**
* @swagger
* /api/roles/autocomplete:
* get: * get:
* security: * security:
* - bearerAuth: [] * - bearerAuth: []
* tags: [Roles] * tags: [Roles]
* summary: Find all roles that match search criteria * summary: Get all roles
* description: Find all roles that match search criteria * description: Get all roles
* responses: * responses:
* 200: * 200:
* description: Roles list successfully received * description: Roles list successfully received
* content:
* application/json:
* schema:
* type: array
* items:
* $ref: "#/components/schemas/Roles"
* 401: * 401:
* $ref: "#/components/responses/UnauthorizedError" * $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Data not found
* 500: * 500:
* description: Some server error * description: Some server error
*/ */
router.get('/autocomplete', async (req, res) => {
const payload = await RolesDBApi.findAllAutocomplete(
req.query.query,
req.query.limit,
req.query.offset,
);
res.status(200).send(payload);
});
/** /**
* @swagger * @swagger
* /api/roles/{id}: * /api/roles/{id}:
* get: * put:
* security: * security:
* - bearerAuth: [] * - bearerAuth: []
* tags: [Roles] * tags: [Roles]
* summary: Get selected item * summary: Update the data of the selected item
* description: Get selected item * parameters:
* parameters: * - in: path
* - in: path * name: id
* name: id * required: true
* description: ID of item to get * schema:
* required: true * type: string
* schema: * requestBody:
* type: string * required: true
* responses: * content:
* 200: * application/json:
* description: Selected item successfully received * schema:
* content: * properties:
* application/json: * id:
* schema: * type: string
* $ref: "#/components/schemas/Roles" * data:
* 400: * type: object
* description: Invalid ID supplied * $ref: "#/components/schemas/Roles"
* 401: * responses:
* $ref: "#/components/responses/UnauthorizedError" * 200:
* 404: * description: The item data was successfully updated
* description: Item not found * 401:
* 500: * $ref: "#/components/responses/UnauthorizedError"
* description: Some server error * 404:
*/ * description: Item not found
router.get('/:id', wrapAsync(async (req, res) => { * 500:
const payload = await RolesDBApi.findBy( * description: Some server error
{ id: req.params.id }, * delete:
); * security:
* - bearerAuth: []
* tags: [Roles]
* summary: Delete the selected item
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: string
* responses:
* 200:
* description: The item was successfully deleted
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Item not found
* 500:
* description: Some server error
* get:
* security:
* - bearerAuth: []
* tags: [Roles]
* summary: Get selected item
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: string
* responses:
* 200:
* description: Selected item successfully received
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Item not found
* 500:
* description: Some server error
*/
module.exports = createEntityRouter('roles', RolesService, RolesDBApi);
res.status(200).send(payload);
}));
router.use('/', require('../helpers').commonErrorHandler);
module.exports = router;

View File

@ -1,22 +1,6 @@
const express = require('express');
const Tour_pagesService = require('../services/tour_pages'); const Tour_pagesService = require('../services/tour_pages');
const Tour_pagesDBApi = require('../db/api/tour_pages'); const Tour_pagesDBApi = require('../db/api/tour_pages');
const wrapAsync = require('../helpers').wrapAsync; const { createEntityRouter } = require('../factories/router.factory');
const router = express.Router();
const { parse } = require('json2csv');
const {
checkCrudPermissions,
} = require('../middlewares/check-permissions');
router.use(checkCrudPermissions('tour_pages'));
/** /**
* @swagger * @swagger
@ -25,35 +9,22 @@ router.use(checkCrudPermissions('tour_pages'));
* Tour_pages: * Tour_pages:
* type: object * type: object
* properties: * properties:
* source_key: * source_key:
* type: string * type: string
* default: source_key
* name: * name:
* type: string * type: string
* default: name
* slug: * slug:
* type: string * type: string
* default: slug
* background_image_url: * background_image_url:
* type: string * type: string
* default: background_image_url
* background_video_url: * background_video_url:
* type: string * type: string
* default: background_video_url
* background_audio_url: * background_audio_url:
* type: string * type: string
* default: background_audio_url
* ui_schema_json: * ui_schema_json:
* type: string * type: string
* default: ui_schema_json
* sort_order: * sort_order:
* type: integer * type: integer
* format: int64
*
*/ */
/** /**
@ -63,393 +34,118 @@ router.use(checkCrudPermissions('tour_pages'));
* description: The Tour_pages managing API * description: The Tour_pages managing API
*/ */
/**
* @swagger
* /api/tour_pages:
* post:
* security:
* - bearerAuth: []
* tags: [Tour_pages]
* summary: Add new item
* description: Add new item
* requestBody:
* required: true
* content:
* application/json:
* schema:
* properties:
* data:
* description: Data of the updated item
* type: object
* $ref: "#/components/schemas/Tour_pages"
* responses:
* 200:
* description: The item was successfully added
* content:
* application/json:
* schema:
* $ref: "#/components/schemas/Tour_pages"
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 405:
* description: Invalid input data
* 500:
* description: Some server error
*/
router.post('/', wrapAsync(async (req, res) => {
const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`;
const link = new URL(referer);
await Tour_pagesService.create(req.body.data, req.currentUser, true, link.host);
const payload = true;
res.status(200).send(payload);
}));
/**
* @swagger
* /api/budgets/bulk-import:
* post:
* security:
* - bearerAuth: []
* tags: [Tour_pages]
* summary: Bulk import items
* description: Bulk import items
* requestBody:
* required: true
* content:
* application/json:
* schema:
* properties:
* data:
* description: Data of the updated items
* type: array
* items:
* $ref: "#/components/schemas/Tour_pages"
* responses:
* 200:
* description: The items were successfully imported
* content:
* application/json:
* schema:
* $ref: "#/components/schemas/Tour_pages"
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 405:
* description: Invalid input data
* 500:
* description: Some server error
*
*/
router.post('/bulk-import', wrapAsync(async (req, res) => {
const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`;
const link = new URL(referer);
await Tour_pagesService.bulkImport(req, res, true, link.host);
const payload = true;
res.status(200).send(payload);
}));
/**
* @swagger
* /api/tour_pages/{id}:
* put:
* security:
* - bearerAuth: []
* tags: [Tour_pages]
* summary: Update the data of the selected item
* description: Update the data of the selected item
* parameters:
* - in: path
* name: id
* description: Item ID to update
* required: true
* schema:
* type: string
* requestBody:
* description: Set new item data
* required: true
* content:
* application/json:
* schema:
* properties:
* id:
* description: ID of the updated item
* type: string
* data:
* description: Data of the updated item
* type: object
* $ref: "#/components/schemas/Tour_pages"
* required:
* - id
* responses:
* 200:
* description: The item data was successfully updated
* content:
* application/json:
* schema:
* $ref: "#/components/schemas/Tour_pages"
* 400:
* description: Invalid ID supplied
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Item not found
* 500:
* description: Some server error
*/
router.put('/:id', wrapAsync(async (req, res) => {
await Tour_pagesService.update(req.body.data, req.body.id, req.currentUser);
const payload = true;
res.status(200).send(payload);
}));
/**
* @swagger
* /api/tour_pages/{id}:
* delete:
* security:
* - bearerAuth: []
* tags: [Tour_pages]
* summary: Delete the selected item
* description: Delete the selected item
* parameters:
* - in: path
* name: id
* description: Item ID to delete
* required: true
* schema:
* type: string
* responses:
* 200:
* description: The item was successfully deleted
* content:
* application/json:
* schema:
* $ref: "#/components/schemas/Tour_pages"
* 400:
* description: Invalid ID supplied
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Item not found
* 500:
* description: Some server error
*/
router.delete('/:id', wrapAsync(async (req, res) => {
await Tour_pagesService.remove(req.params.id, req.currentUser);
const payload = true;
res.status(200).send(payload);
}));
/**
* @swagger
* /api/tour_pages/deleteByIds:
* post:
* security:
* - bearerAuth: []
* tags: [Tour_pages]
* summary: Delete the selected item list
* description: Delete the selected item list
* requestBody:
* required: true
* content:
* application/json:
* schema:
* properties:
* ids:
* description: IDs of the updated items
* type: array
* responses:
* 200:
* description: The items was successfully deleted
* content:
* application/json:
* schema:
* $ref: "#/components/schemas/Tour_pages"
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Items not found
* 500:
* description: Some server error
*/
router.post('/deleteByIds', wrapAsync(async (req, res) => {
await Tour_pagesService.deleteByIds(req.body.data, req.currentUser);
const payload = true;
res.status(200).send(payload);
}));
/**
* @swagger
* /api/tour_pages:
* get:
* security:
* - bearerAuth: []
* tags: [Tour_pages]
* summary: Get all tour_pages
* description: Get all tour_pages
* responses:
* 200:
* description: Tour_pages list successfully received
* content:
* application/json:
* schema:
* type: array
* items:
* $ref: "#/components/schemas/Tour_pages"
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Data not found
* 500:
* description: Some server error
*/
router.get('/', wrapAsync(async (req, res) => {
const filetype = req.query.filetype
const currentUser = req.currentUser;
const runtimeContext = req.runtimeContext;
const payload = await Tour_pagesDBApi.findAll(
req.query, { currentUser, runtimeContext }
);
if (filetype && filetype === 'csv') {
const fields = ['id','source_key','name','slug','background_image_url','background_video_url','background_audio_url','ui_schema_json',
'sort_order',
];
const opts = { fields };
try {
const csv = parse(payload.rows, opts);
res.status(200).attachment(csv);
res.send(csv)
} catch (err) {
console.error(err);
}
} else {
res.status(200).send(payload);
}
}));
/** /**
* @swagger * @swagger
* /api/tour_pages/count: * /api/tour_pages:
* get: * post:
* security: * security:
* - bearerAuth: [] * - bearerAuth: []
* tags: [Tour_pages] * tags: [Tour_pages]
* summary: Count all tour_pages * summary: Add new item
* description: Count all tour_pages * requestBody:
* required: true
* content:
* application/json:
* schema:
* properties:
* data:
* type: object
* $ref: "#/components/schemas/Tour_pages"
* responses: * responses:
* 200: * 200:
* description: Tour_pages count successfully received * description: The item was successfully added
* content:
* application/json:
* schema:
* type: array
* items:
* $ref: "#/components/schemas/Tour_pages"
* 401: * 401:
* $ref: "#/components/responses/UnauthorizedError" * $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Data not found
* 500: * 500:
* description: Some server error * description: Some server error
*/
router.get('/count', wrapAsync(async (req, res) => {
const currentUser = req.currentUser;
const runtimeContext = req.runtimeContext;
const payload = await Tour_pagesDBApi.findAll(
req.query,
null,
{ countOnly: true, currentUser, runtimeContext }
);
res.status(200).send(payload);
}));
/**
* @swagger
* /api/tour_pages/autocomplete:
* get: * get:
* security: * security:
* - bearerAuth: [] * - bearerAuth: []
* tags: [Tour_pages] * tags: [Tour_pages]
* summary: Find all tour_pages that match search criteria * summary: Get all tour_pages
* description: Find all tour_pages that match search criteria
* responses: * responses:
* 200: * 200:
* description: Tour_pages list successfully received * description: Tour_pages list successfully received
* content:
* application/json:
* schema:
* type: array
* items:
* $ref: "#/components/schemas/Tour_pages"
* 401: * 401:
* $ref: "#/components/responses/UnauthorizedError" * $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Data not found
* 500: * 500:
* description: Some server error * description: Some server error
*/ */
router.get('/autocomplete', async (req, res) => {
const payload = await Tour_pagesDBApi.findAllAutocomplete(
req.query.query,
req.query.limit,
req.query.offset,
);
res.status(200).send(payload);
});
/** /**
* @swagger * @swagger
* /api/tour_pages/{id}: * /api/tour_pages/{id}:
* get: * put:
* security: * security:
* - bearerAuth: [] * - bearerAuth: []
* tags: [Tour_pages] * tags: [Tour_pages]
* summary: Get selected item * summary: Update the selected item
* description: Get selected item * parameters:
* parameters: * - in: path
* - in: path * name: id
* name: id * required: true
* description: ID of item to get * schema:
* required: true * type: string
* schema: * requestBody:
* type: string * required: true
* responses: * content:
* 200: * application/json:
* description: Selected item successfully received * schema:
* content: * properties:
* application/json: * id:
* schema: * type: string
* $ref: "#/components/schemas/Tour_pages" * data:
* 400: * type: object
* description: Invalid ID supplied * $ref: "#/components/schemas/Tour_pages"
* 401: * responses:
* $ref: "#/components/responses/UnauthorizedError" * 200:
* 404: * description: The item was successfully updated
* description: Item not found * 401:
* 500: * $ref: "#/components/responses/UnauthorizedError"
* description: Some server error * 404:
*/ * description: Item not found
router.get('/:id', wrapAsync(async (req, res) => { * 500:
const runtimeContext = req.runtimeContext; * description: Some server error
const payload = await Tour_pagesDBApi.findBy( * delete:
{ id: req.params.id }, * security:
{ runtimeContext }, * - bearerAuth: []
); * tags: [Tour_pages]
* summary: Delete the selected item
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: string
* responses:
* 200:
* description: The item was successfully deleted
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Item not found
* 500:
* description: Some server error
* get:
* security:
* - bearerAuth: []
* tags: [Tour_pages]
* summary: Get selected item
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: string
* responses:
* 200:
* description: Selected item successfully received
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Item not found
* 500:
* description: Some server error
*/
module.exports = createEntityRouter('tour_pages', Tour_pagesService, Tour_pagesDBApi);
res.status(200).send(payload);
}));
router.use('/', require('../helpers').commonErrorHandler);
module.exports = router;

View File

@ -1,22 +1,6 @@
const express = require('express');
const TransitionsService = require('../services/transitions'); const TransitionsService = require('../services/transitions');
const TransitionsDBApi = require('../db/api/transitions'); const TransitionsDBApi = require('../db/api/transitions');
const wrapAsync = require('../helpers').wrapAsync; const { createEntityRouter } = require('../factories/router.factory');
const router = express.Router();
const { parse } = require('json2csv');
const {
checkCrudPermissions,
} = require('../middlewares/check-permissions');
router.use(checkCrudPermissions('transitions'));
/** /**
* @swagger * @swagger
@ -25,29 +9,18 @@ router.use(checkCrudPermissions('transitions'));
* Transitions: * Transitions:
* type: object * type: object
* properties: * properties:
* source_key: * source_key:
* type: string * type: string
* default: source_key
* name: * name:
* type: string * type: string
* default: name
* slug: * slug:
* type: string * type: string
* default: slug
* video_url: * video_url:
* type: string * type: string
* default: video_url
* audio_url: * audio_url:
* type: string * type: string
* default: audio_url
* duration_sec: * duration_sec:
* type: integer * type: number
* format: int64
*
*/ */
/** /**
@ -57,393 +30,118 @@ router.use(checkCrudPermissions('transitions'));
* description: The Transitions managing API * description: The Transitions managing API
*/ */
/**
* @swagger
* /api/transitions:
* post:
* security:
* - bearerAuth: []
* tags: [Transitions]
* summary: Add new item
* description: Add new item
* requestBody:
* required: true
* content:
* application/json:
* schema:
* properties:
* data:
* description: Data of the updated item
* type: object
* $ref: "#/components/schemas/Transitions"
* responses:
* 200:
* description: The item was successfully added
* content:
* application/json:
* schema:
* $ref: "#/components/schemas/Transitions"
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 405:
* description: Invalid input data
* 500:
* description: Some server error
*/
router.post('/', wrapAsync(async (req, res) => {
const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`;
const link = new URL(referer);
await TransitionsService.create(req.body.data, req.currentUser, true, link.host);
const payload = true;
res.status(200).send(payload);
}));
/**
* @swagger
* /api/budgets/bulk-import:
* post:
* security:
* - bearerAuth: []
* tags: [Transitions]
* summary: Bulk import items
* description: Bulk import items
* requestBody:
* required: true
* content:
* application/json:
* schema:
* properties:
* data:
* description: Data of the updated items
* type: array
* items:
* $ref: "#/components/schemas/Transitions"
* responses:
* 200:
* description: The items were successfully imported
* content:
* application/json:
* schema:
* $ref: "#/components/schemas/Transitions"
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 405:
* description: Invalid input data
* 500:
* description: Some server error
*
*/
router.post('/bulk-import', wrapAsync(async (req, res) => {
const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`;
const link = new URL(referer);
await TransitionsService.bulkImport(req, res, true, link.host);
const payload = true;
res.status(200).send(payload);
}));
/**
* @swagger
* /api/transitions/{id}:
* put:
* security:
* - bearerAuth: []
* tags: [Transitions]
* summary: Update the data of the selected item
* description: Update the data of the selected item
* parameters:
* - in: path
* name: id
* description: Item ID to update
* required: true
* schema:
* type: string
* requestBody:
* description: Set new item data
* required: true
* content:
* application/json:
* schema:
* properties:
* id:
* description: ID of the updated item
* type: string
* data:
* description: Data of the updated item
* type: object
* $ref: "#/components/schemas/Transitions"
* required:
* - id
* responses:
* 200:
* description: The item data was successfully updated
* content:
* application/json:
* schema:
* $ref: "#/components/schemas/Transitions"
* 400:
* description: Invalid ID supplied
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Item not found
* 500:
* description: Some server error
*/
router.put('/:id', wrapAsync(async (req, res) => {
await TransitionsService.update(req.body.data, req.body.id, req.currentUser);
const payload = true;
res.status(200).send(payload);
}));
/**
* @swagger
* /api/transitions/{id}:
* delete:
* security:
* - bearerAuth: []
* tags: [Transitions]
* summary: Delete the selected item
* description: Delete the selected item
* parameters:
* - in: path
* name: id
* description: Item ID to delete
* required: true
* schema:
* type: string
* responses:
* 200:
* description: The item was successfully deleted
* content:
* application/json:
* schema:
* $ref: "#/components/schemas/Transitions"
* 400:
* description: Invalid ID supplied
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Item not found
* 500:
* description: Some server error
*/
router.delete('/:id', wrapAsync(async (req, res) => {
await TransitionsService.remove(req.params.id, req.currentUser);
const payload = true;
res.status(200).send(payload);
}));
/**
* @swagger
* /api/transitions/deleteByIds:
* post:
* security:
* - bearerAuth: []
* tags: [Transitions]
* summary: Delete the selected item list
* description: Delete the selected item list
* requestBody:
* required: true
* content:
* application/json:
* schema:
* properties:
* ids:
* description: IDs of the updated items
* type: array
* responses:
* 200:
* description: The items was successfully deleted
* content:
* application/json:
* schema:
* $ref: "#/components/schemas/Transitions"
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Items not found
* 500:
* description: Some server error
*/
router.post('/deleteByIds', wrapAsync(async (req, res) => {
await TransitionsService.deleteByIds(req.body.data, req.currentUser);
const payload = true;
res.status(200).send(payload);
}));
/**
* @swagger
* /api/transitions:
* get:
* security:
* - bearerAuth: []
* tags: [Transitions]
* summary: Get all transitions
* description: Get all transitions
* responses:
* 200:
* description: Transitions list successfully received
* content:
* application/json:
* schema:
* type: array
* items:
* $ref: "#/components/schemas/Transitions"
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Data not found
* 500:
* description: Some server error
*/
router.get('/', wrapAsync(async (req, res) => {
const filetype = req.query.filetype
const currentUser = req.currentUser;
const runtimeContext = req.runtimeContext;
const payload = await TransitionsDBApi.findAll(
req.query, { currentUser, runtimeContext }
);
if (filetype && filetype === 'csv') {
const fields = ['id','source_key','name','slug','video_url','audio_url',
'duration_sec',
];
const opts = { fields };
try {
const csv = parse(payload.rows, opts);
res.status(200).attachment(csv);
res.send(csv)
} catch (err) {
console.error(err);
}
} else {
res.status(200).send(payload);
}
}));
/** /**
* @swagger * @swagger
* /api/transitions/count: * /api/transitions:
* get: * post:
* security: * security:
* - bearerAuth: [] * - bearerAuth: []
* tags: [Transitions] * tags: [Transitions]
* summary: Count all transitions * summary: Add new item
* description: Count all transitions * requestBody:
* required: true
* content:
* application/json:
* schema:
* properties:
* data:
* type: object
* $ref: "#/components/schemas/Transitions"
* responses: * responses:
* 200: * 200:
* description: Transitions count successfully received * description: The item was successfully added
* content:
* application/json:
* schema:
* type: array
* items:
* $ref: "#/components/schemas/Transitions"
* 401: * 401:
* $ref: "#/components/responses/UnauthorizedError" * $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Data not found
* 500: * 500:
* description: Some server error * description: Some server error
*/
router.get('/count', wrapAsync(async (req, res) => {
const currentUser = req.currentUser;
const runtimeContext = req.runtimeContext;
const payload = await TransitionsDBApi.findAll(
req.query,
null,
{ countOnly: true, currentUser, runtimeContext }
);
res.status(200).send(payload);
}));
/**
* @swagger
* /api/transitions/autocomplete:
* get: * get:
* security: * security:
* - bearerAuth: [] * - bearerAuth: []
* tags: [Transitions] * tags: [Transitions]
* summary: Find all transitions that match search criteria * summary: Get all transitions
* description: Find all transitions that match search criteria
* responses: * responses:
* 200: * 200:
* description: Transitions list successfully received * description: Transitions list successfully received
* content:
* application/json:
* schema:
* type: array
* items:
* $ref: "#/components/schemas/Transitions"
* 401: * 401:
* $ref: "#/components/responses/UnauthorizedError" * $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Data not found
* 500: * 500:
* description: Some server error * description: Some server error
*/ */
router.get('/autocomplete', async (req, res) => {
const payload = await TransitionsDBApi.findAllAutocomplete(
req.query.query,
req.query.limit,
req.query.offset,
);
res.status(200).send(payload);
});
/** /**
* @swagger * @swagger
* /api/transitions/{id}: * /api/transitions/{id}:
* get: * put:
* security: * security:
* - bearerAuth: [] * - bearerAuth: []
* tags: [Transitions] * tags: [Transitions]
* summary: Get selected item * summary: Update the selected item
* description: Get selected item * parameters:
* parameters: * - in: path
* - in: path * name: id
* name: id * required: true
* description: ID of item to get * schema:
* required: true * type: string
* schema: * requestBody:
* type: string * required: true
* responses: * content:
* 200: * application/json:
* description: Selected item successfully received * schema:
* content: * properties:
* application/json: * id:
* schema: * type: string
* $ref: "#/components/schemas/Transitions" * data:
* 400: * type: object
* description: Invalid ID supplied * $ref: "#/components/schemas/Transitions"
* 401: * responses:
* $ref: "#/components/responses/UnauthorizedError" * 200:
* 404: * description: The item was successfully updated
* description: Item not found * 401:
* 500: * $ref: "#/components/responses/UnauthorizedError"
* description: Some server error * 404:
*/ * description: Item not found
router.get('/:id', wrapAsync(async (req, res) => { * 500:
const runtimeContext = req.runtimeContext; * description: Some server error
const payload = await TransitionsDBApi.findBy( * delete:
{ id: req.params.id }, * security:
{ runtimeContext }, * - bearerAuth: []
); * tags: [Transitions]
* summary: Delete the selected item
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: string
* responses:
* 200:
* description: The item was successfully deleted
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Item not found
* 500:
* description: Some server error
* get:
* security:
* - bearerAuth: []
* tags: [Transitions]
* summary: Get selected item
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: string
* responses:
* 200:
* description: Selected item successfully received
* 401:
* $ref: "#/components/responses/UnauthorizedError"
* 404:
* description: Item not found
* 500:
* description: Some server error
*/
module.exports = createEntityRouter('transitions', TransitionsService, TransitionsDBApi);
res.status(200).send(payload);
}));
router.use('/', require('../helpers').commonErrorHandler);
module.exports = router;

View File

@ -1,103 +0,0 @@
const express = require('express');
const Ui_elementsService = require('../services/ui_elements');
const Ui_elementsDBApi = require('../db/api/ui_elements');
const wrapAsync = require('../helpers').wrapAsync;
const router = express.Router();
const { parse } = require('json2csv');
const { checkCrudPermissions } = require('../middlewares/check-permissions');
router.use(checkCrudPermissions('page_elements'));
router.post(
'/',
wrapAsync(async (req, res) => {
await Ui_elementsService.create(req.body.data, req.currentUser);
res.status(200).send(true);
}),
);
router.post(
'/bulk-import',
wrapAsync(async (req, res) => {
await Ui_elementsService.bulkImport(req, res);
res.status(200).send(true);
}),
);
router.put(
'/:id',
wrapAsync(async (req, res) => {
await Ui_elementsService.update(req.body.data, req.body.id, req.currentUser);
res.status(200).send(true);
}),
);
router.delete(
'/:id',
wrapAsync(async (req, res) => {
await Ui_elementsService.remove(req.params.id, req.currentUser);
res.status(200).send(true);
}),
);
router.post(
'/deleteByIds',
wrapAsync(async (req, res) => {
await Ui_elementsService.deleteByIds(req.body.data, req.currentUser);
res.status(200).send(true);
}),
);
router.get(
'/',
wrapAsync(async (req, res) => {
const filetype = req.query.filetype;
const payload = await Ui_elementsDBApi.findAll(req.query, {
currentUser: req.currentUser,
});
if (filetype && filetype === 'csv') {
const fields = ['id', 'name', 'element_type', 'settings_json', 'sort_order'];
const opts = { fields };
try {
const csv = parse(payload.rows, opts);
res.status(200).attachment(csv);
res.send(csv);
} catch (err) {
console.error(err);
throw err;
}
} else {
res.status(200).send(payload);
}
}),
);
router.get(
'/count',
wrapAsync(async (req, res) => {
const payload = await Ui_elementsDBApi.findAll(req.query, { countOnly: true, currentUser: req.currentUser });
res.status(200).send(payload);
}),
);
router.get('/autocomplete', async (req, res) => {
const payload = await Ui_elementsDBApi.findAllAutocomplete(req.query.query, req.query.limit, req.query.offset);
res.status(200).send(payload);
});
router.get(
'/:id',
wrapAsync(async (req, res) => {
const payload = await Ui_elementsDBApi.findBy({ id: req.params.id });
res.status(200).send(payload);
}),
);
router.use('/', require('../helpers').commonErrorHandler);
module.exports = router;

View File

@ -120,6 +120,7 @@ module.exports = class ProjectsService {
{ {
name: sourceAsset.name, name: sourceAsset.name,
asset_type: sourceAsset.asset_type, asset_type: sourceAsset.asset_type,
type: sourceAsset.type || 'general',
cdn_url: sourceAsset.cdn_url, cdn_url: sourceAsset.cdn_url,
storage_key: sourceAsset.storage_key, storage_key: sourceAsset.storage_key,
mime_type: sourceAsset.mime_type, mime_type: sourceAsset.mime_type,

View File

@ -1,114 +0,0 @@
const db = require('../db/models');
const Ui_elementsDBApi = require('../db/api/ui_elements');
const processFile = require('../middlewares/upload');
const ValidationError = require('./notifications/errors/validation');
const csv = require('csv-parser');
const stream = require('stream');
module.exports = class Ui_elementsService {
static async create(data, currentUser) {
const transaction = await db.sequelize.transaction();
try {
await Ui_elementsDBApi.create(data, {
currentUser,
transaction,
});
await transaction.commit();
} catch (error) {
await transaction.rollback();
throw error;
}
}
static async bulkImport(req, res) {
const transaction = await db.sequelize.transaction();
try {
await processFile(req, res);
const bufferStream = new stream.PassThrough();
const results = [];
await bufferStream.end(Buffer.from(req.file.buffer, 'utf-8'));
await new Promise((resolve, reject) => {
bufferStream
.pipe(csv())
.on('data', (data) => results.push(data))
.on('end', async () => {
resolve();
})
.on('error', (error) => reject(error));
});
await Ui_elementsDBApi.bulkImport(results, {
transaction,
ignoreDuplicates: true,
validate: true,
currentUser: req.currentUser,
});
await transaction.commit();
} catch (error) {
await transaction.rollback();
throw error;
}
}
static async update(data, id, currentUser) {
const transaction = await db.sequelize.transaction();
try {
const ui_elements = await Ui_elementsDBApi.findBy(
{ id },
{ transaction },
);
if (!ui_elements) {
throw new ValidationError('ui_elementsNotFound');
}
const updatedUiElements = await Ui_elementsDBApi.update(id, data, {
currentUser,
transaction,
});
await transaction.commit();
return updatedUiElements;
} catch (error) {
await transaction.rollback();
throw error;
}
}
static async deleteByIds(ids, currentUser) {
const transaction = await db.sequelize.transaction();
try {
await Ui_elementsDBApi.deleteByIds(ids, {
currentUser,
transaction,
});
await transaction.commit();
} catch (error) {
await transaction.rollback();
throw error;
}
}
static async remove(id, currentUser) {
const transaction = await db.sequelize.transaction();
try {
await Ui_elementsDBApi.remove(id, {
currentUser,
transaction,
});
await transaction.commit();
} catch (error) {
await transaction.rollback();
throw error;
}
}
};

View File

@ -0,0 +1,79 @@
const { logger } = require('./logger');
class CircuitBreaker {
constructor(options = {}) {
this.name = options.name || 'default';
this.failureThreshold = options.failureThreshold || 5;
this.resetTimeout = options.resetTimeout || 30000;
this.failures = 0;
this.state = 'CLOSED';
this.nextAttempt = Date.now();
}
async execute(fn) {
if (this.state === 'OPEN') {
if (Date.now() < this.nextAttempt) {
logger.warn({ circuitBreaker: this.name, state: this.state }, 'Circuit breaker is OPEN, rejecting request');
throw new Error(`Circuit breaker ${this.name} is OPEN`);
}
this.state = 'HALF-OPEN';
logger.info({ circuitBreaker: this.name }, 'Circuit breaker moved to HALF-OPEN');
}
try {
const result = await fn();
this.onSuccess();
return result;
} catch (error) {
this.onFailure(error);
throw error;
}
}
onSuccess() {
if (this.state === 'HALF-OPEN') {
logger.info({ circuitBreaker: this.name }, 'Circuit breaker recovered, moving to CLOSED');
}
this.failures = 0;
this.state = 'CLOSED';
}
onFailure(error) {
this.failures++;
logger.warn({
circuitBreaker: this.name,
failures: this.failures,
threshold: this.failureThreshold,
error: error.message,
}, 'Circuit breaker recorded failure');
if (this.failures >= this.failureThreshold) {
this.state = 'OPEN';
this.nextAttempt = Date.now() + this.resetTimeout;
logger.error({
circuitBreaker: this.name,
resetAt: new Date(this.nextAttempt).toISOString(),
}, 'Circuit breaker tripped to OPEN');
}
}
getState() {
return {
name: this.name,
state: this.state,
failures: this.failures,
nextAttempt: this.state === 'OPEN' ? new Date(this.nextAttempt).toISOString() : null,
};
}
}
const circuitBreakers = new Map();
function getCircuitBreaker(name, options = {}) {
if (!circuitBreakers.has(name)) {
circuitBreakers.set(name, new CircuitBreaker({ name, ...options }));
}
return circuitBreakers.get(name);
}
module.exports = { CircuitBreaker, getCircuitBreaker };

View File

@ -0,0 +1,65 @@
const Joi = require('joi');
const envSchema = Joi.object({
NODE_ENV: Joi.string()
.valid('development', 'test', 'production', 'dev_stage')
.default('development'),
PORT: Joi.number().default(8080),
DB_HOST: Joi.string().default('localhost'),
DB_PORT: Joi.number().default(5432),
DB_NAME: Joi.string().default('db_tour_builder_platform'),
DB_USER: Joi.string().default('postgres'),
DB_PASS: Joi.string().allow('').default(''),
SECRET_KEY: Joi.string().min(16).default('88dbeaf8-e906-405e-9e41-c3baadeda5c6'),
ADMIN_PASS: Joi.string().default('88dbeaf8'),
USER_PASS: Joi.string().default('c3baadeda5c6'),
ADMIN_EMAIL: Joi.string().email().default('admin@flatlogic.com'),
GOOGLE_CLIENT_ID: Joi.string().allow('').default(''),
GOOGLE_CLIENT_SECRET: Joi.string().allow('').default(''),
MS_CLIENT_ID: Joi.string().allow('').default(''),
MS_CLIENT_SECRET: Joi.string().allow('').default(''),
AWS_ACCESS_KEY_ID: Joi.string().allow('').default(''),
AWS_SECRET_ACCESS_KEY: Joi.string().allow('').default(''),
AWS_S3_BUCKET: Joi.string().allow('').default(''),
AWS_S3_REGION: Joi.string().default('us-east-1'),
AWS_S3_PREFIX: Joi.string().default('afeefb9d49f5b7977577876b99532ac7'),
EMAIL_USER: Joi.string().allow('').default(''),
EMAIL_PASS: Joi.string().allow('').default(''),
EMAIL_TLS_REJECT_UNAUTHORIZED: Joi.string().valid('true', 'false').default('true'),
GPT_KEY: Joi.string().allow('').default(''),
PEXELS_KEY: Joi.string().allow('').default(''),
LOG_LEVEL: Joi.string()
.valid('fatal', 'error', 'warn', 'info', 'debug', 'trace')
.default('info'),
}).unknown(true);
function validateEnv() {
const { error, value } = envSchema.validate(process.env, {
abortEarly: false,
stripUnknown: false,
});
if (error) {
const messages = error.details.map(d => ` - ${d.message}`).join('\n');
console.error('Environment validation failed:\n' + messages);
if (process.env.NODE_ENV === 'production') {
process.exit(1);
} else {
console.warn('Continuing with default values in non-production mode');
}
}
return value;
}
module.exports = { validateEnv, envSchema };

View File

@ -0,0 +1,48 @@
class AppError extends Error {
constructor(message, statusCode = 500, details = null) {
super(message);
this.statusCode = statusCode;
this.details = details;
this.isOperational = true;
Error.captureStackTrace(this, this.constructor);
}
}
class NotFoundError extends AppError {
constructor(resource = 'Resource') {
super(`${resource} not found`, 404);
}
}
class ValidationError extends AppError {
constructor(message, details = null) {
super(message, 400, details);
}
}
class ForbiddenError extends AppError {
constructor(message = 'Access denied') {
super(message, 403);
}
}
class UnauthorizedError extends AppError {
constructor(message = 'Unauthorized') {
super(message, 401);
}
}
class ConflictError extends AppError {
constructor(message = 'Resource conflict') {
super(message, 409);
}
}
module.exports = {
AppError,
NotFoundError,
ValidationError,
ForbiddenError,
UnauthorizedError,
ConflictError,
};

View File

@ -0,0 +1,53 @@
const EventEmitter = require('events');
const { logger } = require('./logger');
class AppEventEmitter extends EventEmitter {
emit(event, data) {
logger.debug({ event, hasListeners: this.listenerCount(event) > 0 }, 'Event emitted');
return super.emit(event, data);
}
async emitAsync(event, data) {
logger.debug({ event, listenerCount: this.listenerCount(event) }, 'Async event emitted');
const results = await Promise.allSettled(
this.listeners(event).map(listener => listener(data))
);
const failures = results.filter(r => r.status === 'rejected');
if (failures.length > 0) {
logger.warn({ event, failures: failures.map(f => f.reason?.message) }, 'Some event listeners failed');
}
return results;
}
onAsync(event, listener) {
this.on(event, async (data) => {
try {
await listener(data);
} catch (error) {
logger.error({ event, error: error.message }, 'Async event listener error');
}
});
}
}
const appEvents = new AppEventEmitter();
appEvents.on('user.created', (user) => {
logger.info({ userId: user.id, email: user.email }, 'User created event received');
});
appEvents.on('project.created', (project) => {
logger.info({ projectId: project.id, name: project.name }, 'Project created event received');
});
appEvents.on('project.published', (project) => {
logger.info({ projectId: project.id, slug: project.slug }, 'Project published event received');
});
appEvents.on('error', (error) => {
logger.error({ error: error.message }, 'Application event error');
});
module.exports = { appEvents, AppEventEmitter };

View File

@ -0,0 +1,7 @@
module.exports = {
...require('./errors'),
...require('./logger'),
...require('./circuit-breaker'),
...require('./events'),
envValidation: require('./env-validation'),
};

View File

@ -0,0 +1,46 @@
const pino = require('pino');
const crypto = require('crypto');
const isDevelopment = process.env.NODE_ENV === 'development';
const logger = pino({
level: process.env.LOG_LEVEL || 'info',
transport: isDevelopment
? { target: 'pino-pretty', options: { colorize: true } }
: undefined,
base: {
service: 'tour-builder-api',
env: process.env.NODE_ENV || 'development',
},
});
function requestLogger(req, res, next) {
const requestId = req.headers['x-request-id'] || crypto.randomUUID();
req.log = logger.child({ requestId });
req.requestId = requestId;
res.setHeader('X-Request-Id', requestId);
const start = Date.now();
res.on('finish', () => {
const duration = Date.now() - start;
const logData = {
method: req.method,
url: req.originalUrl || req.url,
status: res.statusCode,
duration,
userAgent: req.headers['user-agent'],
};
if (res.statusCode >= 500) {
req.log.error(logData, 'Request completed with server error');
} else if (res.statusCode >= 400) {
req.log.warn(logData, 'Request completed with client error');
} else {
req.log.info(logData, 'Request completed');
}
});
next();
}
module.exports = { logger, requestLogger };

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1 @@
{}

View File

@ -12,11 +12,11 @@
"@emotion/styled": "^11.11.0", "@emotion/styled": "^11.11.0",
"@mdi/js": "^7.4.47", "@mdi/js": "^7.4.47",
"@mui/material": "^6.3.0", "@mui/material": "^6.3.0",
"@mui/x-data-grid": "^6.19.2", "@mui/x-data-grid": "^7.0.0",
"@reduxjs/toolkit": "^2.1.0", "@reduxjs/toolkit": "^2.1.0",
"@tailwindcss/typography": "^0.5.13", "@tailwindcss/typography": "^0.5.13",
"@tinymce/tinymce-react": "^4.3.2", "@tinymce/tinymce-react": "^4.3.2",
"apexcharts": "^3.45.2", "apexcharts": "^5.0.0",
"axios": "^1.8.4", "axios": "^1.8.4",
"chart.js": "^4.4.1", "chart.js": "^4.4.1",
"chroma-js": "^2.4.2", "chroma-js": "^2.4.2",
@ -30,7 +30,7 @@
"intro.js": "^7.2.0", "intro.js": "^7.2.0",
"intro.js-react": "^1.0.0", "intro.js-react": "^1.0.0",
"jsonwebtoken": "^9.0.2", "jsonwebtoken": "^9.0.2",
"jwt-decode": "^3.1.2", "jwt-decode": "^4.0.0",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"moment": "^2.30.1", "moment": "^2.30.1",
"next": "^15.3.1", "next": "^15.3.1",
@ -38,21 +38,22 @@
"numeral": "^2.0.6", "numeral": "^2.0.6",
"query-string": "^8.1.0", "query-string": "^8.1.0",
"react": "^19.0.0", "react": "^19.0.0",
"react-apexcharts": "^1.4.1", "react-apexcharts": "^2.0.0",
"react-big-calendar": "^1.10.3", "react-big-calendar": "^1.19.0",
"react-chartjs-2": "^4.3.1", "react-chartjs-2": "^5.0.0",
"react-datepicker": "^4.10.0", "react-datepicker": "^7.0.0",
"react-dnd": "^16.0.1", "react-dnd": "^16.0.1",
"react-dnd-html5-backend": "^16.0.1", "react-dnd-html5-backend": "^16.0.1",
"react-dom": "^19.0.0", "react-dom": "^19.0.0",
"react-i18next": "^15.5.1", "react-i18next": "^15.5.1",
"react-redux": "^8.0.2", "react-redux": "^9.0.0",
"react-select": "^5.7.0", "react-select": "^5.7.0",
"react-select-async-paginate": "^0.7.9", "react-select-async-paginate": "^0.7.9",
"react-switch": "^7.0.0", "react-switch": "^7.0.0",
"react-toastify": "^11.0.2", "react-toastify": "^11.0.2",
"swr": "^1.3.0", "swr": "^2.0.0",
"uuid": "^9.0.0" "uuid": "^9.0.0",
"zod": "^4.3.6"
}, },
"devDependencies": { "devDependencies": {
"@tailwindcss/forms": "^0.5.7", "@tailwindcss/forms": "^0.5.7",

View File

@ -0,0 +1,99 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Offline - Tour Builder</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
background-color: #1f2937;
color: #ffffff;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 2rem;
text-align: center;
}
.container {
max-width: 400px;
}
.icon {
width: 80px;
height: 80px;
margin-bottom: 2rem;
opacity: 0.6;
}
h1 {
font-size: 1.5rem;
margin-bottom: 1rem;
font-weight: 600;
}
p {
color: #9ca3af;
margin-bottom: 2rem;
line-height: 1.6;
}
.retry-btn {
display: inline-block;
padding: 0.75rem 1.5rem;
background-color: #3b82f6;
color: #ffffff;
border: none;
border-radius: 0.5rem;
font-size: 1rem;
font-weight: 500;
cursor: pointer;
transition: background-color 0.2s;
}
.retry-btn:hover {
background-color: #2563eb;
}
.retry-btn:active {
background-color: #1d4ed8;
}
</style>
</head>
<body>
<div class="container">
<svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M18.364 5.636a9 9 0 010 12.728M5.636 18.364a9 9 0 010-12.728" />
<path d="M15.536 8.464a5 5 0 010 7.072M8.464 15.536a5 5 0 010-7.072" />
<circle cx="12" cy="12" r="1" fill="currentColor" />
</svg>
<h1>You're Offline</h1>
<p>
It looks like you've lost your internet connection.
Some features may not be available until you're back online.
</p>
<button class="retry-btn" onclick="window.location.reload()">
Try Again
</button>
</div>
<script>
// Auto-retry when connection is restored
window.addEventListener('online', () => {
window.location.reload();
});
</script>
</body>
</html>

210
frontend/public/sw.js Normal file
View File

@ -0,0 +1,210 @@
/**
* Tour Builder Platform - Service Worker
*
* Provides offline caching for PWA functionality.
* Caches tour assets (images, videos, audio) for offline viewing.
*/
const CACHE_NAME = 'tour-builder-v1';
const STATIC_CACHE_NAME = 'tour-builder-static-v1';
const DYNAMIC_CACHE_NAME = 'tour-builder-dynamic-v1';
// Static assets to cache on install
const STATIC_ASSETS = [
'/',
'/runtime',
'/offline.html',
];
// Asset types to cache
const CACHEABLE_EXTENSIONS = [
'.png', '.jpg', '.jpeg', '.gif', '.webp', '.svg', '.ico',
'.mp4', '.webm', '.mov',
'.mp3', '.wav', '.ogg', '.m4a',
'.woff', '.woff2', '.ttf', '.eot',
'.css', '.js',
];
// Check if request should be cached
const isCacheableRequest = (request) => {
const url = new URL(request.url);
// Don't cache API requests (except static assets from /file/download)
if (url.pathname.startsWith('/api/') && !url.pathname.includes('/file/download')) {
return false;
}
// Cache known asset extensions
const hasExtension = CACHEABLE_EXTENSIONS.some((ext) => url.pathname.toLowerCase().endsWith(ext));
if (hasExtension) {
return true;
}
// Cache file downloads (S3 presigned URLs, CDN assets)
if (url.pathname.includes('/file/download') || url.hostname.includes('amazonaws.com') || url.hostname.includes('cloudfront.net')) {
return true;
}
return false;
};
// Install event - cache static assets
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(STATIC_CACHE_NAME).then((cache) => {
console.log('[SW] Caching static assets');
return cache.addAll(STATIC_ASSETS).catch((error) => {
console.warn('[SW] Failed to cache some static assets:', error);
});
})
);
self.skipWaiting();
});
// Activate event - clean up old caches
self.addEventListener('activate', (event) => {
event.waitUntil(
caches.keys().then((cacheNames) => {
return Promise.all(
cacheNames
.filter((name) => {
return name.startsWith('tour-builder-') && name !== STATIC_CACHE_NAME && name !== DYNAMIC_CACHE_NAME;
})
.map((name) => {
console.log('[SW] Deleting old cache:', name);
return caches.delete(name);
})
);
})
);
self.clients.claim();
});
// Fetch event - serve from cache or network
self.addEventListener('fetch', (event) => {
const { request } = event;
// Only handle GET requests
if (request.method !== 'GET') {
return;
}
// Skip non-http(s) requests
if (!request.url.startsWith('http')) {
return;
}
event.respondWith(
caches.match(request).then((cachedResponse) => {
// Return cached response if available
if (cachedResponse) {
// Optionally update cache in background (stale-while-revalidate)
if (isCacheableRequest(request)) {
event.waitUntil(
fetch(request)
.then((networkResponse) => {
if (networkResponse && networkResponse.status === 200) {
const responseToCache = networkResponse.clone();
caches.open(DYNAMIC_CACHE_NAME).then((cache) => {
cache.put(request, responseToCache);
});
}
})
.catch(() => {
// Network failed, but we have cache - that's fine
})
);
}
return cachedResponse;
}
// Fetch from network
return fetch(request)
.then((networkResponse) => {
// Cache successful responses for cacheable requests
if (networkResponse && networkResponse.status === 200 && isCacheableRequest(request)) {
const responseToCache = networkResponse.clone();
caches.open(DYNAMIC_CACHE_NAME).then((cache) => {
cache.put(request, responseToCache);
});
}
return networkResponse;
})
.catch((error) => {
console.warn('[SW] Fetch failed:', error);
// Return offline page for navigation requests
if (request.mode === 'navigate') {
return caches.match('/offline.html');
}
// Return empty response for assets
return new Response('', {
status: 503,
statusText: 'Service Unavailable',
});
});
})
);
});
// Message event - handle commands from main thread
self.addEventListener('message', (event) => {
const { type, payload } = event.data || {};
switch (type) {
case 'CACHE_ASSETS':
// Cache specific assets for a project/page
if (Array.isArray(payload?.urls)) {
event.waitUntil(
caches.open(DYNAMIC_CACHE_NAME).then((cache) => {
return Promise.all(
payload.urls.map((url) =>
fetch(url)
.then((response) => {
if (response.status === 200) {
return cache.put(url, response);
}
})
.catch((error) => {
console.warn('[SW] Failed to cache asset:', url, error);
})
)
);
})
);
}
break;
case 'CLEAR_CACHE':
// Clear all dynamic caches
event.waitUntil(
caches.delete(DYNAMIC_CACHE_NAME).then(() => {
console.log('[SW] Dynamic cache cleared');
})
);
break;
case 'GET_CACHE_STATUS':
// Return current cache status
event.waitUntil(
caches.open(DYNAMIC_CACHE_NAME).then((cache) => {
return cache.keys().then((keys) => {
event.source.postMessage({
type: 'CACHE_STATUS',
payload: {
cachedCount: keys.length,
urls: keys.map((request) => request.url),
},
});
});
})
);
break;
default:
break;
}
});
console.log('[SW] Service worker loaded');

View File

@ -1,21 +1,22 @@
import type { ColorButtonKey } from './interfaces' import type { ColorButtonKey } from './interfaces';
export const gradientBgBase = 'bg-gradient-to-tr' export const gradientBgBase = 'bg-gradient-to-tr';
export const colorBgBase = "bg-violet-50/50" export const colorBgBase = 'bg-violet-50/50';
export const gradientBgPurplePink = `${gradientBgBase} from-purple-400 via-pink-500 to-red-500` export const gradientBgPurplePink = `${gradientBgBase} from-purple-400 via-pink-500 to-red-500`;
export const gradientBgViolet = `${gradientBgBase} ${colorBgBase}` export const gradientBgViolet = `${gradientBgBase} ${colorBgBase}`;
export const gradientBgDark = `${gradientBgBase} from-dark-700 via-dark-900 to-dark-800`; export const gradientBgDark = `${gradientBgBase} from-dark-700 via-dark-900 to-dark-800`;
export const gradientBgPinkRed = `${gradientBgBase} from-pink-400 via-red-500 to-yellow-500` export const gradientBgPinkRed = `${gradientBgBase} from-pink-400 via-red-500 to-yellow-500`;
export const colorsBgLight = { export const colorsBgLight = {
white: 'bg-white text-black', white: 'bg-white text-black',
light: ' bg-white text-black text-black dark:bg-dark-900 dark:text-white', light: ' bg-white text-black text-black dark:bg-dark-900 dark:text-white',
contrast: 'bg-gray-800 text-white dark:bg-white dark:text-black', contrast: 'bg-gray-800 text-white dark:bg-white dark:text-black',
success: 'bg-emerald-500 border-emerald-500 dark:bg-pavitra-blue dark:border-pavitra-blue text-white', success:
'bg-emerald-500 border-emerald-500 dark:bg-pavitra-blue dark:border-pavitra-blue text-white',
danger: 'bg-red-500 border-red-500 text-white', danger: 'bg-red-500 border-red-500 text-white',
warning: 'bg-yellow-500 border-yellow-500 text-white', warning: 'bg-yellow-500 border-yellow-500 text-white',
info: 'bg-blue-500 border-blue-500 dark:bg-pavitra-blue dark:border-pavitra-blue text-white', info: 'bg-blue-500 border-blue-500 dark:bg-pavitra-blue dark:border-pavitra-blue text-white',
} };
export const colorsText = { export const colorsText = {
white: 'text-black dark:text-slate-100', white: 'text-black dark:text-slate-100',
@ -30,7 +31,9 @@ export const colorsText = {
export const colorsOutline = { export const colorsOutline = {
white: [colorsText.white, 'border-gray-100'].join(' '), white: [colorsText.white, 'border-gray-100'].join(' '),
light: [colorsText.light, 'border-gray-100'].join(' '), light: [colorsText.light, 'border-gray-100'].join(' '),
contrast: [colorsText.contrast, 'border-gray-900 dark:border-slate-100'].join(' '), contrast: [colorsText.contrast, 'border-gray-900 dark:border-slate-100'].join(
' ',
),
success: [colorsText.success, 'border-emerald-500'].join(' '), success: [colorsText.success, 'border-emerald-500'].join(' '),
danger: [colorsText.danger, 'border-red-500'].join(' '), danger: [colorsText.danger, 'border-red-500'].join(' '),
warning: [colorsText.warning, 'border-yellow-500'].join(' '), warning: [colorsText.warning, 'border-yellow-500'].join(' '),
@ -41,10 +44,10 @@ export const getButtonColor = (
color: ColorButtonKey, color: ColorButtonKey,
isOutlined: boolean, isOutlined: boolean,
hasHover: boolean, hasHover: boolean,
isActive = false isActive = false,
) => { ) => {
if (color === 'void') { if (color === 'void') {
return '' return '';
} }
const colors = { const colors = {
@ -56,7 +59,7 @@ export const getButtonColor = (
success: 'ring-emerald-300 dark:ring-pavitra-blue', success: 'ring-emerald-300 dark:ring-pavitra-blue',
danger: 'ring-red-300 dark:ring-red-700', danger: 'ring-red-300 dark:ring-red-700',
warning: 'ring-yellow-300 dark:ring-yellow-700', warning: 'ring-yellow-300 dark:ring-yellow-700',
info: "ring-blue-300 dark:ring-pavitra-blue", info: 'ring-blue-300 dark:ring-pavitra-blue',
}, },
active: { active: {
white: 'bg-gray-100', white: 'bg-gray-100',
@ -76,7 +79,7 @@ export const getButtonColor = (
success: 'bg-emerald-600 dark:bg-pavitra-blue text-white', success: 'bg-emerald-600 dark:bg-pavitra-blue text-white',
danger: 'bg-red-600 text-white dark:bg-red-500 ', danger: 'bg-red-600 text-white dark:bg-red-500 ',
warning: 'bg-yellow-600 dark:bg-yellow-500 text-white', warning: 'bg-yellow-600 dark:bg-yellow-500 text-white',
info: " bg-blue-600 dark:bg-pavitra-blue text-white ", info: ' bg-blue-600 dark:bg-pavitra-blue text-white ',
}, },
bgHover: { bgHover: {
white: 'hover:bg-gray-100', white: 'hover:bg-gray-100',
@ -84,12 +87,12 @@ export const getButtonColor = (
lightDark: 'hover:bg-gray-200 hover:dark:bg-slate-700', lightDark: 'hover:bg-gray-200 hover:dark:bg-slate-700',
contrast: 'hover:bg-gray-700 hover:dark:bg-slate-100', contrast: 'hover:bg-gray-700 hover:dark:bg-slate-100',
success: success:
'hover:bg-emerald-700 hover:border-emerald-700 hover:dark:bg-pavitra-blue hover:dark:border-pavitra-blue', 'hover:bg-emerald-700 hover:border-emerald-700 hover:dark:bg-pavitra-blue hover:dark:border-pavitra-blue',
danger: danger:
'hover:bg-red-700 hover:border-red-700 hover:dark:bg-red-600 hover:dark:border-red-600', 'hover:bg-red-700 hover:border-red-700 hover:dark:bg-red-600 hover:dark:border-red-600',
warning: warning:
'hover:bg-yellow-700 hover:border-yellow-700 hover:dark:bg-yellow-600 hover:dark:border-yellow-600', 'hover:bg-yellow-700 hover:border-yellow-700 hover:dark:bg-yellow-600 hover:dark:border-yellow-600',
info: "hover:bg-blue-700 hover:border-blue-700 hover:dark:bg-pavitra-blue/80 hover:dark:border-pavitra-blue/80", info: 'hover:bg-blue-700 hover:border-blue-700 hover:dark:bg-pavitra-blue/80 hover:dark:border-pavitra-blue/80',
}, },
borders: { borders: {
white: 'border-white', white: 'border-white',
@ -99,7 +102,7 @@ export const getButtonColor = (
success: 'border-emerald-600 dark:border-pavitra-blue', success: 'border-emerald-600 dark:border-pavitra-blue',
danger: 'border-red-600 dark:border-red-500', danger: 'border-red-600 dark:border-red-500',
warning: 'border-yellow-600 dark:border-yellow-500', warning: 'border-yellow-600 dark:border-yellow-500',
info: "border-blue-600 border-blue-600 dark:border-pavitra-blue", info: 'border-blue-600 border-blue-600 dark:border-pavitra-blue',
}, },
text: { text: {
contrast: 'dark:text-slate-100', contrast: 'dark:text-slate-100',
@ -111,28 +114,32 @@ export const getButtonColor = (
outlineHover: { outlineHover: {
contrast: contrast:
'hover:bg-gray-800 hover:text-gray-100 hover:dark:bg-slate-100 hover:dark:text-black', 'hover:bg-gray-800 hover:text-gray-100 hover:dark:bg-slate-100 hover:dark:text-black',
success: 'hover:bg-emerald-600 hover:text-white hover:text-white hover:dark:text-white hover:dark:border-pavitra-blue', success:
'hover:bg-emerald-600 hover:text-white hover:text-white hover:dark:text-white hover:dark:border-pavitra-blue',
danger: danger:
'hover:bg-red-600 hover:text-white hover:text-white hover:dark:text-white hover:dark:border-red-600', 'hover:bg-red-600 hover:text-white hover:text-white hover:dark:text-white hover:dark:border-red-600',
warning: warning:
'hover:bg-yellow-600 hover:text-white hover:text-white hover:dark:text-white hover:dark:border-yellow-600', 'hover:bg-yellow-600 hover:text-white hover:text-white hover:dark:text-white hover:dark:border-yellow-600',
info: "hover:bg-blue-600 hover:bg-blue-600 hover:text-white hover:dark:text-white hover:dark:border-pavitra-blue", info: 'hover:bg-blue-600 hover:bg-blue-600 hover:text-white hover:dark:text-white hover:dark:border-pavitra-blue',
}, },
} };
const isOutlinedProcessed = isOutlined && ['white', 'whiteDark', 'lightDark'].indexOf(color) < 0 const isOutlinedProcessed =
isOutlined && ['white', 'whiteDark', 'lightDark'].indexOf(color) < 0;
const base = [colors.borders[color], colors.ring[color]] const base = [colors.borders[color], colors.ring[color]];
if (isActive) { if (isActive) {
base.push(colors.active[color]) base.push(colors.active[color]);
} else { } else {
base.push(isOutlinedProcessed ? colors.text[color] : colors.bg[color]) base.push(isOutlinedProcessed ? colors.text[color] : colors.bg[color]);
} }
if (hasHover) { if (hasHover) {
base.push(isOutlinedProcessed ? colors.outlineHover[color] : colors.bgHover[color]) base.push(
isOutlinedProcessed ? colors.outlineHover[color] : colors.bgHover[color],
);
} }
return base.join(' ') return base.join(' ');
} };

View File

@ -4,12 +4,11 @@ import ListActionsPopover from '../ListActionsPopover';
import { useAppSelector } from '../../stores/hooks'; import { useAppSelector } from '../../stores/hooks';
import dataFormatter from '../../helpers/dataFormatter'; import dataFormatter from '../../helpers/dataFormatter';
import { Pagination } from '../Pagination'; import { Pagination } from '../Pagination';
import {saveFile} from "../../helpers/fileSaver"; import { saveFile } from '../../helpers/fileSaver';
import LoadingSpinner from "../LoadingSpinner"; import LoadingSpinner from '../LoadingSpinner';
import Link from 'next/link'; import Link from 'next/link';
import {hasPermission} from "../../helpers/userPermissions"; import { hasPermission } from '../../helpers/userPermissions';
type Props = { type Props = {
access_logs: any[]; access_logs: any[];
@ -28,17 +27,16 @@ const CardAccess_logs = ({
numPages, numPages,
onPageChange, onPageChange,
}: Props) => { }: Props) => {
const asideScrollbarsStyle = useAppSelector( const asideScrollbarsStyle = useAppSelector(
(state) => state.style.asideScrollbarsStyle, (state) => state.style.asideScrollbarsStyle,
); );
const bgColor = useAppSelector((state) => state.style.cardsColor); const bgColor = useAppSelector((state) => state.style.cardsColor);
const darkMode = useAppSelector((state) => state.style.darkMode); const darkMode = useAppSelector((state) => state.style.darkMode);
const corners = useAppSelector((state) => state.style.corners); const corners = useAppSelector((state) => state.style.corners);
const focusRing = useAppSelector((state) => state.style.focusRingColor); const focusRing = useAppSelector((state) => state.style.focusRingColor);
const currentUser = useAppSelector((state) => state.auth.currentUser);
const hasUpdatePermission = hasPermission(currentUser, 'UPDATE_ACCESS_LOGS')
const currentUser = useAppSelector((state) => state.auth.currentUser);
const hasUpdatePermission = hasPermission(currentUser, 'UPDATE_ACCESS_LOGS');
return ( return (
<div className={'p-4'}> <div className={'p-4'}>
@ -47,122 +45,108 @@ const CardAccess_logs = ({
role='list' role='list'
className='grid grid-cols-1 gap-x-6 gap-y-8 lg:grid-cols-3 2xl:grid-cols-4 xl:gap-x-8' className='grid grid-cols-1 gap-x-6 gap-y-8 lg:grid-cols-3 2xl:grid-cols-4 xl:gap-x-8'
> >
{!loading && access_logs.map((item, index) => ( {!loading &&
<li access_logs.map((item, index) => (
key={item.id} <li
className={`overflow-hidden ${corners !== 'rounded-full'? corners : 'rounded-3xl'} border ${focusRing} border-gray-200 dark:border-dark-700 ${ key={item.id}
className={`overflow-hidden ${corners !== 'rounded-full' ? corners : 'rounded-3xl'} border ${focusRing} border-gray-200 dark:border-dark-700 ${
darkMode ? 'aside-scrollbars-[slate]' : asideScrollbarsStyle darkMode ? 'aside-scrollbars-[slate]' : asideScrollbarsStyle
}`} }`}
> >
<div
<div className={`flex items-center ${bgColor} p-6 gap-x-4 border-b border-gray-900/5 bg-gray-50 dark:bg-dark-800 relative`}> className={`flex items-center ${bgColor} p-6 gap-x-4 border-b border-gray-900/5 bg-gray-50 dark:bg-dark-800 relative`}
>
<Link href={`/access_logs/access_logs-view/?id=${item.id}`} className='text-lg font-bold leading-6 line-clamp-1'> <Link
{item.path} href={`/access_logs/access_logs-view/?id=${item.id}`}
className='text-lg font-bold leading-6 line-clamp-1'
>
{item.path}
</Link> </Link>
<div className='ml-auto '>
<div className='ml-auto '> <ListActionsPopover
<ListActionsPopover onDelete={onDelete}
onDelete={onDelete} itemId={item.id}
itemId={item.id} pathEdit={`/access_logs/access_logs-edit/?id=${item.id}`}
pathEdit={`/access_logs/access_logs-edit/?id=${item.id}`} pathView={`/access_logs/access_logs-view/?id=${item.id}`}
pathView={`/access_logs/access_logs-view/?id=${item.id}`} hasUpdatePermission={hasUpdatePermission}
/>
hasUpdatePermission={hasUpdatePermission} </div>
/>
</div> </div>
</div> <dl className='divide-y divide-stone-300 dark:divide-dark-700 px-6 py-4 text-sm leading-6 h-64 overflow-y-auto'>
<dl className='divide-y divide-stone-300 dark:divide-dark-700 px-6 py-4 text-sm leading-6 h-64 overflow-y-auto'>
<div className='flex justify-between gap-x-4 py-3'> <div className='flex justify-between gap-x-4 py-3'>
<dt className=' text-gray-500 dark:text-dark-600'>Project</dt> <dt className=' text-gray-500 dark:text-dark-600'>
<dd className='flex items-start gap-x-2'> Project
<div className='font-medium line-clamp-4'> </dt>
{ dataFormatter.projectsOneListFormatter(item.project) } <dd className='flex items-start gap-x-2'>
</div> <div className='font-medium line-clamp-4'>
</dd> {dataFormatter.projectsOneListFormatter(item.project)}
</div>
</dd>
</div> </div>
<div className='flex justify-between gap-x-4 py-3'> <div className='flex justify-between gap-x-4 py-3'>
<dt className=' text-gray-500 dark:text-dark-600'>Environment</dt> <dt className=' text-gray-500 dark:text-dark-600'>
<dd className='flex items-start gap-x-2'> Environment
<div className='font-medium line-clamp-4'> </dt>
{ item.environment } <dd className='flex items-start gap-x-2'>
</div> <div className='font-medium line-clamp-4'>
</dd> {item.environment}
</div>
</dd>
</div> </div>
<div className='flex justify-between gap-x-4 py-3'> <div className='flex justify-between gap-x-4 py-3'>
<dt className=' text-gray-500 dark:text-dark-600'>User</dt> <dt className=' text-gray-500 dark:text-dark-600'>User</dt>
<dd className='flex items-start gap-x-2'> <dd className='flex items-start gap-x-2'>
<div className='font-medium line-clamp-4'> <div className='font-medium line-clamp-4'>
{ dataFormatter.usersOneListFormatter(item.user) } {dataFormatter.usersOneListFormatter(item.user)}
</div> </div>
</dd> </dd>
</div> </div>
<div className='flex justify-between gap-x-4 py-3'> <div className='flex justify-between gap-x-4 py-3'>
<dt className=' text-gray-500 dark:text-dark-600'>Path</dt> <dt className=' text-gray-500 dark:text-dark-600'>Path</dt>
<dd className='flex items-start gap-x-2'> <dd className='flex items-start gap-x-2'>
<div className='font-medium line-clamp-4'> <div className='font-medium line-clamp-4'>{item.path}</div>
{ item.path } </dd>
</div>
</dd>
</div> </div>
<div className='flex justify-between gap-x-4 py-3'> <div className='flex justify-between gap-x-4 py-3'>
<dt className=' text-gray-500 dark:text-dark-600'>IPaddress</dt> <dt className=' text-gray-500 dark:text-dark-600'>
<dd className='flex items-start gap-x-2'> IPaddress
<div className='font-medium line-clamp-4'> </dt>
{ item.ip_address } <dd className='flex items-start gap-x-2'>
</div> <div className='font-medium line-clamp-4'>
</dd> {item.ip_address}
</div>
</dd>
</div> </div>
<div className='flex justify-between gap-x-4 py-3'> <div className='flex justify-between gap-x-4 py-3'>
<dt className=' text-gray-500 dark:text-dark-600'>Useragent</dt> <dt className=' text-gray-500 dark:text-dark-600'>
<dd className='flex items-start gap-x-2'> Useragent
<div className='font-medium line-clamp-4'> </dt>
{ item.user_agent } <dd className='flex items-start gap-x-2'>
</div> <div className='font-medium line-clamp-4'>
</dd> {item.user_agent}
</div>
</dd>
</div> </div>
<div className='flex justify-between gap-x-4 py-3'> <div className='flex justify-between gap-x-4 py-3'>
<dt className=' text-gray-500 dark:text-dark-600'>Accessedat</dt> <dt className=' text-gray-500 dark:text-dark-600'>
<dd className='flex items-start gap-x-2'> Accessedat
<div className='font-medium line-clamp-4'> </dt>
{ dataFormatter.dateTimeFormatter(item.accessed_at) } <dd className='flex items-start gap-x-2'>
</div> <div className='font-medium line-clamp-4'>
</dd> {dataFormatter.dateTimeFormatter(item.accessed_at)}
</div>
</dd>
</div> </div>
</dl>
</li>
))}
</dl>
</li>
))}
{!loading && access_logs.length === 0 && ( {!loading && access_logs.length === 0 && (
<div className='col-span-full flex items-center justify-center h-40'> <div className='col-span-full flex items-center justify-center h-40'>
<p className=''>No data to display</p> <p className=''>No data to display</p>

View File

@ -2,135 +2,122 @@ import React from 'react';
import CardBox from '../CardBox'; import CardBox from '../CardBox';
import ImageField from '../ImageField'; import ImageField from '../ImageField';
import dataFormatter from '../../helpers/dataFormatter'; import dataFormatter from '../../helpers/dataFormatter';
import {saveFile} from "../../helpers/fileSaver"; import { saveFile } from '../../helpers/fileSaver';
import ListActionsPopover from "../ListActionsPopover"; import ListActionsPopover from '../ListActionsPopover';
import {useAppSelector} from "../../stores/hooks"; import { useAppSelector } from '../../stores/hooks';
import {Pagination} from "../Pagination"; import { Pagination } from '../Pagination';
import LoadingSpinner from "../LoadingSpinner"; import LoadingSpinner from '../LoadingSpinner';
import Link from 'next/link'; import Link from 'next/link';
import {hasPermission} from "../../helpers/userPermissions"; import { hasPermission } from '../../helpers/userPermissions';
type Props = { type Props = {
access_logs: any[]; access_logs: any[];
loading: boolean; loading: boolean;
onDelete: (id: string) => void; onDelete: (id: string) => void;
currentPage: number; currentPage: number;
numPages: number; numPages: number;
onPageChange: (page: number) => void; onPageChange: (page: number) => void;
}; };
const ListAccess_logs = ({ access_logs, loading, onDelete, currentPage, numPages, onPageChange }: Props) => { const ListAccess_logs = ({
access_logs,
loading,
onDelete,
currentPage,
numPages,
onPageChange,
}: Props) => {
const currentUser = useAppSelector((state) => state.auth.currentUser);
const hasUpdatePermission = hasPermission(currentUser, 'UPDATE_ACCESS_LOGS');
const currentUser = useAppSelector((state) => state.auth.currentUser); const corners = useAppSelector((state) => state.style.corners);
const hasUpdatePermission = hasPermission(currentUser, 'UPDATE_ACCESS_LOGS') const bgColor = useAppSelector((state) => state.style.cardsColor);
const corners = useAppSelector((state) => state.style.corners); return (
const bgColor = useAppSelector((state) => state.style.cardsColor); <>
<div className='relative overflow-x-auto p-4 space-y-4'>
{loading && <LoadingSpinner />}
{!loading &&
access_logs.map((item) => (
<div key={item.id}>
<CardBox hasTable isList className={'rounded shadow-none'}>
<div
className={`flex rounded dark:bg-dark-900 border border-stone-300 items-center overflow-hidden`}
>
<Link
href={`/access_logs/access_logs-view/?id=${item.id}`}
className={
'flex-1 px-4 py-6 h-24 flex divide-x-2 divide-stone-300 items-center overflow-hidden`}> dark:divide-dark-700 overflow-x-auto'
}
>
<div className={'flex-1 px-3'}>
<p className={'text-xs text-gray-500 '}>Project</p>
<p className={'line-clamp-2'}>
{dataFormatter.projectsOneListFormatter(item.project)}
</p>
</div>
<div className={'flex-1 px-3'}>
<p className={'text-xs text-gray-500 '}>Environment</p>
<p className={'line-clamp-2'}>{item.environment}</p>
</div>
return ( <div className={'flex-1 px-3'}>
<> <p className={'text-xs text-gray-500 '}>User</p>
<div className='relative overflow-x-auto p-4 space-y-4'> <p className={'line-clamp-2'}>
{loading && <LoadingSpinner />} {dataFormatter.usersOneListFormatter(item.user)}
{!loading && access_logs.map((item) => ( </p>
<div key={item.id}> </div>
<CardBox hasTable isList className={'rounded shadow-none'}>
<div className={`flex rounded dark:bg-dark-900 border border-stone-300 items-center overflow-hidden`}>
<Link <div className={'flex-1 px-3'}>
href={`/access_logs/access_logs-view/?id=${item.id}`} <p className={'text-xs text-gray-500 '}>Path</p>
className={ <p className={'line-clamp-2'}>{item.path}</p>
'flex-1 px-4 py-6 h-24 flex divide-x-2 divide-stone-300 items-center overflow-hidden`}> dark:divide-dark-700 overflow-x-auto' </div>
}
>
<div className={'flex-1 px-3'}>
<p className={'text-xs text-gray-500 '}>IPaddress</p>
<p className={'line-clamp-2'}>{item.ip_address}</p>
</div>
<div className={'flex-1 px-3'}> <div className={'flex-1 px-3'}>
<p className={'text-xs text-gray-500 '}>Project</p> <p className={'text-xs text-gray-500 '}>Useragent</p>
<p className={'line-clamp-2'}>{ dataFormatter.projectsOneListFormatter(item.project) }</p> <p className={'line-clamp-2'}>{item.user_agent}</p>
</div> </div>
<div className={'flex-1 px-3'}>
<p className={'text-xs text-gray-500 '}>Accessedat</p>
<p className={'line-clamp-2'}>
<div className={'flex-1 px-3'}> {dataFormatter.dateTimeFormatter(item.accessed_at)}
<p className={'text-xs text-gray-500 '}>Environment</p> </p>
<p className={'line-clamp-2'}>{ item.environment }</p> </div>
</div> </Link>
<ListActionsPopover
onDelete={onDelete}
itemId={item.id}
pathEdit={`/access_logs/access_logs-edit/?id=${item.id}`}
<div className={'flex-1 px-3'}> pathView={`/access_logs/access_logs-view/?id=${item.id}`}
<p className={'text-xs text-gray-500 '}>User</p> hasUpdatePermission={hasUpdatePermission}
<p className={'line-clamp-2'}>{ dataFormatter.usersOneListFormatter(item.user) }</p> />
</div> </div>
</CardBox>
<div className={'flex-1 px-3'}>
<p className={'text-xs text-gray-500 '}>Path</p>
<p className={'line-clamp-2'}>{ item.path }</p>
</div>
<div className={'flex-1 px-3'}>
<p className={'text-xs text-gray-500 '}>IPaddress</p>
<p className={'line-clamp-2'}>{ item.ip_address }</p>
</div>
<div className={'flex-1 px-3'}>
<p className={'text-xs text-gray-500 '}>Useragent</p>
<p className={'line-clamp-2'}>{ item.user_agent }</p>
</div>
<div className={'flex-1 px-3'}>
<p className={'text-xs text-gray-500 '}>Accessedat</p>
<p className={'line-clamp-2'}>{ dataFormatter.dateTimeFormatter(item.accessed_at) }</p>
</div>
</Link>
<ListActionsPopover
onDelete={onDelete}
itemId={item.id}
pathEdit={`/access_logs/access_logs-edit/?id=${item.id}`}
pathView={`/access_logs/access_logs-view/?id=${item.id}`}
hasUpdatePermission={hasUpdatePermission}
/>
</div>
</CardBox>
</div>
))}
{!loading && access_logs.length === 0 && (
<div className='col-span-full flex items-center justify-center h-40'>
<p className=''>No data to display</p>
</div>
)}
</div> </div>
<div className={'flex items-center justify-center my-6'}> ))}
<Pagination {!loading && access_logs.length === 0 && (
currentPage={currentPage} <div className='col-span-full flex items-center justify-center h-40'>
numPages={numPages} <p className=''>No data to display</p>
setCurrentPage={onPageChange} </div>
/> )}
</div> </div>
</> <div className={'flex items-center justify-center my-6'}>
) <Pagination
currentPage={currentPage}
numPages={numPages}
setCurrentPage={onPageChange}
/>
</div>
</>
);
}; };
export default ListAccess_logs export default ListAccess_logs;

View File

@ -1,463 +1,48 @@
import React, { useEffect, useState, useMemo } from 'react' /**
import { createPortal } from 'react-dom'; * Access Logs Table Component
import { ToastContainer, toast } from 'react-toastify'; */
import BaseButton from '../BaseButton'
import CardBoxModal from '../CardBoxModal' import React from 'react';
import CardBox from "../CardBox"; import GenericTable from '../Generic/GenericTable';
import { fetch, update, deleteItem, setRefetch, deleteItemsByIds } from '../../stores/access_logs/access_logsSlice'
import { useAppDispatch, useAppSelector } from '../../stores/hooks'
import { useRouter } from 'next/router'
import { Field, Form, Formik } from "formik";
import { import {
DataGrid, fetch,
GridColDef, update,
} from '@mui/x-data-grid'; deleteItem,
import {loadColumns} from "./configureAccess_logsCols"; setRefetch,
import _ from 'lodash'; deleteItemsByIds,
import dataFormatter from '../../helpers/dataFormatter' } from '../../stores/access_logs/access_logsSlice';
import {dataGridStyles} from "../../styles"; import { loadColumns } from './configureAccess_logsCols';
import type { AccessLog } from '../../types/entities';
import type { RootState } from '../../stores/store';
import type { Filter, FilterItem } from '../../types/filters';
interface TableAccess_logsProps {
filterItems: FilterItem[];
const perPage = 10 setFilterItems: (items: FilterItem[]) => void;
filters: Filter[];
const TableSampleAccess_logs = ({ filterItems, setFilterItems, filters, showGrid }) => { showGrid?: boolean;
const notify = (type, msg) => toast( msg, {type, position: "bottom-center"});
const dispatch = useAppDispatch();
const router = useRouter();
const pagesList = [];
const [id, setId] = useState(null);
const [currentPage, setCurrentPage] = useState(0);
const [filterRequest, setFilterRequest] = React.useState('');
const [columns, setColumns] = useState<GridColDef[]>([]);
const [selectedRows, setSelectedRows] = useState([]);
const [sortModel, setSortModel] = useState([
{
field: '',
sort: 'desc',
},
]);
const { access_logs, loading, count, notify: access_logsNotify, refetch } = useAppSelector((state) => state.access_logs)
const { currentUser } = useAppSelector((state) => state.auth);
const focusRing = useAppSelector((state) => state.style.focusRingColor);
const bgColor = useAppSelector((state) => state.style.bgLayoutColor);
const corners = useAppSelector((state) => state.style.corners);
const numPages = Math.floor(count / perPage) === 0 ? 1 : Math.ceil(count / perPage);
for (let i = 0; i < numPages; i++) {
pagesList.push(i);
}
const loadData = async (page = currentPage, request = filterRequest) => {
if (page !== currentPage) setCurrentPage(page);
if (request !== filterRequest) setFilterRequest(request);
const { sort, field } = sortModel[0];
const query = `?page=${page}&limit=${perPage}${request}&sort=${sort}&field=${field}`;
dispatch(fetch({ limit: perPage, page, query }));
};
useEffect(() => {
if (access_logsNotify.showNotification) {
notify(access_logsNotify.typeNotification, access_logsNotify.textNotification);
}
}, [access_logsNotify.showNotification]);
useEffect(() => {
if (!currentUser) return;
loadData();
}, [sortModel, currentUser]);
useEffect(() => {
if (refetch) {
loadData(0);
dispatch(setRefetch(false));
}
}, [refetch, dispatch]);
const [isModalInfoActive, setIsModalInfoActive] = useState(false)
const [isModalTrashActive, setIsModalTrashActive] = useState(false)
const handleModalAction = () => {
setIsModalInfoActive(false)
setIsModalTrashActive(false)
}
const handleDeleteModalAction = (id: string) => {
setId(id)
setIsModalTrashActive(true)
}
const handleDeleteAction = async () => {
if (id) {
await dispatch(deleteItem(id));
await loadData(0);
setIsModalTrashActive(false);
}
};
const generateFilterRequests = useMemo(() => {
let request = '&';
filterItems.forEach((item) => {
const isRangeFilter = filters.find(
(filter) =>
filter.title === item.fields.selectedField &&
(filter.number || filter.date),
);
if (isRangeFilter) {
const from = item.fields.filterValueFrom;
const to = item.fields.filterValueTo;
if (from) {
request += `${item.fields.selectedField}Range=${from}&`;
}
if (to) {
request += `${item.fields.selectedField}Range=${to}&`;
}
} else {
const value = item.fields.filterValue;
if (value) {
request += `${item.fields.selectedField}=${value}&`;
}
}
});
return request;
}, [filterItems, filters]);
const deleteFilter = (value) => {
const newItems = filterItems.filter((item) => item.id !== value);
if (newItems.length) {
setFilterItems(newItems);
} else {
loadData(0, '');
setFilterItems(newItems);
}
};
const handleSubmit = () => {
loadData(0, generateFilterRequests);
};
const handleChange = (id) => (e) => {
const value = e.target.value;
const name = e.target.name;
setFilterItems(
filterItems.map((item) => {
if (item.id !== id) return item;
if (name === 'selectedField') return { id, fields: { [name]: value } };
return { id, fields: { ...item.fields, [name]: value } }
}),
);
};
const handleReset = () => {
setFilterItems([]);
loadData(0, '');
};
const onPageChange = (page: number) => {
loadData(page);
setCurrentPage(page);
};
useEffect(() => {
if (!currentUser) return;
loadColumns(
handleDeleteModalAction,
`access_logs`,
currentUser,
).then((newCols) => setColumns(newCols));
}, [currentUser]);
const handleTableSubmit = async (id: string, data) => {
if (!_.isEmpty(data)) {
await dispatch(update({ id, data }))
.unwrap()
.then((res) => res)
.catch((err) => {
throw new Error(err);
});
}
};
const onDeleteRows = async (selectedRows) => {
await dispatch(deleteItemsByIds(selectedRows));
await loadData(0);
};
const controlClasses =
'w-full py-2 px-2 my-2 rounded dark:placeholder-gray-400 ' +
` ${bgColor} ${focusRing} ${corners} ` +
'dark:bg-slate-800 border';
const dataGrid = (
<div className='relative overflow-x-auto'>
<DataGrid
autoHeight
rowHeight={64}
sx={dataGridStyles}
className={'datagrid--table'}
getRowClassName={() => `datagrid--row`}
rows={access_logs ?? []}
columns={columns}
initialState={{
pagination: {
paginationModel: {
pageSize: 10,
},
},
}}
disableRowSelectionOnClick
onProcessRowUpdateError={(params) => {
console.log('Error', params);
}}
processRowUpdate={async (newRow, oldRow) => {
const data = dataFormatter.dataGridEditFormatter(newRow);
try {
await handleTableSubmit(newRow.id, data);
return newRow;
} catch {
return oldRow;
}
}}
sortingMode={'server'}
checkboxSelection
onRowSelectionModelChange={(ids) => {
setSelectedRows(ids)
}}
onSortModelChange={(params) => {
params.length
? setSortModel(params)
: setSortModel([{ field: '', sort: 'desc' }]);
}}
rowCount={count}
pageSizeOptions={[10]}
paginationMode={'server'}
loading={loading}
onPaginationModelChange={(params) => {
onPageChange(params.page);
}}
/>
</div>
)
return (
<>
{filterItems && Array.isArray( filterItems ) && filterItems.length ?
<CardBox>
<Formik
initialValues={{
checkboxes: ['lorem'],
switches: ['lorem'],
radio: 'lorem',
}}
onSubmit={() => null}
>
<Form>
<>
{filterItems && filterItems.map((filterItem) => {
return (
<div key={filterItem.id} className="flex mb-4">
<div className="flex flex-col w-full mr-3">
<div className=" text-gray-500 font-bold">Filter</div>
<Field
className={controlClasses}
name='selectedField'
id='selectedField'
component='select'
value={filterItem?.fields?.selectedField || ''}
onChange={handleChange(filterItem.id)}
>
{filters.map((selectOption) => (
<option
key={selectOption.title}
value={`${selectOption.title}`}
>
{selectOption.label}
</option>
))}
</Field>
</div>
{filters.find((filter) =>
filter.title === filterItem?.fields?.selectedField
)?.type === 'enum' ? (
<div className="flex flex-col w-full mr-3">
<div className="text-gray-500 font-bold">
Value
</div>
<Field
className={controlClasses}
name="filterValue"
id='filterValue'
component="select"
value={filterItem?.fields?.filterValue || ''}
onChange={handleChange(filterItem.id)}
>
<option value="">Select Value</option>
{filters.find((filter) =>
filter.title === filterItem?.fields?.selectedField
)?.options?.map((option) => (
<option key={option} value={option}>
{option}
</option>
))}
</Field>
</div>
) : filters.find((filter) =>
filter.title === filterItem?.fields?.selectedField
)?.number ? (
<div className="flex flex-row w-full mr-3">
<div className="flex flex-col w-full mr-3">
<div className=" text-gray-500 font-bold">From</div>
<Field
className={controlClasses}
name='filterValueFrom'
placeholder='From'
id='filterValueFrom'
value={filterItem?.fields?.filterValueFrom || ''}
onChange={handleChange(filterItem.id)}
/>
</div>
<div className="flex flex-col w-full">
<div className=" text-gray-500 font-bold">To</div>
<Field
className={controlClasses}
name='filterValueTo'
placeholder='to'
id='filterValueTo'
value={filterItem?.fields?.filterValueTo || ''}
onChange={handleChange(filterItem.id)}
/>
</div>
</div>
) : filters.find(
(filter) =>
filter.title ===
filterItem?.fields?.selectedField
)?.date ? (
<div className='flex flex-row w-full mr-3'>
<div className='flex flex-col w-full mr-3'>
<div className=' text-gray-500 font-bold'>
From
</div>
<Field
className={controlClasses}
name='filterValueFrom'
placeholder='From'
id='filterValueFrom'
type='datetime-local'
value={filterItem?.fields?.filterValueFrom || ''}
onChange={handleChange(filterItem.id)}
/>
</div>
<div className='flex flex-col w-full'>
<div className=' text-gray-500 font-bold'>To</div>
<Field
className={controlClasses}
name='filterValueTo'
placeholder='to'
id='filterValueTo'
type='datetime-local'
value={filterItem?.fields?.filterValueTo || ''}
onChange={handleChange(filterItem.id)}
/>
</div>
</div>
) : (
<div className="flex flex-col w-full mr-3">
<div className=" text-gray-500 font-bold">Contains</div>
<Field
className={controlClasses}
name='filterValue'
placeholder='Contained'
id='filterValue'
value={filterItem?.fields?.filterValue || ''}
onChange={handleChange(filterItem.id)}
/>
</div>
)}
<div className="flex flex-col">
<div className=" text-gray-500 font-bold">Action</div>
<BaseButton
className="my-2"
type='reset'
color='danger'
label='Delete'
onClick={() => {
deleteFilter(filterItem.id)
}}
/>
</div>
</div>
)
})}
<div className="flex">
<BaseButton
className="my-2 mr-3"
color="success"
label='Apply'
onClick={handleSubmit}
/>
<BaseButton
className="my-2"
color='info'
label='Cancel'
onClick={handleReset}
/>
</div>
</>
</Form>
</Formik>
</CardBox> : null
}
<CardBoxModal
title="Please confirm"
buttonColor="info"
buttonLabel={loading ? 'Deleting...' : 'Confirm'}
isActive={isModalTrashActive}
onConfirm={handleDeleteAction}
onCancel={handleModalAction}
>
<p>Are you sure you want to delete this item?</p>
</CardBoxModal>
{dataGrid}
{selectedRows.length > 0 &&
createPortal(
<BaseButton
className='me-4'
color='danger'
label={`Delete ${selectedRows.length === 1 ? 'Row' : 'Rows'}`}
onClick={() => onDeleteRows(selectedRows)}
/>,
document.getElementById('delete-rows-button'),
)}
<ToastContainer />
</>
)
} }
export default TableSampleAccess_logs const TableAccess_logs: React.FC<TableAccess_logsProps> = ({
filterItems,
setFilterItems,
filters,
}) => {
return (
<GenericTable<AccessLog>
entityName='access_logs'
sliceSelector={(state: RootState) => state.access_logs}
fetchAction={fetch}
updateAction={update}
deleteAction={deleteItem}
deleteByIdsAction={deleteItemsByIds}
setRefetchAction={setRefetch}
loadColumnsFunction={loadColumns}
filters={filters}
filterItems={filterItems}
setFilterItems={setFilterItems}
/>
);
};
export default TableAccess_logs;

View File

@ -3,188 +3,164 @@ import BaseIcon from '../BaseIcon';
import { mdiEye, mdiTrashCan, mdiPencilOutline } from '@mdi/js'; import { mdiEye, mdiTrashCan, mdiPencilOutline } from '@mdi/js';
import axios from 'axios'; import axios from 'axios';
import { import {
GridActionsCellItem, GridActionsCellItem,
GridRowParams, GridRowParams,
GridValueGetterParams, GridValueGetterParams,
} from '@mui/x-data-grid'; } from '@mui/x-data-grid';
import ImageField from '../ImageField'; import ImageField from '../ImageField';
import {saveFile} from "../../helpers/fileSaver"; import { saveFile } from '../../helpers/fileSaver';
import dataFormatter from '../../helpers/dataFormatter' import dataFormatter from '../../helpers/dataFormatter';
import DataGridMultiSelect from "../DataGridMultiSelect"; import DataGridMultiSelect from '../DataGridMultiSelect';
import ListActionsPopover from '../ListActionsPopover'; import ListActionsPopover from '../ListActionsPopover';
import {hasPermission} from "../../helpers/userPermissions"; import { hasPermission } from '../../helpers/userPermissions';
type Params = (id: string) => void; type Params = (id: string) => void;
export const loadColumns = async ( export const loadColumns = async (
onDelete: Params, onDelete: Params,
entityName: string, entityName: string,
user
user,
) => { ) => {
async function callOptionsApi(entityName: string) { async function callOptionsApi(entityName: string) {
if (!hasPermission(user, 'READ_' + entityName.toUpperCase())) return [];
if (!hasPermission(user, 'READ_' + entityName.toUpperCase())) return []; try {
const data = await axios(`/${entityName}/autocomplete?limit=100`);
try { return data.data;
const data = await axios(`/${entityName}/autocomplete?limit=100`); } catch (error) {
return data.data; console.log(error);
} catch (error) { return [];
console.log(error);
return [];
}
} }
}
const hasUpdatePermission = hasPermission(user, 'UPDATE_ACCESS_LOGS') const hasUpdatePermission = hasPermission(user, 'UPDATE_ACCESS_LOGS');
return [ return [
{
field: 'project',
headerName: 'Project',
flex: 1,
minWidth: 120,
filterable: false,
headerClassName: 'datagrid--header',
cellClassName: 'datagrid--cell',
{ editable: hasUpdatePermission,
field: 'project',
headerName: 'Project',
flex: 1,
minWidth: 120,
filterable: false,
headerClassName: 'datagrid--header',
cellClassName: 'datagrid--cell',
sortable: false,
type: 'singleSelect',
getOptionValue: (value: any) => value?.id,
getOptionLabel: (value: any) => value?.label,
valueOptions: await callOptionsApi('projects'),
valueGetter: (params: GridValueGetterParams) =>
params?.value?.id ?? params?.value,
},
editable: hasUpdatePermission, {
field: 'environment',
headerName: 'Environment',
flex: 1,
minWidth: 120,
filterable: false,
headerClassName: 'datagrid--header',
cellClassName: 'datagrid--cell',
sortable: false, editable: hasUpdatePermission,
type: 'singleSelect', },
getOptionValue: (value: any) => value?.id,
getOptionLabel: (value: any) => value?.label,
valueOptions: await callOptionsApi('projects'),
valueGetter: (params: GridValueGetterParams) =>
params?.value?.id ?? params?.value,
}, {
field: 'user',
headerName: 'User',
flex: 1,
minWidth: 120,
filterable: false,
headerClassName: 'datagrid--header',
cellClassName: 'datagrid--cell',
{ editable: hasUpdatePermission,
field: 'environment',
headerName: 'Environment',
flex: 1,
minWidth: 120,
filterable: false,
headerClassName: 'datagrid--header',
cellClassName: 'datagrid--cell',
sortable: false,
type: 'singleSelect',
getOptionValue: (value: any) => value?.id,
getOptionLabel: (value: any) => value?.label,
valueOptions: await callOptionsApi('users'),
valueGetter: (params: GridValueGetterParams) =>
params?.value?.id ?? params?.value,
},
editable: hasUpdatePermission, {
field: 'path',
headerName: 'Path',
flex: 1,
minWidth: 120,
filterable: false,
headerClassName: 'datagrid--header',
cellClassName: 'datagrid--cell',
editable: hasUpdatePermission,
},
}, {
field: 'ip_address',
headerName: 'IPaddress',
flex: 1,
minWidth: 120,
filterable: false,
headerClassName: 'datagrid--header',
cellClassName: 'datagrid--cell',
{ editable: hasUpdatePermission,
field: 'user', },
headerName: 'User',
flex: 1,
minWidth: 120,
filterable: false,
headerClassName: 'datagrid--header',
cellClassName: 'datagrid--cell',
{
field: 'user_agent',
headerName: 'Useragent',
flex: 1,
minWidth: 120,
filterable: false,
headerClassName: 'datagrid--header',
cellClassName: 'datagrid--cell',
editable: hasUpdatePermission, editable: hasUpdatePermission,
},
sortable: false, {
type: 'singleSelect', field: 'accessed_at',
getOptionValue: (value: any) => value?.id, headerName: 'Accessedat',
getOptionLabel: (value: any) => value?.label, flex: 1,
valueOptions: await callOptionsApi('users'), minWidth: 120,
valueGetter: (params: GridValueGetterParams) => filterable: false,
params?.value?.id ?? params?.value, headerClassName: 'datagrid--header',
cellClassName: 'datagrid--cell',
}, editable: hasUpdatePermission,
{ type: 'dateTime',
field: 'path', valueGetter: (params: GridValueGetterParams) =>
headerName: 'Path', new Date(params.row.accessed_at),
flex: 1, },
minWidth: 120,
filterable: false,
headerClassName: 'datagrid--header',
cellClassName: 'datagrid--cell',
{
editable: hasUpdatePermission, field: 'actions',
type: 'actions',
minWidth: 30,
}, headerClassName: 'datagrid--header',
cellClassName: 'datagrid--cell',
{ getActions: (params: GridRowParams) => {
field: 'ip_address', return [
headerName: 'IPaddress', <div key={params?.row?.id}>
flex: 1, <ListActionsPopover
minWidth: 120, onDelete={onDelete}
filterable: false, itemId={params?.row?.id}
headerClassName: 'datagrid--header', pathEdit={`/access_logs/access_logs-edit/?id=${params?.row?.id}`}
cellClassName: 'datagrid--cell', pathView={`/access_logs/access_logs-view/?id=${params?.row?.id}`}
hasUpdatePermission={hasUpdatePermission}
/>
editable: hasUpdatePermission, </div>,
];
},
}, },
];
{
field: 'user_agent',
headerName: 'Useragent',
flex: 1,
minWidth: 120,
filterable: false,
headerClassName: 'datagrid--header',
cellClassName: 'datagrid--cell',
editable: hasUpdatePermission,
},
{
field: 'accessed_at',
headerName: 'Accessedat',
flex: 1,
minWidth: 120,
filterable: false,
headerClassName: 'datagrid--header',
cellClassName: 'datagrid--cell',
editable: hasUpdatePermission,
type: 'dateTime',
valueGetter: (params: GridValueGetterParams) =>
new Date(params.row.accessed_at),
},
{
field: 'actions',
type: 'actions',
minWidth: 30,
headerClassName: 'datagrid--header',
cellClassName: 'datagrid--cell',
getActions: (params: GridRowParams) => {
return [
<div key={params?.row?.id}>
<ListActionsPopover
onDelete={onDelete}
itemId={params?.row?.id}
pathEdit={`/access_logs/access_logs-edit/?id=${params?.row?.id}`}
pathView={`/access_logs/access_logs-view/?id=${params?.row?.id}`}
hasUpdatePermission={hasUpdatePermission}
/>
</div>,
]
},
},
];
}; };

View File

@ -1,14 +1,14 @@
import React from 'react' import React from 'react';
import { MenuAsideItem } from '../interfaces' import { MenuAsideItem } from '../interfaces';
import AsideMenuLayer from './AsideMenuLayer' import AsideMenuLayer from './AsideMenuLayer';
import OverlayLayer from './OverlayLayer' import OverlayLayer from './OverlayLayer';
type Props = { type Props = {
menu: MenuAsideItem[] menu: MenuAsideItem[];
isAsideMobileExpanded: boolean isAsideMobileExpanded: boolean;
isAsideLgActive: boolean isAsideLgActive: boolean;
onAsideLgClose: () => void onAsideLgClose: () => void;
} };
export default function AsideMenu({ export default function AsideMenu({
isAsideMobileExpanded = false, isAsideMobileExpanded = false,
@ -24,7 +24,9 @@ export default function AsideMenu({
}`} }`}
onAsideLgCloseClick={props.onAsideLgClose} onAsideLgCloseClick={props.onAsideLgClose}
/> />
{isAsideLgActive && <OverlayLayer zIndex="z-30" onClick={props.onAsideLgClose} />} {isAsideLgActive && (
<OverlayLayer zIndex='z-30' onClick={props.onAsideLgClose} />
)}
</> </>
) );
} }

View File

@ -1,49 +1,60 @@
import React, { useEffect, useState } from 'react' import React, { useEffect, useState } from 'react';
import { mdiMinus, mdiPlus } from '@mdi/js' import { mdiMinus, mdiPlus } from '@mdi/js';
import BaseIcon from './BaseIcon' import BaseIcon from './BaseIcon';
import Link from 'next/link' import Link from 'next/link';
import { getButtonColor } from '../colors' import { getButtonColor } from '../colors';
import AsideMenuList from './AsideMenuList' import AsideMenuList from './AsideMenuList';
import { MenuAsideItem } from '../interfaces' import { MenuAsideItem } from '../interfaces';
import { useAppSelector } from '../stores/hooks' import { useAppSelector } from '../stores/hooks';
import { useRouter } from 'next/router' import { useRouter } from 'next/router';
type Props = { type Props = {
item: MenuAsideItem item: MenuAsideItem;
isDropdownList?: boolean isDropdownList?: boolean;
} };
const AsideMenuItem = ({ item, isDropdownList = false }: Props) => { const AsideMenuItem = ({ item, isDropdownList = false }: Props) => {
const [isLinkActive, setIsLinkActive] = useState(false) const [isLinkActive, setIsLinkActive] = useState(false);
const [isDropdownActive, setIsDropdownActive] = useState(false) const [isDropdownActive, setIsDropdownActive] = useState(false);
const asideMenuItemStyle = useAppSelector((state) => state.style.asideMenuItemStyle) const asideMenuItemStyle = useAppSelector(
const asideMenuDropdownStyle = useAppSelector((state) => state.style.asideMenuDropdownStyle) (state) => state.style.asideMenuItemStyle,
const asideMenuItemActiveStyle = useAppSelector((state) => state.style.asideMenuItemActiveStyle) );
const asideMenuDropdownStyle = useAppSelector(
(state) => state.style.asideMenuDropdownStyle,
);
const asideMenuItemActiveStyle = useAppSelector(
(state) => state.style.asideMenuItemActiveStyle,
);
const borders = useAppSelector((state) => state.style.borders); const borders = useAppSelector((state) => state.style.borders);
const activeLinkColor = useAppSelector( const activeLinkColor = useAppSelector(
(state) => state.style.activeLinkColor, (state) => state.style.activeLinkColor,
); );
const activeClassAddon = !item.color && isLinkActive ? asideMenuItemActiveStyle : '' const activeClassAddon =
!item.color && isLinkActive ? asideMenuItemActiveStyle : '';
const { asPath, isReady } = useRouter() const { asPath, isReady } = useRouter();
useEffect(() => { useEffect(() => {
if (item.href && isReady) { if (item.href && isReady) {
const linkPathName = new URL(item.href, location.href).pathname + '/'; const linkPathName = new URL(item.href, location.href).pathname + '/';
const activePathname = new URL(asPath, location.href).pathname const activePathname = new URL(asPath, location.href).pathname;
const activeView = activePathname.split('/')[1]; const activeView = activePathname.split('/')[1];
const linkPathNameView = linkPathName.split('/')[1]; const linkPathNameView = linkPathName.split('/')[1];
setIsLinkActive(linkPathNameView === activeView); setIsLinkActive(linkPathNameView === activeView);
} }
}, [item.href, isReady, asPath]) }, [item.href, isReady, asPath]);
const asideMenuItemInnerContents = ( const asideMenuItemInnerContents = (
<> <>
{item.icon && ( {item.icon && (
<BaseIcon path={item.icon} className={`flex-none mx-3 ${activeClassAddon}`} size="18" /> <BaseIcon
path={item.icon}
className={`flex-none mx-3 ${activeClassAddon}`}
size='18'
/>
)} )}
<span <span
className={`grow text-ellipsis line-clamp-1 ${ className={`grow text-ellipsis line-clamp-1 ${
@ -56,21 +67,21 @@ const AsideMenuItem = ({ item, isDropdownList = false }: Props) => {
<BaseIcon <BaseIcon
path={isDropdownActive ? mdiMinus : mdiPlus} path={isDropdownActive ? mdiMinus : mdiPlus}
className={`flex-none ${activeClassAddon}`} className={`flex-none ${activeClassAddon}`}
w="w-12" w='w-12'
/> />
)} )}
</> </>
) );
const componentClass = [ const componentClass = [
'flex cursor-pointer py-1.5 ', 'flex cursor-pointer py-1.5 ',
isDropdownList ? 'px-6 text-sm' : '', isDropdownList ? 'px-6 text-sm' : '',
item.color item.color
? getButtonColor(item.color, false, true) ? getButtonColor(item.color, false, true)
: `${asideMenuItemStyle}`, : `${asideMenuItemStyle}`,
isLinkActive isLinkActive
? `text-black ${activeLinkColor} dark:text-white dark:bg-dark-800` ? `text-black ${activeLinkColor} dark:text-white dark:bg-dark-800`
: '', : '',
].join(' '); ].join(' ');
return ( return (
@ -82,7 +93,10 @@ const AsideMenuItem = ({ item, isDropdownList = false }: Props) => {
</Link> </Link>
)} )}
{!item.href && ( {!item.href && (
<div className={componentClass} onClick={() => setIsDropdownActive(!isDropdownActive)}> <div
className={componentClass}
onClick={() => setIsDropdownActive(!isDropdownActive)}
>
{asideMenuItemInnerContents} {asideMenuItemInnerContents}
</div> </div>
)} )}
@ -96,7 +110,7 @@ const AsideMenuItem = ({ item, isDropdownList = false }: Props) => {
/> />
)} )}
</li> </li>
) );
} };
export default AsideMenuItem export default AsideMenuItem;

View File

@ -1,30 +1,36 @@
import React from 'react' import React from 'react';
import { mdiLogout, mdiClose } from '@mdi/js' import { mdiLogout, mdiClose } from '@mdi/js';
import BaseIcon from './BaseIcon' import BaseIcon from './BaseIcon';
import AsideMenuList from './AsideMenuList' import AsideMenuList from './AsideMenuList';
import { MenuAsideItem } from '../interfaces' import { MenuAsideItem } from '../interfaces';
import { useAppSelector } from '../stores/hooks' import { useAppSelector } from '../stores/hooks';
import Link from 'next/link'; import Link from 'next/link';
type Props = { type Props = {
menu: MenuAsideItem[] menu: MenuAsideItem[];
className?: string className?: string;
onAsideLgCloseClick: () => void onAsideLgCloseClick: () => void;
} };
export default function AsideMenuLayer({ menu, className = '', ...props }: Props) { export default function AsideMenuLayer({
menu,
className = '',
...props
}: Props) {
const corners = useAppSelector((state) => state.style.corners); const corners = useAppSelector((state) => state.style.corners);
const asideStyle = useAppSelector((state) => state.style.asideStyle) const asideStyle = useAppSelector((state) => state.style.asideStyle);
const asideBrandStyle = useAppSelector((state) => state.style.asideBrandStyle) const asideBrandStyle = useAppSelector(
const asideScrollbarsStyle = useAppSelector((state) => state.style.asideScrollbarsStyle) (state) => state.style.asideBrandStyle,
const darkMode = useAppSelector((state) => state.style.darkMode) );
const asideScrollbarsStyle = useAppSelector(
(state) => state.style.asideScrollbarsStyle,
);
const darkMode = useAppSelector((state) => state.style.darkMode);
const handleAsideLgCloseClick = (e: React.MouseEvent) => { const handleAsideLgCloseClick = (e: React.MouseEvent) => {
e.preventDefault() e.preventDefault();
props.onAsideLgCloseClick() props.onAsideLgCloseClick();
} };
return ( return (
<aside <aside
@ -32,19 +38,16 @@ export default function AsideMenuLayer({ menu, className = '', ...props }: Props
className={`${className} zzz lg:py-2 lg:pl-2 w-60 fixed flex z-40 top-0 h-screen transition-position overflow-hidden`} className={`${className} zzz lg:py-2 lg:pl-2 w-60 fixed flex z-40 top-0 h-screen transition-position overflow-hidden`}
> >
<div <div
className={`flex-1 flex flex-col overflow-hidden dark:bg-dark-900 ${asideStyle} ${corners}`} className={`flex-1 flex flex-col overflow-hidden dark:bg-dark-900 ${asideStyle} ${corners}`}
> >
<div <div
className={`flex flex-row h-14 items-center justify-between ${asideBrandStyle}`} className={`flex flex-row h-14 items-center justify-between ${asideBrandStyle}`}
> >
<div className="text-center flex-1 lg:text-left lg:pl-6 xl:text-center xl:pl-0"> <div className='text-center flex-1 lg:text-left lg:pl-6 xl:text-center xl:pl-0'>
<b className='font-black'>Shimahara Visual</b>
<b className="font-black">Shimahara Visual</b>
</div> </div>
<button <button
className="hidden lg:inline-block xl:hidden p-3" className='hidden lg:inline-block xl:hidden p-3'
onClick={handleAsideLgCloseClick} onClick={handleAsideLgCloseClick}
> >
<BaseIcon path={mdiClose} /> <BaseIcon path={mdiClose} />
@ -59,5 +62,5 @@ export default function AsideMenuLayer({ menu, className = '', ...props }: Props
</div> </div>
</div> </div>
</aside> </aside>
) );
} }

View File

@ -1,16 +1,20 @@
import React from 'react' import React from 'react';
import { MenuAsideItem } from '../interfaces' import { MenuAsideItem } from '../interfaces';
import AsideMenuItem from './AsideMenuItem' import AsideMenuItem from './AsideMenuItem';
import {useAppSelector} from "../stores/hooks"; import { useAppSelector } from '../stores/hooks';
import {hasPermission} from "../helpers/userPermissions"; import { hasPermission } from '../helpers/userPermissions';
type Props = { type Props = {
menu: MenuAsideItem[] menu: MenuAsideItem[];
isDropdownList?: boolean isDropdownList?: boolean;
className?: string className?: string;
} };
export default function AsideMenuList({ menu, isDropdownList = false, className = '' }: Props) { export default function AsideMenuList({
menu,
isDropdownList = false,
className = '',
}: Props) {
const { currentUser } = useAppSelector((state) => state.auth); const { currentUser } = useAppSelector((state) => state.auth);
if (!currentUser) return null; if (!currentUser) return null;
@ -18,18 +22,14 @@ export default function AsideMenuList({ menu, isDropdownList = false, className
return ( return (
<ul className={className}> <ul className={className}>
{menu.map((item, index) => { {menu.map((item, index) => {
if (!hasPermission(currentUser, item.permissions)) return null;
if (!hasPermission(currentUser, item.permissions)) return null;
return ( return (
<div key={index}> <div key={index}>
<AsideMenuItem <AsideMenuItem item={item} isDropdownList={isDropdownList} />
item={item}
isDropdownList={isDropdownList}
/>
</div> </div>
) );
})} })}
</ul> </ul>
) );
} }

View File

@ -4,12 +4,11 @@ import ListActionsPopover from '../ListActionsPopover';
import { useAppSelector } from '../../stores/hooks'; import { useAppSelector } from '../../stores/hooks';
import dataFormatter from '../../helpers/dataFormatter'; import dataFormatter from '../../helpers/dataFormatter';
import { Pagination } from '../Pagination'; import { Pagination } from '../Pagination';
import {saveFile} from "../../helpers/fileSaver"; import { saveFile } from '../../helpers/fileSaver';
import LoadingSpinner from "../LoadingSpinner"; import LoadingSpinner from '../LoadingSpinner';
import Link from 'next/link'; import Link from 'next/link';
import {hasPermission} from "../../helpers/userPermissions"; import { hasPermission } from '../../helpers/userPermissions';
type Props = { type Props = {
asset_variants: any[]; asset_variants: any[];
@ -28,17 +27,19 @@ const CardAsset_variants = ({
numPages, numPages,
onPageChange, onPageChange,
}: Props) => { }: Props) => {
const asideScrollbarsStyle = useAppSelector( const asideScrollbarsStyle = useAppSelector(
(state) => state.style.asideScrollbarsStyle, (state) => state.style.asideScrollbarsStyle,
); );
const bgColor = useAppSelector((state) => state.style.cardsColor); const bgColor = useAppSelector((state) => state.style.cardsColor);
const darkMode = useAppSelector((state) => state.style.darkMode); const darkMode = useAppSelector((state) => state.style.darkMode);
const corners = useAppSelector((state) => state.style.corners); const corners = useAppSelector((state) => state.style.corners);
const focusRing = useAppSelector((state) => state.style.focusRingColor); const focusRing = useAppSelector((state) => state.style.focusRingColor);
const currentUser = useAppSelector((state) => state.auth.currentUser);
const hasUpdatePermission = hasPermission(currentUser, 'UPDATE_ASSET_VARIANTS')
const currentUser = useAppSelector((state) => state.auth.currentUser);
const hasUpdatePermission = hasPermission(
currentUser,
'UPDATE_ASSET_VARIANTS',
);
return ( return (
<div className={'p-4'}> <div className={'p-4'}>
@ -47,110 +48,101 @@ const CardAsset_variants = ({
role='list' role='list'
className='grid grid-cols-1 gap-x-6 gap-y-8 lg:grid-cols-3 2xl:grid-cols-4 xl:gap-x-8' className='grid grid-cols-1 gap-x-6 gap-y-8 lg:grid-cols-3 2xl:grid-cols-4 xl:gap-x-8'
> >
{!loading && asset_variants.map((item, index) => ( {!loading &&
<li asset_variants.map((item, index) => (
key={item.id} <li
className={`overflow-hidden ${corners !== 'rounded-full'? corners : 'rounded-3xl'} border ${focusRing} border-gray-200 dark:border-dark-700 ${ key={item.id}
className={`overflow-hidden ${corners !== 'rounded-full' ? corners : 'rounded-3xl'} border ${focusRing} border-gray-200 dark:border-dark-700 ${
darkMode ? 'aside-scrollbars-[slate]' : asideScrollbarsStyle darkMode ? 'aside-scrollbars-[slate]' : asideScrollbarsStyle
}`} }`}
> >
<div
<div className={`flex items-center ${bgColor} p-6 gap-x-4 border-b border-gray-900/5 bg-gray-50 dark:bg-dark-800 relative`}> className={`flex items-center ${bgColor} p-6 gap-x-4 border-b border-gray-900/5 bg-gray-50 dark:bg-dark-800 relative`}
>
<Link href={`/asset_variants/asset_variants-view/?id=${item.id}`} className='text-lg font-bold leading-6 line-clamp-1'> <Link
{item.variant_type} href={`/asset_variants/asset_variants-view/?id=${item.id}`}
className='text-lg font-bold leading-6 line-clamp-1'
>
{item.variant_type}
</Link> </Link>
<div className='ml-auto '>
<div className='ml-auto '> <ListActionsPopover
<ListActionsPopover onDelete={onDelete}
onDelete={onDelete} itemId={item.id}
itemId={item.id} pathEdit={`/asset_variants/asset_variants-edit/?id=${item.id}`}
pathEdit={`/asset_variants/asset_variants-edit/?id=${item.id}`} pathView={`/asset_variants/asset_variants-view/?id=${item.id}`}
pathView={`/asset_variants/asset_variants-view/?id=${item.id}`} hasUpdatePermission={hasUpdatePermission}
/>
hasUpdatePermission={hasUpdatePermission} </div>
/>
</div> </div>
</div> <dl className='divide-y divide-stone-300 dark:divide-dark-700 px-6 py-4 text-sm leading-6 h-64 overflow-y-auto'>
<dl className='divide-y divide-stone-300 dark:divide-dark-700 px-6 py-4 text-sm leading-6 h-64 overflow-y-auto'>
<div className='flex justify-between gap-x-4 py-3'> <div className='flex justify-between gap-x-4 py-3'>
<dt className=' text-gray-500 dark:text-dark-600'>Asset</dt> <dt className=' text-gray-500 dark:text-dark-600'>Asset</dt>
<dd className='flex items-start gap-x-2'> <dd className='flex items-start gap-x-2'>
<div className='font-medium line-clamp-4'> <div className='font-medium line-clamp-4'>
{ dataFormatter.assetsOneListFormatter(item.asset) } {dataFormatter.assetsOneListFormatter(item.asset)}
</div> </div>
</dd> </dd>
</div> </div>
<div className='flex justify-between gap-x-4 py-3'> <div className='flex justify-between gap-x-4 py-3'>
<dt className=' text-gray-500 dark:text-dark-600'>Varianttype</dt> <dt className=' text-gray-500 dark:text-dark-600'>
<dd className='flex items-start gap-x-2'> Varianttype
<div className='font-medium line-clamp-4'> </dt>
{ item.variant_type } <dd className='flex items-start gap-x-2'>
</div> <div className='font-medium line-clamp-4'>
</dd> {item.variant_type}
</div>
</dd>
</div> </div>
<div className='flex justify-between gap-x-4 py-3'> <div className='flex justify-between gap-x-4 py-3'>
<dt className=' text-gray-500 dark:text-dark-600'>CDNURL</dt> <dt className=' text-gray-500 dark:text-dark-600'>
<dd className='flex items-start gap-x-2'> CDNURL
<div className='font-medium line-clamp-4'> </dt>
{ item.cdn_url } <dd className='flex items-start gap-x-2'>
</div> <div className='font-medium line-clamp-4'>
</dd> {item.cdn_url}
</div>
</dd>
</div> </div>
<div className='flex justify-between gap-x-4 py-3'> <div className='flex justify-between gap-x-4 py-3'>
<dt className=' text-gray-500 dark:text-dark-600'>Width(px)</dt> <dt className=' text-gray-500 dark:text-dark-600'>
<dd className='flex items-start gap-x-2'> Width(px)
<div className='font-medium line-clamp-4'> </dt>
{ item.width_px } <dd className='flex items-start gap-x-2'>
</div> <div className='font-medium line-clamp-4'>
</dd> {item.width_px}
</div>
</dd>
</div> </div>
<div className='flex justify-between gap-x-4 py-3'> <div className='flex justify-between gap-x-4 py-3'>
<dt className=' text-gray-500 dark:text-dark-600'>Height(px)</dt> <dt className=' text-gray-500 dark:text-dark-600'>
<dd className='flex items-start gap-x-2'> Height(px)
<div className='font-medium line-clamp-4'> </dt>
{ item.height_px } <dd className='flex items-start gap-x-2'>
</div> <div className='font-medium line-clamp-4'>
</dd> {item.height_px}
</div>
</dd>
</div> </div>
<div className='flex justify-between gap-x-4 py-3'> <div className='flex justify-between gap-x-4 py-3'>
<dt className=' text-gray-500 dark:text-dark-600'>Size(MB)</dt> <dt className=' text-gray-500 dark:text-dark-600'>
<dd className='flex items-start gap-x-2'> Size(MB)
<div className='font-medium line-clamp-4'> </dt>
{ item.size_mb } <dd className='flex items-start gap-x-2'>
</div> <div className='font-medium line-clamp-4'>
</dd> {item.size_mb}
</div>
</dd>
</div> </div>
</dl>
</li>
))}
</dl>
</li>
))}
{!loading && asset_variants.length === 0 && ( {!loading && asset_variants.length === 0 && (
<div className='col-span-full flex items-center justify-center h-40'> <div className='col-span-full flex items-center justify-center h-40'>
<p className=''>No data to display</p> <p className=''>No data to display</p>

View File

@ -2,127 +2,116 @@ import React from 'react';
import CardBox from '../CardBox'; import CardBox from '../CardBox';
import ImageField from '../ImageField'; import ImageField from '../ImageField';
import dataFormatter from '../../helpers/dataFormatter'; import dataFormatter from '../../helpers/dataFormatter';
import {saveFile} from "../../helpers/fileSaver"; import { saveFile } from '../../helpers/fileSaver';
import ListActionsPopover from "../ListActionsPopover"; import ListActionsPopover from '../ListActionsPopover';
import {useAppSelector} from "../../stores/hooks"; import { useAppSelector } from '../../stores/hooks';
import {Pagination} from "../Pagination"; import { Pagination } from '../Pagination';
import LoadingSpinner from "../LoadingSpinner"; import LoadingSpinner from '../LoadingSpinner';
import Link from 'next/link'; import Link from 'next/link';
import {hasPermission} from "../../helpers/userPermissions"; import { hasPermission } from '../../helpers/userPermissions';
type Props = { type Props = {
asset_variants: any[]; asset_variants: any[];
loading: boolean; loading: boolean;
onDelete: (id: string) => void; onDelete: (id: string) => void;
currentPage: number; currentPage: number;
numPages: number; numPages: number;
onPageChange: (page: number) => void; onPageChange: (page: number) => void;
}; };
const ListAsset_variants = ({ asset_variants, loading, onDelete, currentPage, numPages, onPageChange }: Props) => { const ListAsset_variants = ({
asset_variants,
loading,
onDelete,
currentPage,
numPages,
onPageChange,
}: Props) => {
const currentUser = useAppSelector((state) => state.auth.currentUser);
const hasUpdatePermission = hasPermission(
currentUser,
'UPDATE_ASSET_VARIANTS',
);
const currentUser = useAppSelector((state) => state.auth.currentUser); const corners = useAppSelector((state) => state.style.corners);
const hasUpdatePermission = hasPermission(currentUser, 'UPDATE_ASSET_VARIANTS') const bgColor = useAppSelector((state) => state.style.cardsColor);
const corners = useAppSelector((state) => state.style.corners); return (
const bgColor = useAppSelector((state) => state.style.cardsColor); <>
<div className='relative overflow-x-auto p-4 space-y-4'>
{loading && <LoadingSpinner />}
{!loading &&
asset_variants.map((item) => (
<div key={item.id}>
<CardBox hasTable isList className={'rounded shadow-none'}>
<div
className={`flex rounded dark:bg-dark-900 border border-stone-300 items-center overflow-hidden`}
>
<Link
href={`/asset_variants/asset_variants-view/?id=${item.id}`}
className={
'flex-1 px-4 py-6 h-24 flex divide-x-2 divide-stone-300 items-center overflow-hidden`}> dark:divide-dark-700 overflow-x-auto'
}
>
<div className={'flex-1 px-3'}>
<p className={'text-xs text-gray-500 '}>Asset</p>
<p className={'line-clamp-2'}>
{dataFormatter.assetsOneListFormatter(item.asset)}
</p>
</div>
<div className={'flex-1 px-3'}>
<p className={'text-xs text-gray-500 '}>Varianttype</p>
<p className={'line-clamp-2'}>{item.variant_type}</p>
</div>
return ( <div className={'flex-1 px-3'}>
<> <p className={'text-xs text-gray-500 '}>CDNURL</p>
<div className='relative overflow-x-auto p-4 space-y-4'> <p className={'line-clamp-2'}>{item.cdn_url}</p>
{loading && <LoadingSpinner />} </div>
{!loading && asset_variants.map((item) => (
<div key={item.id}>
<CardBox hasTable isList className={'rounded shadow-none'}>
<div className={`flex rounded dark:bg-dark-900 border border-stone-300 items-center overflow-hidden`}>
<Link <div className={'flex-1 px-3'}>
href={`/asset_variants/asset_variants-view/?id=${item.id}`} <p className={'text-xs text-gray-500 '}>Width(px)</p>
className={ <p className={'line-clamp-2'}>{item.width_px}</p>
'flex-1 px-4 py-6 h-24 flex divide-x-2 divide-stone-300 items-center overflow-hidden`}> dark:divide-dark-700 overflow-x-auto' </div>
}
>
<div className={'flex-1 px-3'}>
<p className={'text-xs text-gray-500 '}>Height(px)</p>
<p className={'line-clamp-2'}>{item.height_px}</p>
</div>
<div className={'flex-1 px-3'}> <div className={'flex-1 px-3'}>
<p className={'text-xs text-gray-500 '}>Asset</p> <p className={'text-xs text-gray-500 '}>Size(MB)</p>
<p className={'line-clamp-2'}>{ dataFormatter.assetsOneListFormatter(item.asset) }</p> <p className={'line-clamp-2'}>{item.size_mb}</p>
</div> </div>
</Link>
<ListActionsPopover
onDelete={onDelete}
itemId={item.id}
<div className={'flex-1 px-3'}> pathEdit={`/asset_variants/asset_variants-edit/?id=${item.id}`}
<p className={'text-xs text-gray-500 '}>Varianttype</p> pathView={`/asset_variants/asset_variants-view/?id=${item.id}`}
<p className={'line-clamp-2'}>{ item.variant_type }</p> hasUpdatePermission={hasUpdatePermission}
</div> />
</div>
</CardBox>
<div className={'flex-1 px-3'}>
<p className={'text-xs text-gray-500 '}>CDNURL</p>
<p className={'line-clamp-2'}>{ item.cdn_url }</p>
</div>
<div className={'flex-1 px-3'}>
<p className={'text-xs text-gray-500 '}>Width(px)</p>
<p className={'line-clamp-2'}>{ item.width_px }</p>
</div>
<div className={'flex-1 px-3'}>
<p className={'text-xs text-gray-500 '}>Height(px)</p>
<p className={'line-clamp-2'}>{ item.height_px }</p>
</div>
<div className={'flex-1 px-3'}>
<p className={'text-xs text-gray-500 '}>Size(MB)</p>
<p className={'line-clamp-2'}>{ item.size_mb }</p>
</div>
</Link>
<ListActionsPopover
onDelete={onDelete}
itemId={item.id}
pathEdit={`/asset_variants/asset_variants-edit/?id=${item.id}`}
pathView={`/asset_variants/asset_variants-view/?id=${item.id}`}
hasUpdatePermission={hasUpdatePermission}
/>
</div>
</CardBox>
</div>
))}
{!loading && asset_variants.length === 0 && (
<div className='col-span-full flex items-center justify-center h-40'>
<p className=''>No data to display</p>
</div>
)}
</div> </div>
<div className={'flex items-center justify-center my-6'}> ))}
<Pagination {!loading && asset_variants.length === 0 && (
currentPage={currentPage} <div className='col-span-full flex items-center justify-center h-40'>
numPages={numPages} <p className=''>No data to display</p>
setCurrentPage={onPageChange} </div>
/> )}
</div> </div>
</> <div className={'flex items-center justify-center my-6'}>
) <Pagination
currentPage={currentPage}
numPages={numPages}
setCurrentPage={onPageChange}
/>
</div>
</>
);
}; };
export default ListAsset_variants export default ListAsset_variants;

View File

@ -1,463 +1,48 @@
import React, { useEffect, useState, useMemo } from 'react' /**
import { createPortal } from 'react-dom'; * Asset Variants Table Component
import { ToastContainer, toast } from 'react-toastify'; */
import BaseButton from '../BaseButton'
import CardBoxModal from '../CardBoxModal' import React from 'react';
import CardBox from "../CardBox"; import GenericTable from '../Generic/GenericTable';
import { fetch, update, deleteItem, setRefetch, deleteItemsByIds } from '../../stores/asset_variants/asset_variantsSlice'
import { useAppDispatch, useAppSelector } from '../../stores/hooks'
import { useRouter } from 'next/router'
import { Field, Form, Formik } from "formik";
import { import {
DataGrid, fetch,
GridColDef, update,
} from '@mui/x-data-grid'; deleteItem,
import {loadColumns} from "./configureAsset_variantsCols"; setRefetch,
import _ from 'lodash'; deleteItemsByIds,
import dataFormatter from '../../helpers/dataFormatter' } from '../../stores/asset_variants/asset_variantsSlice';
import {dataGridStyles} from "../../styles"; import { loadColumns } from './configureAsset_variantsCols';
import type { AssetVariant } from '../../types/entities';
import type { RootState } from '../../stores/store';
import type { Filter, FilterItem } from '../../types/filters';
interface TableAsset_variantsProps {
filterItems: FilterItem[];
const perPage = 10 setFilterItems: (items: FilterItem[]) => void;
filters: Filter[];
const TableSampleAsset_variants = ({ filterItems, setFilterItems, filters, showGrid }) => { showGrid?: boolean;
const notify = (type, msg) => toast( msg, {type, position: "bottom-center"});
const dispatch = useAppDispatch();
const router = useRouter();
const pagesList = [];
const [id, setId] = useState(null);
const [currentPage, setCurrentPage] = useState(0);
const [filterRequest, setFilterRequest] = React.useState('');
const [columns, setColumns] = useState<GridColDef[]>([]);
const [selectedRows, setSelectedRows] = useState([]);
const [sortModel, setSortModel] = useState([
{
field: '',
sort: 'desc',
},
]);
const { asset_variants, loading, count, notify: asset_variantsNotify, refetch } = useAppSelector((state) => state.asset_variants)
const { currentUser } = useAppSelector((state) => state.auth);
const focusRing = useAppSelector((state) => state.style.focusRingColor);
const bgColor = useAppSelector((state) => state.style.bgLayoutColor);
const corners = useAppSelector((state) => state.style.corners);
const numPages = Math.floor(count / perPage) === 0 ? 1 : Math.ceil(count / perPage);
for (let i = 0; i < numPages; i++) {
pagesList.push(i);
}
const loadData = async (page = currentPage, request = filterRequest) => {
if (page !== currentPage) setCurrentPage(page);
if (request !== filterRequest) setFilterRequest(request);
const { sort, field } = sortModel[0];
const query = `?page=${page}&limit=${perPage}${request}&sort=${sort}&field=${field}`;
dispatch(fetch({ limit: perPage, page, query }));
};
useEffect(() => {
if (asset_variantsNotify.showNotification) {
notify(asset_variantsNotify.typeNotification, asset_variantsNotify.textNotification);
}
}, [asset_variantsNotify.showNotification]);
useEffect(() => {
if (!currentUser) return;
loadData();
}, [sortModel, currentUser]);
useEffect(() => {
if (refetch) {
loadData(0);
dispatch(setRefetch(false));
}
}, [refetch, dispatch]);
const [isModalInfoActive, setIsModalInfoActive] = useState(false)
const [isModalTrashActive, setIsModalTrashActive] = useState(false)
const handleModalAction = () => {
setIsModalInfoActive(false)
setIsModalTrashActive(false)
}
const handleDeleteModalAction = (id: string) => {
setId(id)
setIsModalTrashActive(true)
}
const handleDeleteAction = async () => {
if (id) {
await dispatch(deleteItem(id));
await loadData(0);
setIsModalTrashActive(false);
}
};
const generateFilterRequests = useMemo(() => {
let request = '&';
filterItems.forEach((item) => {
const isRangeFilter = filters.find(
(filter) =>
filter.title === item.fields.selectedField &&
(filter.number || filter.date),
);
if (isRangeFilter) {
const from = item.fields.filterValueFrom;
const to = item.fields.filterValueTo;
if (from) {
request += `${item.fields.selectedField}Range=${from}&`;
}
if (to) {
request += `${item.fields.selectedField}Range=${to}&`;
}
} else {
const value = item.fields.filterValue;
if (value) {
request += `${item.fields.selectedField}=${value}&`;
}
}
});
return request;
}, [filterItems, filters]);
const deleteFilter = (value) => {
const newItems = filterItems.filter((item) => item.id !== value);
if (newItems.length) {
setFilterItems(newItems);
} else {
loadData(0, '');
setFilterItems(newItems);
}
};
const handleSubmit = () => {
loadData(0, generateFilterRequests);
};
const handleChange = (id) => (e) => {
const value = e.target.value;
const name = e.target.name;
setFilterItems(
filterItems.map((item) => {
if (item.id !== id) return item;
if (name === 'selectedField') return { id, fields: { [name]: value } };
return { id, fields: { ...item.fields, [name]: value } }
}),
);
};
const handleReset = () => {
setFilterItems([]);
loadData(0, '');
};
const onPageChange = (page: number) => {
loadData(page);
setCurrentPage(page);
};
useEffect(() => {
if (!currentUser) return;
loadColumns(
handleDeleteModalAction,
`asset_variants`,
currentUser,
).then((newCols) => setColumns(newCols));
}, [currentUser]);
const handleTableSubmit = async (id: string, data) => {
if (!_.isEmpty(data)) {
await dispatch(update({ id, data }))
.unwrap()
.then((res) => res)
.catch((err) => {
throw new Error(err);
});
}
};
const onDeleteRows = async (selectedRows) => {
await dispatch(deleteItemsByIds(selectedRows));
await loadData(0);
};
const controlClasses =
'w-full py-2 px-2 my-2 rounded dark:placeholder-gray-400 ' +
` ${bgColor} ${focusRing} ${corners} ` +
'dark:bg-slate-800 border';
const dataGrid = (
<div className='relative overflow-x-auto'>
<DataGrid
autoHeight
rowHeight={64}
sx={dataGridStyles}
className={'datagrid--table'}
getRowClassName={() => `datagrid--row`}
rows={asset_variants ?? []}
columns={columns}
initialState={{
pagination: {
paginationModel: {
pageSize: 10,
},
},
}}
disableRowSelectionOnClick
onProcessRowUpdateError={(params) => {
console.log('Error', params);
}}
processRowUpdate={async (newRow, oldRow) => {
const data = dataFormatter.dataGridEditFormatter(newRow);
try {
await handleTableSubmit(newRow.id, data);
return newRow;
} catch {
return oldRow;
}
}}
sortingMode={'server'}
checkboxSelection
onRowSelectionModelChange={(ids) => {
setSelectedRows(ids)
}}
onSortModelChange={(params) => {
params.length
? setSortModel(params)
: setSortModel([{ field: '', sort: 'desc' }]);
}}
rowCount={count}
pageSizeOptions={[10]}
paginationMode={'server'}
loading={loading}
onPaginationModelChange={(params) => {
onPageChange(params.page);
}}
/>
</div>
)
return (
<>
{filterItems && Array.isArray( filterItems ) && filterItems.length ?
<CardBox>
<Formik
initialValues={{
checkboxes: ['lorem'],
switches: ['lorem'],
radio: 'lorem',
}}
onSubmit={() => null}
>
<Form>
<>
{filterItems && filterItems.map((filterItem) => {
return (
<div key={filterItem.id} className="flex mb-4">
<div className="flex flex-col w-full mr-3">
<div className=" text-gray-500 font-bold">Filter</div>
<Field
className={controlClasses}
name='selectedField'
id='selectedField'
component='select'
value={filterItem?.fields?.selectedField || ''}
onChange={handleChange(filterItem.id)}
>
{filters.map((selectOption) => (
<option
key={selectOption.title}
value={`${selectOption.title}`}
>
{selectOption.label}
</option>
))}
</Field>
</div>
{filters.find((filter) =>
filter.title === filterItem?.fields?.selectedField
)?.type === 'enum' ? (
<div className="flex flex-col w-full mr-3">
<div className="text-gray-500 font-bold">
Value
</div>
<Field
className={controlClasses}
name="filterValue"
id='filterValue'
component="select"
value={filterItem?.fields?.filterValue || ''}
onChange={handleChange(filterItem.id)}
>
<option value="">Select Value</option>
{filters.find((filter) =>
filter.title === filterItem?.fields?.selectedField
)?.options?.map((option) => (
<option key={option} value={option}>
{option}
</option>
))}
</Field>
</div>
) : filters.find((filter) =>
filter.title === filterItem?.fields?.selectedField
)?.number ? (
<div className="flex flex-row w-full mr-3">
<div className="flex flex-col w-full mr-3">
<div className=" text-gray-500 font-bold">From</div>
<Field
className={controlClasses}
name='filterValueFrom'
placeholder='From'
id='filterValueFrom'
value={filterItem?.fields?.filterValueFrom || ''}
onChange={handleChange(filterItem.id)}
/>
</div>
<div className="flex flex-col w-full">
<div className=" text-gray-500 font-bold">To</div>
<Field
className={controlClasses}
name='filterValueTo'
placeholder='to'
id='filterValueTo'
value={filterItem?.fields?.filterValueTo || ''}
onChange={handleChange(filterItem.id)}
/>
</div>
</div>
) : filters.find(
(filter) =>
filter.title ===
filterItem?.fields?.selectedField
)?.date ? (
<div className='flex flex-row w-full mr-3'>
<div className='flex flex-col w-full mr-3'>
<div className=' text-gray-500 font-bold'>
From
</div>
<Field
className={controlClasses}
name='filterValueFrom'
placeholder='From'
id='filterValueFrom'
type='datetime-local'
value={filterItem?.fields?.filterValueFrom || ''}
onChange={handleChange(filterItem.id)}
/>
</div>
<div className='flex flex-col w-full'>
<div className=' text-gray-500 font-bold'>To</div>
<Field
className={controlClasses}
name='filterValueTo'
placeholder='to'
id='filterValueTo'
type='datetime-local'
value={filterItem?.fields?.filterValueTo || ''}
onChange={handleChange(filterItem.id)}
/>
</div>
</div>
) : (
<div className="flex flex-col w-full mr-3">
<div className=" text-gray-500 font-bold">Contains</div>
<Field
className={controlClasses}
name='filterValue'
placeholder='Contained'
id='filterValue'
value={filterItem?.fields?.filterValue || ''}
onChange={handleChange(filterItem.id)}
/>
</div>
)}
<div className="flex flex-col">
<div className=" text-gray-500 font-bold">Action</div>
<BaseButton
className="my-2"
type='reset'
color='danger'
label='Delete'
onClick={() => {
deleteFilter(filterItem.id)
}}
/>
</div>
</div>
)
})}
<div className="flex">
<BaseButton
className="my-2 mr-3"
color="success"
label='Apply'
onClick={handleSubmit}
/>
<BaseButton
className="my-2"
color='info'
label='Cancel'
onClick={handleReset}
/>
</div>
</>
</Form>
</Formik>
</CardBox> : null
}
<CardBoxModal
title="Please confirm"
buttonColor="info"
buttonLabel={loading ? 'Deleting...' : 'Confirm'}
isActive={isModalTrashActive}
onConfirm={handleDeleteAction}
onCancel={handleModalAction}
>
<p>Are you sure you want to delete this item?</p>
</CardBoxModal>
{dataGrid}
{selectedRows.length > 0 &&
createPortal(
<BaseButton
className='me-4'
color='danger'
label={`Delete ${selectedRows.length === 1 ? 'Row' : 'Rows'}`}
onClick={() => onDeleteRows(selectedRows)}
/>,
document.getElementById('delete-rows-button'),
)}
<ToastContainer />
</>
)
} }
export default TableSampleAsset_variants const TableAsset_variants: React.FC<TableAsset_variantsProps> = ({
filterItems,
setFilterItems,
filters,
}) => {
return (
<GenericTable<AssetVariant>
entityName='asset_variants'
sliceSelector={(state: RootState) => state.asset_variants}
fetchAction={fetch}
updateAction={update}
deleteAction={deleteItem}
deleteByIdsAction={deleteItemsByIds}
setRefetchAction={setRefetch}
loadColumnsFunction={loadColumns}
filters={filters}
filterItems={filterItems}
setFilterItems={setFilterItems}
/>
);
};
export default TableAsset_variants;

View File

@ -3,166 +3,146 @@ import BaseIcon from '../BaseIcon';
import { mdiEye, mdiTrashCan, mdiPencilOutline } from '@mdi/js'; import { mdiEye, mdiTrashCan, mdiPencilOutline } from '@mdi/js';
import axios from 'axios'; import axios from 'axios';
import { import {
GridActionsCellItem, GridActionsCellItem,
GridRowParams, GridRowParams,
GridValueGetterParams, GridValueGetterParams,
} from '@mui/x-data-grid'; } from '@mui/x-data-grid';
import ImageField from '../ImageField'; import ImageField from '../ImageField';
import {saveFile} from "../../helpers/fileSaver"; import { saveFile } from '../../helpers/fileSaver';
import dataFormatter from '../../helpers/dataFormatter' import dataFormatter from '../../helpers/dataFormatter';
import DataGridMultiSelect from "../DataGridMultiSelect"; import DataGridMultiSelect from '../DataGridMultiSelect';
import ListActionsPopover from '../ListActionsPopover'; import ListActionsPopover from '../ListActionsPopover';
import {hasPermission} from "../../helpers/userPermissions"; import { hasPermission } from '../../helpers/userPermissions';
type Params = (id: string) => void; type Params = (id: string) => void;
export const loadColumns = async ( export const loadColumns = async (
onDelete: Params, onDelete: Params,
entityName: string, entityName: string,
user
user,
) => { ) => {
async function callOptionsApi(entityName: string) { async function callOptionsApi(entityName: string) {
if (!hasPermission(user, 'READ_' + entityName.toUpperCase())) return [];
if (!hasPermission(user, 'READ_' + entityName.toUpperCase())) return []; try {
const data = await axios(`/${entityName}/autocomplete?limit=100`);
try { return data.data;
const data = await axios(`/${entityName}/autocomplete?limit=100`); } catch (error) {
return data.data; console.log(error);
} catch (error) { return [];
console.log(error);
return [];
}
} }
}
const hasUpdatePermission = hasPermission(user, 'UPDATE_ASSET_VARIANTS') const hasUpdatePermission = hasPermission(user, 'UPDATE_ASSET_VARIANTS');
return [ return [
{
field: 'asset',
headerName: 'Asset',
flex: 1,
minWidth: 120,
filterable: false,
headerClassName: 'datagrid--header',
cellClassName: 'datagrid--cell',
{ editable: hasUpdatePermission,
field: 'asset',
headerName: 'Asset',
flex: 1,
minWidth: 120,
filterable: false,
headerClassName: 'datagrid--header',
cellClassName: 'datagrid--cell',
sortable: false,
type: 'singleSelect',
getOptionValue: (value: any) => value?.id,
getOptionLabel: (value: any) => value?.label,
valueOptions: await callOptionsApi('assets'),
valueGetter: (params: GridValueGetterParams) =>
params?.value?.id ?? params?.value,
},
editable: hasUpdatePermission, {
field: 'variant_type',
headerName: 'Varianttype',
flex: 1,
minWidth: 120,
filterable: false,
headerClassName: 'datagrid--header',
cellClassName: 'datagrid--cell',
sortable: false, editable: hasUpdatePermission,
type: 'singleSelect', },
getOptionValue: (value: any) => value?.id,
getOptionLabel: (value: any) => value?.label,
valueOptions: await callOptionsApi('assets'),
valueGetter: (params: GridValueGetterParams) =>
params?.value?.id ?? params?.value,
}, {
field: 'cdn_url',
headerName: 'CDNURL',
flex: 1,
minWidth: 120,
filterable: false,
headerClassName: 'datagrid--header',
cellClassName: 'datagrid--cell',
{ editable: hasUpdatePermission,
field: 'variant_type', },
headerName: 'Varianttype',
flex: 1,
minWidth: 120,
filterable: false,
headerClassName: 'datagrid--header',
cellClassName: 'datagrid--cell',
{
field: 'width_px',
headerName: 'Width(px)',
flex: 1,
minWidth: 120,
filterable: false,
headerClassName: 'datagrid--header',
cellClassName: 'datagrid--cell',
editable: hasUpdatePermission, editable: hasUpdatePermission,
type: 'number',
},
}, {
field: 'height_px',
headerName: 'Height(px)',
flex: 1,
minWidth: 120,
filterable: false,
headerClassName: 'datagrid--header',
cellClassName: 'datagrid--cell',
{ editable: hasUpdatePermission,
field: 'cdn_url',
headerName: 'CDNURL',
flex: 1,
minWidth: 120,
filterable: false,
headerClassName: 'datagrid--header',
cellClassName: 'datagrid--cell',
type: 'number',
},
editable: hasUpdatePermission, {
field: 'size_mb',
headerName: 'Size(MB)',
flex: 1,
minWidth: 120,
filterable: false,
headerClassName: 'datagrid--header',
cellClassName: 'datagrid--cell',
editable: hasUpdatePermission,
}, type: 'number',
},
{ {
field: 'width_px', field: 'actions',
headerName: 'Width(px)', type: 'actions',
flex: 1, minWidth: 30,
minWidth: 120, headerClassName: 'datagrid--header',
filterable: false, cellClassName: 'datagrid--cell',
headerClassName: 'datagrid--header', getActions: (params: GridRowParams) => {
cellClassName: 'datagrid--cell', return [
<div key={params?.row?.id}>
<ListActionsPopover
editable: hasUpdatePermission, onDelete={onDelete}
itemId={params?.row?.id}
type: 'number', pathEdit={`/asset_variants/asset_variants-edit/?id=${params?.row?.id}`}
pathView={`/asset_variants/asset_variants-view/?id=${params?.row?.id}`}
}, hasUpdatePermission={hasUpdatePermission}
/>
{ </div>,
field: 'height_px', ];
headerName: 'Height(px)', },
flex: 1, },
minWidth: 120, ];
filterable: false,
headerClassName: 'datagrid--header',
cellClassName: 'datagrid--cell',
editable: hasUpdatePermission,
type: 'number',
},
{
field: 'size_mb',
headerName: 'Size(MB)',
flex: 1,
minWidth: 120,
filterable: false,
headerClassName: 'datagrid--header',
cellClassName: 'datagrid--cell',
editable: hasUpdatePermission,
type: 'number',
},
{
field: 'actions',
type: 'actions',
minWidth: 30,
headerClassName: 'datagrid--header',
cellClassName: 'datagrid--cell',
getActions: (params: GridRowParams) => {
return [
<div key={params?.row?.id}>
<ListActionsPopover
onDelete={onDelete}
itemId={params?.row?.id}
pathEdit={`/asset_variants/asset_variants-edit/?id=${params?.row?.id}`}
pathView={`/asset_variants/asset_variants-view/?id=${params?.row?.id}`}
hasUpdatePermission={hasUpdatePermission}
/>
</div>,
]
},
},
];
}; };

View File

@ -4,12 +4,11 @@ import ListActionsPopover from '../ListActionsPopover';
import { useAppSelector } from '../../stores/hooks'; import { useAppSelector } from '../../stores/hooks';
import dataFormatter from '../../helpers/dataFormatter'; import dataFormatter from '../../helpers/dataFormatter';
import { Pagination } from '../Pagination'; import { Pagination } from '../Pagination';
import {saveFile} from "../../helpers/fileSaver"; import { saveFile } from '../../helpers/fileSaver';
import LoadingSpinner from "../LoadingSpinner"; import LoadingSpinner from '../LoadingSpinner';
import Link from 'next/link'; import Link from 'next/link';
import {hasPermission} from "../../helpers/userPermissions"; import { hasPermission } from '../../helpers/userPermissions';
type Props = { type Props = {
assets: any[]; assets: any[];
@ -28,17 +27,16 @@ const CardAssets = ({
numPages, numPages,
onPageChange, onPageChange,
}: Props) => { }: Props) => {
const asideScrollbarsStyle = useAppSelector( const asideScrollbarsStyle = useAppSelector(
(state) => state.style.asideScrollbarsStyle, (state) => state.style.asideScrollbarsStyle,
); );
const bgColor = useAppSelector((state) => state.style.cardsColor); const bgColor = useAppSelector((state) => state.style.cardsColor);
const darkMode = useAppSelector((state) => state.style.darkMode); const darkMode = useAppSelector((state) => state.style.darkMode);
const corners = useAppSelector((state) => state.style.corners); const corners = useAppSelector((state) => state.style.corners);
const focusRing = useAppSelector((state) => state.style.focusRingColor); const focusRing = useAppSelector((state) => state.style.focusRingColor);
const currentUser = useAppSelector((state) => state.auth.currentUser);
const hasUpdatePermission = hasPermission(currentUser, 'UPDATE_ASSETS')
const currentUser = useAppSelector((state) => state.auth.currentUser);
const hasUpdatePermission = hasPermission(currentUser, 'UPDATE_ASSETS');
return ( return (
<div className={'p-4'}> <div className={'p-4'}>
@ -47,206 +45,196 @@ const CardAssets = ({
role='list' role='list'
className='grid grid-cols-1 gap-x-6 gap-y-8 lg:grid-cols-3 2xl:grid-cols-4 xl:gap-x-8' className='grid grid-cols-1 gap-x-6 gap-y-8 lg:grid-cols-3 2xl:grid-cols-4 xl:gap-x-8'
> >
{!loading && assets.map((item, index) => ( {!loading &&
<li assets.map((item, index) => (
key={item.id} <li
className={`overflow-hidden ${corners !== 'rounded-full'? corners : 'rounded-3xl'} border ${focusRing} border-gray-200 dark:border-dark-700 ${ key={item.id}
className={`overflow-hidden ${corners !== 'rounded-full' ? corners : 'rounded-3xl'} border ${focusRing} border-gray-200 dark:border-dark-700 ${
darkMode ? 'aside-scrollbars-[slate]' : asideScrollbarsStyle darkMode ? 'aside-scrollbars-[slate]' : asideScrollbarsStyle
}`} }`}
> >
<div
<div className={`flex items-center ${bgColor} p-6 gap-x-4 border-b border-gray-900/5 bg-gray-50 dark:bg-dark-800 relative`}> className={`flex items-center ${bgColor} p-6 gap-x-4 border-b border-gray-900/5 bg-gray-50 dark:bg-dark-800 relative`}
>
<Link href={`/assets/assets-view/?id=${item.id}`} className='text-lg font-bold leading-6 line-clamp-1'> <Link
{item.name} href={`/assets/assets-view/?id=${item.id}`}
className='text-lg font-bold leading-6 line-clamp-1'
>
{item.name}
</Link> </Link>
<div className='ml-auto '>
<div className='ml-auto '> <ListActionsPopover
<ListActionsPopover onDelete={onDelete}
onDelete={onDelete} itemId={item.id}
itemId={item.id} pathEdit={`/assets/assets-edit/?id=${item.id}`}
pathEdit={`/assets/assets-edit/?id=${item.id}`} pathView={`/assets/assets-view/?id=${item.id}`}
pathView={`/assets/assets-view/?id=${item.id}`} hasUpdatePermission={hasUpdatePermission}
/>
hasUpdatePermission={hasUpdatePermission} </div>
/>
</div> </div>
</div> <dl className='divide-y divide-stone-300 dark:divide-dark-700 px-6 py-4 text-sm leading-6 h-64 overflow-y-auto'>
<dl className='divide-y divide-stone-300 dark:divide-dark-700 px-6 py-4 text-sm leading-6 h-64 overflow-y-auto'>
<div className='flex justify-between gap-x-4 py-3'> <div className='flex justify-between gap-x-4 py-3'>
<dt className=' text-gray-500 dark:text-dark-600'>Project</dt> <dt className=' text-gray-500 dark:text-dark-600'>
<dd className='flex items-start gap-x-2'> Project
<div className='font-medium line-clamp-4'> </dt>
{ dataFormatter.projectsOneListFormatter(item.project) } <dd className='flex items-start gap-x-2'>
</div> <div className='font-medium line-clamp-4'>
</dd> {dataFormatter.projectsOneListFormatter(item.project)}
</div>
</dd>
</div> </div>
<div className='flex justify-between gap-x-4 py-3'> <div className='flex justify-between gap-x-4 py-3'>
<dt className=' text-gray-500 dark:text-dark-600'>Name</dt> <dt className=' text-gray-500 dark:text-dark-600'>Name</dt>
<dd className='flex items-start gap-x-2'> <dd className='flex items-start gap-x-2'>
<div className='font-medium line-clamp-4'> <div className='font-medium line-clamp-4'>{item.name}</div>
{ item.name } </dd>
</div>
</dd>
</div> </div>
<div className='flex justify-between gap-x-4 py-3'> <div className='flex justify-between gap-x-4 py-3'>
<dt className=' text-gray-500 dark:text-dark-600'>Assettype</dt> <dt className=' text-gray-500 dark:text-dark-600'>
<dd className='flex items-start gap-x-2'> Asset format
<div className='font-medium line-clamp-4'> </dt>
{ item.asset_type } <dd className='flex items-start gap-x-2'>
</div> <div className='font-medium line-clamp-4'>
</dd> {item.asset_type}
</div>
</dd>
</div> </div>
<div className='flex justify-between gap-x-4 py-3'> <div className='flex justify-between gap-x-4 py-3'>
<dt className=' text-gray-500 dark:text-dark-600'>CDNURL</dt> <dt className=' text-gray-500 dark:text-dark-600'>Type</dt>
<dd className='flex items-start gap-x-2'> <dd className='flex items-start gap-x-2'>
<div className='font-medium line-clamp-4'> <div className='font-medium line-clamp-4'>
{ item.cdn_url } {item.type || 'general'}
</div> </div>
</dd> </dd>
</div> </div>
<div className='flex justify-between gap-x-4 py-3'> <div className='flex justify-between gap-x-4 py-3'>
<dt className=' text-gray-500 dark:text-dark-600'>Storagekey</dt> <dt className=' text-gray-500 dark:text-dark-600'>
<dd className='flex items-start gap-x-2'> CDNURL
<div className='font-medium line-clamp-4'> </dt>
{ item.storage_key } <dd className='flex items-start gap-x-2'>
</div> <div className='font-medium line-clamp-4'>
</dd> {item.cdn_url}
</div>
</dd>
</div> </div>
<div className='flex justify-between gap-x-4 py-3'> <div className='flex justify-between gap-x-4 py-3'>
<dt className=' text-gray-500 dark:text-dark-600'>MIMEtype</dt> <dt className=' text-gray-500 dark:text-dark-600'>
<dd className='flex items-start gap-x-2'> Storagekey
<div className='font-medium line-clamp-4'> </dt>
{ item.mime_type } <dd className='flex items-start gap-x-2'>
</div> <div className='font-medium line-clamp-4'>
</dd> {item.storage_key}
</div>
</dd>
</div> </div>
<div className='flex justify-between gap-x-4 py-3'> <div className='flex justify-between gap-x-4 py-3'>
<dt className=' text-gray-500 dark:text-dark-600'>Size(MB)</dt> <dt className=' text-gray-500 dark:text-dark-600'>
<dd className='flex items-start gap-x-2'> MIMEtype
<div className='font-medium line-clamp-4'> </dt>
{ item.size_mb } <dd className='flex items-start gap-x-2'>
</div> <div className='font-medium line-clamp-4'>
</dd> {item.mime_type}
</div>
</dd>
</div> </div>
<div className='flex justify-between gap-x-4 py-3'> <div className='flex justify-between gap-x-4 py-3'>
<dt className=' text-gray-500 dark:text-dark-600'>Width(px)</dt> <dt className=' text-gray-500 dark:text-dark-600'>
<dd className='flex items-start gap-x-2'> Size(MB)
<div className='font-medium line-clamp-4'> </dt>
{ item.width_px } <dd className='flex items-start gap-x-2'>
</div> <div className='font-medium line-clamp-4'>
</dd> {item.size_mb}
</div>
</dd>
</div> </div>
<div className='flex justify-between gap-x-4 py-3'> <div className='flex justify-between gap-x-4 py-3'>
<dt className=' text-gray-500 dark:text-dark-600'>Height(px)</dt> <dt className=' text-gray-500 dark:text-dark-600'>
<dd className='flex items-start gap-x-2'> Width(px)
<div className='font-medium line-clamp-4'> </dt>
{ item.height_px } <dd className='flex items-start gap-x-2'>
</div> <div className='font-medium line-clamp-4'>
</dd> {item.width_px}
</div>
</dd>
</div> </div>
<div className='flex justify-between gap-x-4 py-3'> <div className='flex justify-between gap-x-4 py-3'>
<dt className=' text-gray-500 dark:text-dark-600'>Duration(sec)</dt> <dt className=' text-gray-500 dark:text-dark-600'>
<dd className='flex items-start gap-x-2'> Height(px)
<div className='font-medium line-clamp-4'> </dt>
{ item.duration_sec } <dd className='flex items-start gap-x-2'>
</div> <div className='font-medium line-clamp-4'>
</dd> {item.height_px}
</div>
</dd>
</div> </div>
<div className='flex justify-between gap-x-4 py-3'> <div className='flex justify-between gap-x-4 py-3'>
<dt className=' text-gray-500 dark:text-dark-600'>Checksum</dt> <dt className=' text-gray-500 dark:text-dark-600'>
<dd className='flex items-start gap-x-2'> Duration(sec)
<div className='font-medium line-clamp-4'> </dt>
{ item.checksum } <dd className='flex items-start gap-x-2'>
</div> <div className='font-medium line-clamp-4'>
</dd> {item.duration_sec}
</div>
</dd>
</div> </div>
<div className='flex justify-between gap-x-4 py-3'> <div className='flex justify-between gap-x-4 py-3'>
<dt className=' text-gray-500 dark:text-dark-600'>Ispublic</dt> <dt className=' text-gray-500 dark:text-dark-600'>
<dd className='flex items-start gap-x-2'> Checksum
<div className='font-medium line-clamp-4'> </dt>
{ dataFormatter.booleanFormatter(item.is_public) } <dd className='flex items-start gap-x-2'>
</div> <div className='font-medium line-clamp-4'>
</dd> {item.checksum}
</div>
</dd>
</div> </div>
<div className='flex justify-between gap-x-4 py-3'> <div className='flex justify-between gap-x-4 py-3'>
<dt className=' text-gray-500 dark:text-dark-600'>Isdeleted</dt> <dt className=' text-gray-500 dark:text-dark-600'>
<dd className='flex items-start gap-x-2'> Ispublic
<div className='font-medium line-clamp-4'> </dt>
{ dataFormatter.booleanFormatter(item.is_deleted) } <dd className='flex items-start gap-x-2'>
</div> <div className='font-medium line-clamp-4'>
</dd> {dataFormatter.booleanFormatter(item.is_public)}
</div>
</dd>
</div> </div>
<div className='flex justify-between gap-x-4 py-3'> <div className='flex justify-between gap-x-4 py-3'>
<dt className=' text-gray-500 dark:text-dark-600'>Deletedat</dt> <dt className=' text-gray-500 dark:text-dark-600'>
<dd className='flex items-start gap-x-2'> Isdeleted
<div className='font-medium line-clamp-4'> </dt>
{ dataFormatter.dateTimeFormatter(item.deleted_at_time) } <dd className='flex items-start gap-x-2'>
</div> <div className='font-medium line-clamp-4'>
</dd> {dataFormatter.booleanFormatter(item.is_deleted)}
</div>
</dd>
</div> </div>
<div className='flex justify-between gap-x-4 py-3'>
<dt className=' text-gray-500 dark:text-dark-600'>
</dl> Deletedat
</li> </dt>
))} <dd className='flex items-start gap-x-2'>
<div className='font-medium line-clamp-4'>
{dataFormatter.dateTimeFormatter(item.deleted_at_time)}
</div>
</dd>
</div>
</dl>
</li>
))}
{!loading && assets.length === 0 && ( {!loading && assets.length === 0 && (
<div className='col-span-full flex items-center justify-center h-40'> <div className='col-span-full flex items-center justify-center h-40'>
<p className=''>No data to display</p> <p className=''>No data to display</p>

View File

@ -2,191 +2,166 @@ import React from 'react';
import CardBox from '../CardBox'; import CardBox from '../CardBox';
import ImageField from '../ImageField'; import ImageField from '../ImageField';
import dataFormatter from '../../helpers/dataFormatter'; import dataFormatter from '../../helpers/dataFormatter';
import {saveFile} from "../../helpers/fileSaver"; import { saveFile } from '../../helpers/fileSaver';
import ListActionsPopover from "../ListActionsPopover"; import ListActionsPopover from '../ListActionsPopover';
import {useAppSelector} from "../../stores/hooks"; import { useAppSelector } from '../../stores/hooks';
import {Pagination} from "../Pagination"; import { Pagination } from '../Pagination';
import LoadingSpinner from "../LoadingSpinner"; import LoadingSpinner from '../LoadingSpinner';
import Link from 'next/link'; import Link from 'next/link';
import {hasPermission} from "../../helpers/userPermissions"; import { hasPermission } from '../../helpers/userPermissions';
type Props = { type Props = {
assets: any[]; assets: any[];
loading: boolean; loading: boolean;
onDelete: (id: string) => void; onDelete: (id: string) => void;
currentPage: number; currentPage: number;
numPages: number; numPages: number;
onPageChange: (page: number) => void; onPageChange: (page: number) => void;
}; };
const ListAssets = ({ assets, loading, onDelete, currentPage, numPages, onPageChange }: Props) => { const ListAssets = ({
assets,
loading,
onDelete,
currentPage,
numPages,
onPageChange,
}: Props) => {
const currentUser = useAppSelector((state) => state.auth.currentUser);
const hasUpdatePermission = hasPermission(currentUser, 'UPDATE_ASSETS');
const currentUser = useAppSelector((state) => state.auth.currentUser); const corners = useAppSelector((state) => state.style.corners);
const hasUpdatePermission = hasPermission(currentUser, 'UPDATE_ASSETS') const bgColor = useAppSelector((state) => state.style.cardsColor);
const corners = useAppSelector((state) => state.style.corners); return (
const bgColor = useAppSelector((state) => state.style.cardsColor); <>
<div className='relative overflow-x-auto p-4 space-y-4'>
{loading && <LoadingSpinner />}
{!loading &&
assets.map((item) => (
<div key={item.id}>
<CardBox hasTable isList className={'rounded shadow-none'}>
<div
className={`flex rounded dark:bg-dark-900 border border-stone-300 items-center overflow-hidden`}
>
<Link
href={`/assets/assets-view/?id=${item.id}`}
className={
'flex-1 px-4 py-6 h-24 flex divide-x-2 divide-stone-300 items-center overflow-hidden`}> dark:divide-dark-700 overflow-x-auto'
}
>
<div className={'flex-1 px-3'}>
<p className={'text-xs text-gray-500 '}>Project</p>
<p className={'line-clamp-2'}>
{dataFormatter.projectsOneListFormatter(item.project)}
</p>
</div>
<div className={'flex-1 px-3'}>
<p className={'text-xs text-gray-500 '}>Name</p>
<p className={'line-clamp-2'}>{item.name}</p>
</div>
return ( <div className={'flex-1 px-3'}>
<> <p className={'text-xs text-gray-500 '}>Asset format</p>
<div className='relative overflow-x-auto p-4 space-y-4'> <p className={'line-clamp-2'}>{item.asset_type}</p>
{loading && <LoadingSpinner />} </div>
{!loading && assets.map((item) => (
<div key={item.id}>
<CardBox hasTable isList className={'rounded shadow-none'}>
<div className={`flex rounded dark:bg-dark-900 border border-stone-300 items-center overflow-hidden`}>
<Link <div className={'flex-1 px-3'}>
href={`/assets/assets-view/?id=${item.id}`} <p className={'text-xs text-gray-500 '}>Type</p>
className={ <p className={'line-clamp-2'}>{item.type || 'general'}</p>
'flex-1 px-4 py-6 h-24 flex divide-x-2 divide-stone-300 items-center overflow-hidden`}> dark:divide-dark-700 overflow-x-auto' </div>
}
>
<div className={'flex-1 px-3'}>
<p className={'text-xs text-gray-500 '}>CDNURL</p>
<p className={'line-clamp-2'}>{item.cdn_url}</p>
</div>
<div className={'flex-1 px-3'}> <div className={'flex-1 px-3'}>
<p className={'text-xs text-gray-500 '}>Project</p> <p className={'text-xs text-gray-500 '}>Storagekey</p>
<p className={'line-clamp-2'}>{ dataFormatter.projectsOneListFormatter(item.project) }</p> <p className={'line-clamp-2'}>{item.storage_key}</p>
</div> </div>
<div className={'flex-1 px-3'}>
<p className={'text-xs text-gray-500 '}>MIMEtype</p>
<p className={'line-clamp-2'}>{item.mime_type}</p>
</div>
<div className={'flex-1 px-3'}>
<p className={'text-xs text-gray-500 '}>Size(MB)</p>
<p className={'line-clamp-2'}>{item.size_mb}</p>
</div>
<div className={'flex-1 px-3'}>
<p className={'text-xs text-gray-500 '}>Width(px)</p>
<p className={'line-clamp-2'}>{item.width_px}</p>
</div>
<div className={'flex-1 px-3'}> <div className={'flex-1 px-3'}>
<p className={'text-xs text-gray-500 '}>Name</p> <p className={'text-xs text-gray-500 '}>Height(px)</p>
<p className={'line-clamp-2'}>{ item.name }</p> <p className={'line-clamp-2'}>{item.height_px}</p>
</div> </div>
<div className={'flex-1 px-3'}>
<p className={'text-xs text-gray-500 '}>
Duration(sec)
</p>
<p className={'line-clamp-2'}>{item.duration_sec}</p>
</div>
<div className={'flex-1 px-3'}>
<p className={'text-xs text-gray-500 '}>Checksum</p>
<p className={'line-clamp-2'}>{item.checksum}</p>
</div>
<div className={'flex-1 px-3'}>
<p className={'text-xs text-gray-500 '}>Ispublic</p>
<p className={'line-clamp-2'}>
{dataFormatter.booleanFormatter(item.is_public)}
</p>
</div>
<div className={'flex-1 px-3'}> <div className={'flex-1 px-3'}>
<p className={'text-xs text-gray-500 '}>Assettype</p> <p className={'text-xs text-gray-500 '}>Isdeleted</p>
<p className={'line-clamp-2'}>{ item.asset_type }</p> <p className={'line-clamp-2'}>
</div> {dataFormatter.booleanFormatter(item.is_deleted)}
</p>
</div>
<div className={'flex-1 px-3'}>
<p className={'text-xs text-gray-500 '}>Deletedat</p>
<p className={'line-clamp-2'}>
<div className={'flex-1 px-3'}> {dataFormatter.dateTimeFormatter(item.deleted_at_time)}
<p className={'text-xs text-gray-500 '}>CDNURL</p> </p>
<p className={'line-clamp-2'}>{ item.cdn_url }</p> </div>
</div> </Link>
<ListActionsPopover
onDelete={onDelete}
itemId={item.id}
pathEdit={`/assets/assets-edit/?id=${item.id}`}
<div className={'flex-1 px-3'}> pathView={`/assets/assets-view/?id=${item.id}`}
<p className={'text-xs text-gray-500 '}>Storagekey</p> hasUpdatePermission={hasUpdatePermission}
<p className={'line-clamp-2'}>{ item.storage_key }</p> />
</div> </div>
</CardBox>
<div className={'flex-1 px-3'}>
<p className={'text-xs text-gray-500 '}>MIMEtype</p>
<p className={'line-clamp-2'}>{ item.mime_type }</p>
</div>
<div className={'flex-1 px-3'}>
<p className={'text-xs text-gray-500 '}>Size(MB)</p>
<p className={'line-clamp-2'}>{ item.size_mb }</p>
</div>
<div className={'flex-1 px-3'}>
<p className={'text-xs text-gray-500 '}>Width(px)</p>
<p className={'line-clamp-2'}>{ item.width_px }</p>
</div>
<div className={'flex-1 px-3'}>
<p className={'text-xs text-gray-500 '}>Height(px)</p>
<p className={'line-clamp-2'}>{ item.height_px }</p>
</div>
<div className={'flex-1 px-3'}>
<p className={'text-xs text-gray-500 '}>Duration(sec)</p>
<p className={'line-clamp-2'}>{ item.duration_sec }</p>
</div>
<div className={'flex-1 px-3'}>
<p className={'text-xs text-gray-500 '}>Checksum</p>
<p className={'line-clamp-2'}>{ item.checksum }</p>
</div>
<div className={'flex-1 px-3'}>
<p className={'text-xs text-gray-500 '}>Ispublic</p>
<p className={'line-clamp-2'}>{ dataFormatter.booleanFormatter(item.is_public) }</p>
</div>
<div className={'flex-1 px-3'}>
<p className={'text-xs text-gray-500 '}>Isdeleted</p>
<p className={'line-clamp-2'}>{ dataFormatter.booleanFormatter(item.is_deleted) }</p>
</div>
<div className={'flex-1 px-3'}>
<p className={'text-xs text-gray-500 '}>Deletedat</p>
<p className={'line-clamp-2'}>{ dataFormatter.dateTimeFormatter(item.deleted_at_time) }</p>
</div>
</Link>
<ListActionsPopover
onDelete={onDelete}
itemId={item.id}
pathEdit={`/assets/assets-edit/?id=${item.id}`}
pathView={`/assets/assets-view/?id=${item.id}`}
hasUpdatePermission={hasUpdatePermission}
/>
</div>
</CardBox>
</div>
))}
{!loading && assets.length === 0 && (
<div className='col-span-full flex items-center justify-center h-40'>
<p className=''>No data to display</p>
</div>
)}
</div> </div>
<div className={'flex items-center justify-center my-6'}> ))}
<Pagination {!loading && assets.length === 0 && (
currentPage={currentPage} <div className='col-span-full flex items-center justify-center h-40'>
numPages={numPages} <p className=''>No data to display</p>
setCurrentPage={onPageChange} </div>
/> )}
</div> </div>
</> <div className={'flex items-center justify-center my-6'}>
) <Pagination
currentPage={currentPage}
numPages={numPages}
setCurrentPage={onPageChange}
/>
</div>
</>
);
}; };
export default ListAssets export default ListAssets;

View File

@ -1,463 +1,48 @@
import React, { useEffect, useState, useMemo } from 'react' /**
import { createPortal } from 'react-dom'; * Assets Table Component
import { ToastContainer, toast } from 'react-toastify'; */
import BaseButton from '../BaseButton'
import CardBoxModal from '../CardBoxModal' import React from 'react';
import CardBox from "../CardBox"; import GenericTable from '../Generic/GenericTable';
import { fetch, update, deleteItem, setRefetch, deleteItemsByIds } from '../../stores/assets/assetsSlice'
import { useAppDispatch, useAppSelector } from '../../stores/hooks'
import { useRouter } from 'next/router'
import { Field, Form, Formik } from "formik";
import { import {
DataGrid, fetch,
GridColDef, update,
} from '@mui/x-data-grid'; deleteItem,
import {loadColumns} from "./configureAssetsCols"; setRefetch,
import _ from 'lodash'; deleteItemsByIds,
import dataFormatter from '../../helpers/dataFormatter' } from '../../stores/assets/assetsSlice';
import {dataGridStyles} from "../../styles"; import { loadColumns } from './configureAssetsCols';
import type { Asset } from '../../types/entities';
import type { RootState } from '../../stores/store';
import type { Filter, FilterItem } from '../../types/filters';
interface TableAssetsProps {
filterItems: FilterItem[];
const perPage = 10 setFilterItems: (items: FilterItem[]) => void;
filters: Filter[];
const TableSampleAssets = ({ filterItems, setFilterItems, filters, showGrid }) => { showGrid?: boolean;
const notify = (type, msg) => toast( msg, {type, position: "bottom-center"});
const dispatch = useAppDispatch();
const router = useRouter();
const pagesList = [];
const [id, setId] = useState(null);
const [currentPage, setCurrentPage] = useState(0);
const [filterRequest, setFilterRequest] = React.useState('');
const [columns, setColumns] = useState<GridColDef[]>([]);
const [selectedRows, setSelectedRows] = useState([]);
const [sortModel, setSortModel] = useState([
{
field: '',
sort: 'desc',
},
]);
const { assets, loading, count, notify: assetsNotify, refetch } = useAppSelector((state) => state.assets)
const { currentUser } = useAppSelector((state) => state.auth);
const focusRing = useAppSelector((state) => state.style.focusRingColor);
const bgColor = useAppSelector((state) => state.style.bgLayoutColor);
const corners = useAppSelector((state) => state.style.corners);
const numPages = Math.floor(count / perPage) === 0 ? 1 : Math.ceil(count / perPage);
for (let i = 0; i < numPages; i++) {
pagesList.push(i);
}
const loadData = async (page = currentPage, request = filterRequest) => {
if (page !== currentPage) setCurrentPage(page);
if (request !== filterRequest) setFilterRequest(request);
const { sort, field } = sortModel[0];
const query = `?page=${page}&limit=${perPage}${request}&sort=${sort}&field=${field}`;
dispatch(fetch({ limit: perPage, page, query }));
};
useEffect(() => {
if (assetsNotify.showNotification) {
notify(assetsNotify.typeNotification, assetsNotify.textNotification);
}
}, [assetsNotify.showNotification]);
useEffect(() => {
if (!currentUser) return;
loadData();
}, [sortModel, currentUser]);
useEffect(() => {
if (refetch) {
loadData(0);
dispatch(setRefetch(false));
}
}, [refetch, dispatch]);
const [isModalInfoActive, setIsModalInfoActive] = useState(false)
const [isModalTrashActive, setIsModalTrashActive] = useState(false)
const handleModalAction = () => {
setIsModalInfoActive(false)
setIsModalTrashActive(false)
}
const handleDeleteModalAction = (id: string) => {
setId(id)
setIsModalTrashActive(true)
}
const handleDeleteAction = async () => {
if (id) {
await dispatch(deleteItem(id));
await loadData(0);
setIsModalTrashActive(false);
}
};
const generateFilterRequests = useMemo(() => {
let request = '&';
filterItems.forEach((item) => {
const isRangeFilter = filters.find(
(filter) =>
filter.title === item.fields.selectedField &&
(filter.number || filter.date),
);
if (isRangeFilter) {
const from = item.fields.filterValueFrom;
const to = item.fields.filterValueTo;
if (from) {
request += `${item.fields.selectedField}Range=${from}&`;
}
if (to) {
request += `${item.fields.selectedField}Range=${to}&`;
}
} else {
const value = item.fields.filterValue;
if (value) {
request += `${item.fields.selectedField}=${value}&`;
}
}
});
return request;
}, [filterItems, filters]);
const deleteFilter = (value) => {
const newItems = filterItems.filter((item) => item.id !== value);
if (newItems.length) {
setFilterItems(newItems);
} else {
loadData(0, '');
setFilterItems(newItems);
}
};
const handleSubmit = () => {
loadData(0, generateFilterRequests);
};
const handleChange = (id) => (e) => {
const value = e.target.value;
const name = e.target.name;
setFilterItems(
filterItems.map((item) => {
if (item.id !== id) return item;
if (name === 'selectedField') return { id, fields: { [name]: value } };
return { id, fields: { ...item.fields, [name]: value } }
}),
);
};
const handleReset = () => {
setFilterItems([]);
loadData(0, '');
};
const onPageChange = (page: number) => {
loadData(page);
setCurrentPage(page);
};
useEffect(() => {
if (!currentUser) return;
loadColumns(
handleDeleteModalAction,
`assets`,
currentUser,
).then((newCols) => setColumns(newCols));
}, [currentUser]);
const handleTableSubmit = async (id: string, data) => {
if (!_.isEmpty(data)) {
await dispatch(update({ id, data }))
.unwrap()
.then((res) => res)
.catch((err) => {
throw new Error(err);
});
}
};
const onDeleteRows = async (selectedRows) => {
await dispatch(deleteItemsByIds(selectedRows));
await loadData(0);
};
const controlClasses =
'w-full py-2 px-2 my-2 rounded dark:placeholder-gray-400 ' +
` ${bgColor} ${focusRing} ${corners} ` +
'dark:bg-slate-800 border';
const dataGrid = (
<div className='relative overflow-x-auto'>
<DataGrid
autoHeight
rowHeight={64}
sx={dataGridStyles}
className={'datagrid--table'}
getRowClassName={() => `datagrid--row`}
rows={assets ?? []}
columns={columns}
initialState={{
pagination: {
paginationModel: {
pageSize: 10,
},
},
}}
disableRowSelectionOnClick
onProcessRowUpdateError={(params) => {
console.log('Error', params);
}}
processRowUpdate={async (newRow, oldRow) => {
const data = dataFormatter.dataGridEditFormatter(newRow);
try {
await handleTableSubmit(newRow.id, data);
return newRow;
} catch {
return oldRow;
}
}}
sortingMode={'server'}
checkboxSelection
onRowSelectionModelChange={(ids) => {
setSelectedRows(ids)
}}
onSortModelChange={(params) => {
params.length
? setSortModel(params)
: setSortModel([{ field: '', sort: 'desc' }]);
}}
rowCount={count}
pageSizeOptions={[10]}
paginationMode={'server'}
loading={loading}
onPaginationModelChange={(params) => {
onPageChange(params.page);
}}
/>
</div>
)
return (
<>
{filterItems && Array.isArray( filterItems ) && filterItems.length ?
<CardBox>
<Formik
initialValues={{
checkboxes: ['lorem'],
switches: ['lorem'],
radio: 'lorem',
}}
onSubmit={() => null}
>
<Form>
<>
{filterItems && filterItems.map((filterItem) => {
return (
<div key={filterItem.id} className="flex mb-4">
<div className="flex flex-col w-full mr-3">
<div className=" text-gray-500 font-bold">Filter</div>
<Field
className={controlClasses}
name='selectedField'
id='selectedField'
component='select'
value={filterItem?.fields?.selectedField || ''}
onChange={handleChange(filterItem.id)}
>
{filters.map((selectOption) => (
<option
key={selectOption.title}
value={`${selectOption.title}`}
>
{selectOption.label}
</option>
))}
</Field>
</div>
{filters.find((filter) =>
filter.title === filterItem?.fields?.selectedField
)?.type === 'enum' ? (
<div className="flex flex-col w-full mr-3">
<div className="text-gray-500 font-bold">
Value
</div>
<Field
className={controlClasses}
name="filterValue"
id='filterValue'
component="select"
value={filterItem?.fields?.filterValue || ''}
onChange={handleChange(filterItem.id)}
>
<option value="">Select Value</option>
{filters.find((filter) =>
filter.title === filterItem?.fields?.selectedField
)?.options?.map((option) => (
<option key={option} value={option}>
{option}
</option>
))}
</Field>
</div>
) : filters.find((filter) =>
filter.title === filterItem?.fields?.selectedField
)?.number ? (
<div className="flex flex-row w-full mr-3">
<div className="flex flex-col w-full mr-3">
<div className=" text-gray-500 font-bold">From</div>
<Field
className={controlClasses}
name='filterValueFrom'
placeholder='From'
id='filterValueFrom'
value={filterItem?.fields?.filterValueFrom || ''}
onChange={handleChange(filterItem.id)}
/>
</div>
<div className="flex flex-col w-full">
<div className=" text-gray-500 font-bold">To</div>
<Field
className={controlClasses}
name='filterValueTo'
placeholder='to'
id='filterValueTo'
value={filterItem?.fields?.filterValueTo || ''}
onChange={handleChange(filterItem.id)}
/>
</div>
</div>
) : filters.find(
(filter) =>
filter.title ===
filterItem?.fields?.selectedField
)?.date ? (
<div className='flex flex-row w-full mr-3'>
<div className='flex flex-col w-full mr-3'>
<div className=' text-gray-500 font-bold'>
From
</div>
<Field
className={controlClasses}
name='filterValueFrom'
placeholder='From'
id='filterValueFrom'
type='datetime-local'
value={filterItem?.fields?.filterValueFrom || ''}
onChange={handleChange(filterItem.id)}
/>
</div>
<div className='flex flex-col w-full'>
<div className=' text-gray-500 font-bold'>To</div>
<Field
className={controlClasses}
name='filterValueTo'
placeholder='to'
id='filterValueTo'
type='datetime-local'
value={filterItem?.fields?.filterValueTo || ''}
onChange={handleChange(filterItem.id)}
/>
</div>
</div>
) : (
<div className="flex flex-col w-full mr-3">
<div className=" text-gray-500 font-bold">Contains</div>
<Field
className={controlClasses}
name='filterValue'
placeholder='Contained'
id='filterValue'
value={filterItem?.fields?.filterValue || ''}
onChange={handleChange(filterItem.id)}
/>
</div>
)}
<div className="flex flex-col">
<div className=" text-gray-500 font-bold">Action</div>
<BaseButton
className="my-2"
type='reset'
color='danger'
label='Delete'
onClick={() => {
deleteFilter(filterItem.id)
}}
/>
</div>
</div>
)
})}
<div className="flex">
<BaseButton
className="my-2 mr-3"
color="success"
label='Apply'
onClick={handleSubmit}
/>
<BaseButton
className="my-2"
color='info'
label='Cancel'
onClick={handleReset}
/>
</div>
</>
</Form>
</Formik>
</CardBox> : null
}
<CardBoxModal
title="Please confirm"
buttonColor="info"
buttonLabel={loading ? 'Deleting...' : 'Confirm'}
isActive={isModalTrashActive}
onConfirm={handleDeleteAction}
onCancel={handleModalAction}
>
<p>Are you sure you want to delete this item?</p>
</CardBoxModal>
{dataGrid}
{selectedRows.length > 0 &&
createPortal(
<BaseButton
className='me-4'
color='danger'
label={`Delete ${selectedRows.length === 1 ? 'Row' : 'Rows'}`}
onClick={() => onDeleteRows(selectedRows)}
/>,
document.getElementById('delete-rows-button'),
)}
<ToastContainer />
</>
)
} }
export default TableSampleAssets const TableAssets: React.FC<TableAssetsProps> = ({
filterItems,
setFilterItems,
filters,
}) => {
return (
<GenericTable<Asset>
entityName='assets'
sliceSelector={(state: RootState) => state.assets}
fetchAction={fetch}
updateAction={update}
deleteAction={deleteItem}
deleteByIdsAction={deleteItemsByIds}
setRefetchAction={setRefetch}
loadColumnsFunction={loadColumns}
filters={filters}
filterItems={filterItems}
setFilterItems={setFilterItems}
/>
);
};
export default TableAssets;

View File

@ -3,292 +3,264 @@ import BaseIcon from '../BaseIcon';
import { mdiEye, mdiTrashCan, mdiPencilOutline } from '@mdi/js'; import { mdiEye, mdiTrashCan, mdiPencilOutline } from '@mdi/js';
import axios from 'axios'; import axios from 'axios';
import { import {
GridActionsCellItem, GridActionsCellItem,
GridRowParams, GridRowParams,
GridValueGetterParams, GridValueGetterParams,
} from '@mui/x-data-grid'; } from '@mui/x-data-grid';
import ImageField from '../ImageField'; import ImageField from '../ImageField';
import {saveFile} from "../../helpers/fileSaver"; import { saveFile } from '../../helpers/fileSaver';
import dataFormatter from '../../helpers/dataFormatter' import dataFormatter from '../../helpers/dataFormatter';
import DataGridMultiSelect from "../DataGridMultiSelect"; import DataGridMultiSelect from '../DataGridMultiSelect';
import ListActionsPopover from '../ListActionsPopover'; import ListActionsPopover from '../ListActionsPopover';
import {hasPermission} from "../../helpers/userPermissions"; import { hasPermission } from '../../helpers/userPermissions';
type Params = (id: string) => void; type Params = (id: string) => void;
export const loadColumns = async ( export const loadColumns = async (
onDelete: Params, onDelete: Params,
entityName: string, entityName: string,
user
user,
) => { ) => {
async function callOptionsApi(entityName: string) { async function callOptionsApi(entityName: string) {
if (!hasPermission(user, 'READ_' + entityName.toUpperCase())) return [];
if (!hasPermission(user, 'READ_' + entityName.toUpperCase())) return []; try {
const data = await axios(`/${entityName}/autocomplete?limit=100`);
try { return data.data;
const data = await axios(`/${entityName}/autocomplete?limit=100`); } catch (error) {
return data.data; console.log(error);
} catch (error) { return [];
console.log(error);
return [];
}
} }
}
const hasUpdatePermission = hasPermission(user, 'UPDATE_ASSETS')
const hasUpdatePermission = hasPermission(user, 'UPDATE_ASSETS');
return [
return [
{ {
field: 'project', field: 'project',
headerName: 'Project', headerName: 'Project',
flex: 1, flex: 1,
minWidth: 120, minWidth: 120,
filterable: false, filterable: false,
headerClassName: 'datagrid--header', headerClassName: 'datagrid--header',
cellClassName: 'datagrid--cell', cellClassName: 'datagrid--cell',
editable: hasUpdatePermission,
editable: hasUpdatePermission,
sortable: false,
sortable: false, type: 'singleSelect',
type: 'singleSelect', getOptionValue: (value: any) => value?.id,
getOptionValue: (value: any) => value?.id, getOptionLabel: (value: any) => value?.label,
getOptionLabel: (value: any) => value?.label, valueOptions: await callOptionsApi('projects'),
valueOptions: await callOptionsApi('projects'), valueGetter: (params: GridValueGetterParams) =>
valueGetter: (params: GridValueGetterParams) => params?.value?.id ?? params?.value,
params?.value?.id ?? params?.value, },
}, {
field: 'name',
{ headerName: 'Name',
field: 'name', flex: 1,
headerName: 'Name', minWidth: 120,
flex: 1, filterable: false,
minWidth: 120, headerClassName: 'datagrid--header',
filterable: false, cellClassName: 'datagrid--cell',
headerClassName: 'datagrid--header',
cellClassName: 'datagrid--cell', editable: hasUpdatePermission,
},
editable: hasUpdatePermission, {
field: 'asset_type',
headerName: 'Asset format',
}, flex: 1,
minWidth: 120,
{ filterable: false,
field: 'asset_type', headerClassName: 'datagrid--header',
headerName: 'Assettype', cellClassName: 'datagrid--cell',
flex: 1,
minWidth: 120, editable: hasUpdatePermission,
filterable: false, },
headerClassName: 'datagrid--header',
cellClassName: 'datagrid--cell', {
field: 'type',
headerName: 'Type',
editable: hasUpdatePermission, flex: 1,
minWidth: 120,
filterable: false,
}, headerClassName: 'datagrid--header',
cellClassName: 'datagrid--cell',
{
field: 'cdn_url', editable: hasUpdatePermission,
headerName: 'CDNURL', },
flex: 1,
minWidth: 120, {
filterable: false, field: 'cdn_url',
headerClassName: 'datagrid--header', headerName: 'CDNURL',
cellClassName: 'datagrid--cell', flex: 1,
minWidth: 120,
filterable: false,
editable: hasUpdatePermission, headerClassName: 'datagrid--header',
cellClassName: 'datagrid--cell',
}, editable: hasUpdatePermission,
},
{
field: 'storage_key', {
headerName: 'Storagekey', field: 'storage_key',
flex: 1, headerName: 'Storagekey',
minWidth: 120, flex: 1,
filterable: false, minWidth: 120,
headerClassName: 'datagrid--header', filterable: false,
cellClassName: 'datagrid--cell', headerClassName: 'datagrid--header',
cellClassName: 'datagrid--cell',
editable: hasUpdatePermission, editable: hasUpdatePermission,
},
}, {
field: 'mime_type',
{ headerName: 'MIMEtype',
field: 'mime_type', flex: 1,
headerName: 'MIMEtype', minWidth: 120,
flex: 1, filterable: false,
minWidth: 120, headerClassName: 'datagrid--header',
filterable: false, cellClassName: 'datagrid--cell',
headerClassName: 'datagrid--header',
cellClassName: 'datagrid--cell', editable: hasUpdatePermission,
},
editable: hasUpdatePermission, {
field: 'size_mb',
headerName: 'Size(MB)',
}, flex: 1,
minWidth: 120,
{ filterable: false,
field: 'size_mb', headerClassName: 'datagrid--header',
headerName: 'Size(MB)', cellClassName: 'datagrid--cell',
flex: 1,
minWidth: 120, editable: hasUpdatePermission,
filterable: false,
headerClassName: 'datagrid--header', type: 'number',
cellClassName: 'datagrid--cell', },
{
editable: hasUpdatePermission, field: 'width_px',
headerName: 'Width(px)',
type: 'number', flex: 1,
minWidth: 120,
}, filterable: false,
headerClassName: 'datagrid--header',
{ cellClassName: 'datagrid--cell',
field: 'width_px',
headerName: 'Width(px)', editable: hasUpdatePermission,
flex: 1,
minWidth: 120, type: 'number',
filterable: false, },
headerClassName: 'datagrid--header',
cellClassName: 'datagrid--cell', {
field: 'height_px',
headerName: 'Height(px)',
editable: hasUpdatePermission, flex: 1,
minWidth: 120,
type: 'number', filterable: false,
headerClassName: 'datagrid--header',
}, cellClassName: 'datagrid--cell',
{ editable: hasUpdatePermission,
field: 'height_px',
headerName: 'Height(px)', type: 'number',
flex: 1, },
minWidth: 120,
filterable: false, {
headerClassName: 'datagrid--header', field: 'duration_sec',
cellClassName: 'datagrid--cell', headerName: 'Duration(sec)',
flex: 1,
minWidth: 120,
editable: hasUpdatePermission, filterable: false,
headerClassName: 'datagrid--header',
type: 'number', cellClassName: 'datagrid--cell',
}, editable: hasUpdatePermission,
{ type: 'number',
field: 'duration_sec', },
headerName: 'Duration(sec)',
flex: 1, {
minWidth: 120, field: 'checksum',
filterable: false, headerName: 'Checksum',
headerClassName: 'datagrid--header', flex: 1,
cellClassName: 'datagrid--cell', minWidth: 120,
filterable: false,
headerClassName: 'datagrid--header',
editable: hasUpdatePermission, cellClassName: 'datagrid--cell',
type: 'number', editable: hasUpdatePermission,
},
},
{
{ field: 'is_public',
field: 'checksum', headerName: 'Ispublic',
headerName: 'Checksum', flex: 1,
flex: 1, minWidth: 120,
minWidth: 120, filterable: false,
filterable: false, headerClassName: 'datagrid--header',
headerClassName: 'datagrid--header', cellClassName: 'datagrid--cell',
cellClassName: 'datagrid--cell',
editable: hasUpdatePermission,
editable: hasUpdatePermission, type: 'boolean',
},
}, {
field: 'is_deleted',
{ headerName: 'Isdeleted',
field: 'is_public', flex: 1,
headerName: 'Ispublic', minWidth: 120,
flex: 1, filterable: false,
minWidth: 120, headerClassName: 'datagrid--header',
filterable: false, cellClassName: 'datagrid--cell',
headerClassName: 'datagrid--header',
cellClassName: 'datagrid--cell', editable: hasUpdatePermission,
type: 'boolean',
editable: hasUpdatePermission, },
type: 'boolean', {
field: 'deleted_at_time',
}, headerName: 'Deletedat',
flex: 1,
{ minWidth: 120,
field: 'is_deleted', filterable: false,
headerName: 'Isdeleted', headerClassName: 'datagrid--header',
flex: 1, cellClassName: 'datagrid--cell',
minWidth: 120,
filterable: false, editable: hasUpdatePermission,
headerClassName: 'datagrid--header',
cellClassName: 'datagrid--cell', type: 'dateTime',
valueGetter: (params: GridValueGetterParams) =>
new Date(params.row.deleted_at_time),
editable: hasUpdatePermission, },
type: 'boolean', {
field: 'actions',
}, type: 'actions',
minWidth: 30,
{ headerClassName: 'datagrid--header',
field: 'deleted_at_time', cellClassName: 'datagrid--cell',
headerName: 'Deletedat', getActions: (params: GridRowParams) => {
flex: 1, return [
minWidth: 120, <div key={params?.row?.id}>
filterable: false, <ListActionsPopover
headerClassName: 'datagrid--header', onDelete={onDelete}
cellClassName: 'datagrid--cell', itemId={params?.row?.id}
pathEdit={`/assets/assets-edit/?id=${params?.row?.id}`}
pathView={`/assets/assets-view/?id=${params?.row?.id}`}
editable: hasUpdatePermission, hasUpdatePermission={hasUpdatePermission}
/>
type: 'dateTime', </div>,
valueGetter: (params: GridValueGetterParams) => ];
new Date(params.row.deleted_at_time), },
},
}, ];
{
field: 'actions',
type: 'actions',
minWidth: 30,
headerClassName: 'datagrid--header',
cellClassName: 'datagrid--cell',
getActions: (params: GridRowParams) => {
return [
<div key={params?.row?.id}>
<ListActionsPopover
onDelete={onDelete}
itemId={params?.row?.id}
pathEdit={`/assets/assets-edit/?id=${params?.row?.id}`}
pathView={`/assets/assets-view/?id=${params?.row?.id}`}
hasUpdatePermission={hasUpdatePermission}
/>
</div>,
]
},
},
];
}; };

View File

@ -1,28 +1,28 @@
import React from 'react' import React from 'react';
import Link from 'next/link' import Link from 'next/link';
import { getButtonColor } from '../colors' import { getButtonColor } from '../colors';
import BaseIcon from './BaseIcon' import BaseIcon from './BaseIcon';
import type { ColorButtonKey } from '../interfaces' import type { ColorButtonKey } from '../interfaces';
import { useAppSelector } from '../stores/hooks'; import { useAppSelector } from '../stores/hooks';
type Props = { type Props = {
label?: string label?: string;
icon?: string icon?: string;
iconSize?: string | number iconSize?: string | number;
href?: string href?: string;
target?: string target?: string;
type?: string type?: string;
color?: ColorButtonKey color?: ColorButtonKey;
className?: string className?: string;
iconClassName?: string iconClassName?: string;
asAnchor?: boolean asAnchor?: boolean;
small?: boolean small?: boolean;
outline?: boolean outline?: boolean;
active?: boolean active?: boolean;
disabled?: boolean disabled?: boolean;
roundedFull?: boolean roundedFull?: boolean;
onClick?: (e: React.MouseEvent) => void onClick?: (e: React.MouseEvent) => void;
} };
export default function BaseButton({ export default function BaseButton({
label, label,
@ -57,40 +57,50 @@ export default function BaseButton({
roundedFull ? 'rounded-full' : `${corners}`, roundedFull ? 'rounded-full' : `${corners}`,
getButtonColor(color, outline, !disabled, active), getButtonColor(color, outline, !disabled, active),
className, className,
] ];
if (!label && icon) { if (!label && icon) {
componentClass.push('p-1') componentClass.push('p-1');
} else if (small) { } else if (small) {
componentClass.push('text-sm', roundedFull ? 'px-3 py-1' : 'p-1') componentClass.push('text-sm', roundedFull ? 'px-3 py-1' : 'p-1');
} else { } else {
componentClass.push('py-2', roundedFull ? 'px-6' : 'px-3') componentClass.push('py-2', roundedFull ? 'px-6' : 'px-3');
} }
if (disabled) { if (disabled) {
componentClass.push(outline ? 'opacity-50' : 'opacity-70') componentClass.push(outline ? 'opacity-50' : 'opacity-70');
} }
const componentClassString = componentClass.join(' ') const componentClassString = componentClass.join(' ');
const componentChildren = ( const componentChildren = (
<> <>
{icon && <BaseIcon path={icon} size={iconSize} className={iconClassName} />} {icon && (
{label && <span className={small && icon ? 'px-1' : 'px-2'}>{label}</span>} <BaseIcon path={icon} size={iconSize} className={iconClassName} />
)}
{label && (
<span className={small && icon ? 'px-1' : 'px-2'}>{label}</span>
)}
</> </>
) );
if (href && !disabled) { if (href && !disabled) {
return ( return (
<Link href={href} target={target} className={componentClassString}> <Link href={href} target={target} className={componentClassString}>
{componentChildren} {componentChildren}
</Link> </Link>
) );
} }
return React.createElement( return React.createElement(
asAnchor ? 'a' : 'button', asAnchor ? 'a' : 'button',
{ className: componentClassString, type: type ?? 'button', target, disabled, onClick }, {
componentChildren className: componentClassString,
) type: type ?? 'button',
target,
disabled,
onClick,
},
componentChildren,
);
} }

View File

@ -11,27 +11,27 @@ type Props = {
}; };
const BaseButtons = ({ const BaseButtons = ({
type = 'justify-end', type = 'justify-end',
mb = '-mb-3', mb = '-mb-3',
classAddon = 'mr-3 last:mr-0 mb-3', classAddon = 'mr-3 last:mr-0 mb-3',
noWrap = false, noWrap = false,
children, children,
className, className,
}: Props) => { }: Props) => {
return ( return (
<div <div
className={`flex items-center ${type} ${className} ${mb} ${ className={`flex items-center ${type} ${className} ${mb} ${
noWrap ? 'flex-nowrap' : 'flex-wrap' noWrap ? 'flex-nowrap' : 'flex-wrap'
}`} }`}
> >
{Children.map(children, (child: ReactElement) => {Children.map(children, (child: ReactElement) =>
child child
? cloneElement(child as ReactElement<{ className?: string }>, { ? cloneElement(child as ReactElement<{ className?: string }>, {
className: `${classAddon} ${(child.props as { className?: string }).className || ''}`, className: `${classAddon} ${(child.props as { className?: string }).className || ''}`,
}) })
: null, : null,
)} )}
</div> </div>
); );
}; };

View File

@ -1,14 +1,14 @@
import React from 'react' import React from 'react';
import { useAppSelector } from '../stores/hooks'; import { useAppSelector } from '../stores/hooks';
type Props = { type Props = {
navBar?: boolean navBar?: boolean;
} };
export default function BaseDivider({ navBar = false }: Props) { export default function BaseDivider({ navBar = false }: Props) {
const borders = useAppSelector((state) => state.style.borders); const borders = useAppSelector((state) => state.style.borders);
const classAddon = navBar const classAddon = navBar
? 'hidden lg:block lg:my-0.5 dark:border-dark-700' ? 'hidden lg:block lg:my-0.5 dark:border-dark-700'
: 'my-6 -mx-6 dark:border-dark-800' : 'my-6 -mx-6 dark:border-dark-800';
return <hr className={`${classAddon} border-t ${borders} `} /> return <hr className={`${classAddon} border-t ${borders} `} />;
} }

Some files were not shown because too many files have changed in this diff Show More