@opengis/fastify-table 1.4.53 → 1.4.55
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +86 -86
- package/package.json +1 -1
- package/server/helpers/core/buttonHelper.js +1 -1
- package/server/helpers/core/token.js +18 -18
- package/server/helpers/format/formatAuto.js +13 -13
- package/server/helpers/format/formatDate.js +258 -258
- package/server/helpers/format/formatDigit.js +21 -21
- package/server/helpers/format/formatNum.js +365 -365
- package/server/helpers/format/formatNumber.js +55 -55
- package/server/helpers/format/formatRelative.js +106 -106
- package/server/helpers/format/formatUnit.js +40 -40
- package/server/helpers/format/num_format.js +44 -44
- package/server/helpers/format/set.js +2 -2
- package/server/helpers/funcs/_math.js +50 -50
- package/server/helpers/funcs/empty.js +21 -21
- package/server/helpers/funcs/ifCond.js +109 -109
- package/server/helpers/funcs/ifCondAnd.js +114 -114
- package/server/helpers/funcs/ifCondOr.js +115 -115
- package/server/helpers/funcs/inc.js +20 -20
- package/server/helpers/funcs/json.js +3 -3
- package/server/helpers/funcs/qrcode.js +2 -2
- package/server/helpers/funcs/round.js +29 -29
- package/server/helpers/list/buttonHelper.js +22 -22
- package/server/helpers/list/utils/button.js +4 -4
- package/server/helpers/string/coalesce.js +39 -39
- package/server/helpers/string/concat.js +28 -28
- package/server/helpers/string/split.js +20 -20
- package/server/helpers/string/str_replace.js +62 -62
- package/server/helpers/string/substr.js +32 -32
- package/server/helpers/string/translit.js +23 -23
- package/server/helpers/string/utils/alphabet.js +76 -76
- package/server/helpers/utils/button.js +4 -4
- package/server/helpers/utils/buttonAdd.js +1 -1
- package/server/helpers/utils/buttonDel.js +1 -1
- package/server/helpers/utils/buttonDownload.js +1 -1
- package/server/helpers/utils/buttonEdit.js +2 -2
- package/server/helpers/utils/buttonPreview.js +1 -1
- package/server/helpers/utils/mdToHTML.js +17 -17
- package/server/plugins/cron/funcs/addCron.js +52 -52
- package/server/plugins/cron/funcs/interval2ms.js +1 -1
- package/server/plugins/cron/index.js +77 -77
- package/server/plugins/crud/funcs/getAccess.js +3 -2
- package/server/plugins/crud/funcs/getOpt.js +14 -14
- package/server/plugins/crud/funcs/setOpt.js +21 -21
- package/server/plugins/crud/funcs/setToken.js +43 -43
- package/server/plugins/crud/funcs/utils/getFolder.js +11 -11
- package/server/plugins/crud/index.js +23 -23
- package/server/plugins/file/uploadMultiPart.js +8 -6
- package/server/plugins/hook/index.js +8 -8
- package/server/plugins/logger/errorStatus.js +19 -19
- package/server/plugins/logger/index.js +33 -33
- package/server/plugins/logger/timestampWithTimeZone.js +1 -1
- package/server/plugins/migration/index.js +7 -7
- package/server/plugins/pg/funcs/getDBParams.js +3 -3
- package/server/plugins/policy/sqlInjection.js +34 -34
- package/server/plugins/redis/client.js +8 -8
- package/server/plugins/redis/funcs/redisClients.js +3 -3
- package/server/plugins/redis/index.js +17 -17
- package/server/plugins/table/funcs/customTokens.js +1 -1
- package/server/plugins/table/funcs/getFilterSQL/util/getCustomQuery.js +13 -13
- package/server/plugins/table/funcs/getFilterSQL/util/getOptimizedQuery.js +2 -2
- package/server/plugins/table/funcs/getFilterSQL/util/getTableSql.js +34 -34
- package/server/plugins/table/funcs/getTemplates.js +19 -19
- package/server/plugins/table/funcs/gisIRColumn.js +82 -82
- package/server/plugins/table/funcs/loadTemplate.js +1 -1
- package/server/plugins/table/funcs/loadTemplatePath.js +1 -1
- package/server/plugins/table/funcs/userTemplateDir.js +1 -1
- package/server/plugins/table/funcs/userTokens.js +1 -1
- package/server/plugins/util/index.js +7 -7
- package/server/plugins/yml/funcs/json2yml.js +1 -1
- package/server/plugins/yml/funcs/yml2json.js +2 -2
- package/server/routes/file/controllers/export.js +1 -1
- package/server/routes/file/controllers/utils/jsonToXls.js +1 -2
- package/server/routes/logger/controllers/utils/getRootDir.js +27 -27
- package/server/routes/table/controllers/form.js +42 -42
- package/server/routes/table/controllers/search.js +74 -74
- package/server/routes/table/controllers/utils/locales.js +1 -1
- package/server/routes/util/controllers/status.monitor.js +8 -8
- package/server/routes/widget/controllers/widget.del.js +89 -89
- package/server/routes/widget/controllers/widget.set.js +106 -106
- package/server/routes/widget/hook/onWidgetSet.js +13 -13
- package/server/routes/widget/index.mjs +38 -38
|
@@ -1,42 +1,42 @@
|
|
|
1
|
-
import { applyHook, getTemplate } from '../../../../utils.js';
|
|
2
|
-
|
|
3
|
-
const sql = `select property_key as key, property_json as json, property_int as int,
|
|
4
|
-
property_text as text from admin.properties where 1=1`;
|
|
5
|
-
|
|
6
|
-
async function getSettings({ pg }) {
|
|
7
|
-
const { rows = [] } = await pg.query(sql);
|
|
8
|
-
const data = rows.reduce((acc, curr) => Object.assign(acc, { [curr.key]: curr.json || curr.int || curr.text }), {});
|
|
9
|
-
return data;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export default async function formFunction(req) {
|
|
13
|
-
const time = Date.now();
|
|
14
|
-
|
|
15
|
-
const { pg, params, user } = req;
|
|
16
|
-
const hookData = await applyHook('preForm', { form: params?.form, user });
|
|
17
|
-
|
|
18
|
-
if (hookData?.message && hookData?.status) {
|
|
19
|
-
return { message: hookData?.message, status: hookData?.status };
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const form = await getTemplate('form', hookData?.form || params?.form);
|
|
23
|
-
if (!form) { return { status: 404, message: 'not found' }; }
|
|
24
|
-
|
|
25
|
-
// replace settings
|
|
26
|
-
const arr = JSON.stringify(form).match(/{{settings.([^}]*)}}/g);
|
|
27
|
-
if (arr?.length) {
|
|
28
|
-
const string = JSON.stringify(form);
|
|
29
|
-
const settings = await getSettings({ pg });
|
|
30
|
-
const match = arr.reduce((acc, curr) => Object.assign(acc, { [curr]: settings[curr.replace(/^{{settings./g, '').replace(/}}$/, '')] }), {});
|
|
31
|
-
const res = Object.keys(match).reduce((s, m) => s.replace(m, match[m]), string);
|
|
32
|
-
return { time: Date.now() - time, form: JSON.parse(res) };
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const res = { time: Date.now() - time, form };
|
|
36
|
-
const res1 = await applyHook('afterForm', {
|
|
37
|
-
form: hookData?.form || params?.form,
|
|
38
|
-
payload: res,
|
|
39
|
-
user,
|
|
40
|
-
});
|
|
41
|
-
return res1 || { time: Date.now() - time, form };
|
|
42
|
-
}
|
|
1
|
+
import { applyHook, getTemplate } from '../../../../utils.js';
|
|
2
|
+
|
|
3
|
+
const sql = `select property_key as key, property_json as json, property_int as int,
|
|
4
|
+
property_text as text from admin.properties where 1=1`;
|
|
5
|
+
|
|
6
|
+
async function getSettings({ pg }) {
|
|
7
|
+
const { rows = [] } = await pg.query(sql);
|
|
8
|
+
const data = rows.reduce((acc, curr) => Object.assign(acc, { [curr.key]: curr.json || curr.int || curr.text }), {});
|
|
9
|
+
return data;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export default async function formFunction(req) {
|
|
13
|
+
const time = Date.now();
|
|
14
|
+
|
|
15
|
+
const { pg, params, user } = req;
|
|
16
|
+
const hookData = await applyHook('preForm', { form: params?.form, user });
|
|
17
|
+
|
|
18
|
+
if (hookData?.message && hookData?.status) {
|
|
19
|
+
return { message: hookData?.message, status: hookData?.status };
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const form = await getTemplate('form', hookData?.form || params?.form);
|
|
23
|
+
if (!form) { return { status: 404, message: 'not found' }; }
|
|
24
|
+
|
|
25
|
+
// replace settings
|
|
26
|
+
const arr = JSON.stringify(form).match(/{{settings.([^}]*)}}/g);
|
|
27
|
+
if (arr?.length) {
|
|
28
|
+
const string = JSON.stringify(form);
|
|
29
|
+
const settings = await getSettings({ pg });
|
|
30
|
+
const match = arr.reduce((acc, curr) => Object.assign(acc, { [curr]: settings[curr.replace(/^{{settings./g, '').replace(/}}$/, '')] }), {});
|
|
31
|
+
const res = Object.keys(match).reduce((s, m) => s.replace(m, match[m]), string);
|
|
32
|
+
return { time: Date.now() - time, form: JSON.parse(res) };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const res = { time: Date.now() - time, form };
|
|
36
|
+
const res1 = await applyHook('afterForm', {
|
|
37
|
+
form: hookData?.form || params?.form,
|
|
38
|
+
payload: res,
|
|
39
|
+
user,
|
|
40
|
+
});
|
|
41
|
+
return res1 || { time: Date.now() - time, form };
|
|
42
|
+
}
|
|
@@ -1,74 +1,74 @@
|
|
|
1
|
-
import {
|
|
2
|
-
getMeta, metaFormat, getTemplates, getTemplate, handlebars,
|
|
3
|
-
} from '../../../../utils.js';
|
|
4
|
-
|
|
5
|
-
function sequence(tables, data, fn) {
|
|
6
|
-
return tables.reduce((promise, table) => promise.then(() => fn({
|
|
7
|
-
...data, tableName: table.replace('.json', ''),
|
|
8
|
-
})), Promise.resolve());
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
async function getData({
|
|
12
|
-
pg, tableName, query = {}, maxLimit, res,
|
|
13
|
-
}) {
|
|
14
|
-
const loadTable = await getTemplate('table', tableName);
|
|
15
|
-
|
|
16
|
-
if (!loadTable) { return { message: 'not found', status: 404 }; }
|
|
17
|
-
|
|
18
|
-
const {
|
|
19
|
-
table, columns, meta,
|
|
20
|
-
} = loadTable;
|
|
21
|
-
|
|
22
|
-
const { pk } = await getMeta(table);
|
|
23
|
-
|
|
24
|
-
const cols = columns.map((el) => el.name || el).join(',');
|
|
25
|
-
const [orderColumn, orderDir] = (query.order || loadTable.order || '').split('-');
|
|
26
|
-
const order = cols.includes(orderColumn) && orderColumn?.length ? `order by ${orderColumn} ${query.desc || orderDir === 'desc' ? 'desc' : ''}` : '';
|
|
27
|
-
|
|
28
|
-
const limit = Math.max(maxLimit - res.rows.length, 0);
|
|
29
|
-
// Math.max(query.offset - res.rows.length,0)
|
|
30
|
-
const offset = query.page && query.page > 0 ? ` offset ${(query.page - 1) * limit}` : '';
|
|
31
|
-
|
|
32
|
-
const search1 = meta?.search && query.key ? `(${meta?.search.concat(meta?.title ? `,${meta?.title}` : '').split(',').map(el => `${el} ilike '%${query.key}%'`).join(' or ')})` : 'false';
|
|
33
|
-
|
|
34
|
-
const where = [!pk ? 'false' : 'true', loadTable.query, search1].filter((el) => el);
|
|
35
|
-
const q = `select ${[`"${pk}" as id`, meta?.title ? `${meta.title} as title` : ''].filter((el) => el).join(',')} from ${table} t where ${where.join(' and ') || 'true'} ${order} ${offset} limit ${limit}`;
|
|
36
|
-
if (query.sql) {
|
|
37
|
-
res.sql.push(q);
|
|
38
|
-
return null;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const { rows } = await pg.query(q);
|
|
42
|
-
|
|
43
|
-
const total = await pg.queryCache(`select count(*) from ${table} t where ${where.join(' and ') || 'true'}`).then((el) => el?.rows[0]?.count) || 0;
|
|
44
|
-
|
|
45
|
-
await metaFormat({ rows, table: tableName });
|
|
46
|
-
res.total += +total;
|
|
47
|
-
rows.forEach((row) => {
|
|
48
|
-
const href = meta?.href ? handlebars.compile(meta.href)({ ...row, [pk]: row.id }) : undefined;
|
|
49
|
-
res.rows.push({
|
|
50
|
-
...row, register: tableName, register_title: loadTable.ua, href,
|
|
51
|
-
});
|
|
52
|
-
});
|
|
53
|
-
return null;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export default async function search({
|
|
57
|
-
pg, funcs, query = {},
|
|
58
|
-
}) {
|
|
59
|
-
const time = Date.now();
|
|
60
|
-
|
|
61
|
-
const tables = query.table ? [query.table] : await getTemplates('table');
|
|
62
|
-
const res = { rows: [], sql: [], total: 0 };
|
|
63
|
-
|
|
64
|
-
const maxLimit = Math.min(100, query.limit || '16');
|
|
65
|
-
await sequence(tables, {
|
|
66
|
-
pg, funcs, query, maxLimit, res,
|
|
67
|
-
}, getData);
|
|
68
|
-
|
|
69
|
-
if (query.sql) return res.sql.join(';\n');
|
|
70
|
-
|
|
71
|
-
return {
|
|
72
|
-
time: Date.now() - time, total: res.total, count: res.rows.length, rows: res.rows,
|
|
73
|
-
};
|
|
74
|
-
}
|
|
1
|
+
import {
|
|
2
|
+
getMeta, metaFormat, getTemplates, getTemplate, handlebars,
|
|
3
|
+
} from '../../../../utils.js';
|
|
4
|
+
|
|
5
|
+
function sequence(tables, data, fn) {
|
|
6
|
+
return tables.reduce((promise, table) => promise.then(() => fn({
|
|
7
|
+
...data, tableName: table.replace('.json', ''),
|
|
8
|
+
})), Promise.resolve());
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
async function getData({
|
|
12
|
+
pg, tableName, query = {}, maxLimit, res,
|
|
13
|
+
}) {
|
|
14
|
+
const loadTable = await getTemplate('table', tableName);
|
|
15
|
+
|
|
16
|
+
if (!loadTable) { return { message: 'not found', status: 404 }; }
|
|
17
|
+
|
|
18
|
+
const {
|
|
19
|
+
table, columns, meta,
|
|
20
|
+
} = loadTable;
|
|
21
|
+
|
|
22
|
+
const { pk } = await getMeta(table);
|
|
23
|
+
|
|
24
|
+
const cols = columns.map((el) => el.name || el).join(',');
|
|
25
|
+
const [orderColumn, orderDir] = (query.order || loadTable.order || '').split('-');
|
|
26
|
+
const order = cols.includes(orderColumn) && orderColumn?.length ? `order by ${orderColumn} ${query.desc || orderDir === 'desc' ? 'desc' : ''}` : '';
|
|
27
|
+
|
|
28
|
+
const limit = Math.max(maxLimit - res.rows.length, 0);
|
|
29
|
+
// Math.max(query.offset - res.rows.length,0)
|
|
30
|
+
const offset = query.page && query.page > 0 ? ` offset ${(query.page - 1) * limit}` : '';
|
|
31
|
+
|
|
32
|
+
const search1 = meta?.search && query.key ? `(${meta?.search.concat(meta?.title ? `,${meta?.title}` : '').split(',').map(el => `${el} ilike '%${query.key}%'`).join(' or ')})` : 'false';
|
|
33
|
+
|
|
34
|
+
const where = [!pk ? 'false' : 'true', loadTable.query, search1].filter((el) => el);
|
|
35
|
+
const q = `select ${[`"${pk}" as id`, meta?.title ? `${meta.title} as title` : ''].filter((el) => el).join(',')} from ${table} t where ${where.join(' and ') || 'true'} ${order} ${offset} limit ${limit}`;
|
|
36
|
+
if (query.sql) {
|
|
37
|
+
res.sql.push(q);
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const { rows } = await pg.query(q);
|
|
42
|
+
|
|
43
|
+
const total = await pg.queryCache(`select count(*) from ${table} t where ${where.join(' and ') || 'true'}`).then((el) => el?.rows[0]?.count) || 0;
|
|
44
|
+
|
|
45
|
+
await metaFormat({ rows, table: tableName });
|
|
46
|
+
res.total += +total;
|
|
47
|
+
rows.forEach((row) => {
|
|
48
|
+
const href = meta?.href ? handlebars.compile(meta.href)({ ...row, [pk]: row.id }) : undefined;
|
|
49
|
+
res.rows.push({
|
|
50
|
+
...row, register: tableName, register_title: loadTable.ua, href,
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export default async function search({
|
|
57
|
+
pg, funcs, query = {},
|
|
58
|
+
}) {
|
|
59
|
+
const time = Date.now();
|
|
60
|
+
|
|
61
|
+
const tables = query.table ? [query.table] : await getTemplates('table');
|
|
62
|
+
const res = { rows: [], sql: [], total: 0 };
|
|
63
|
+
|
|
64
|
+
const maxLimit = Math.min(100, query.limit || '16');
|
|
65
|
+
await sequence(tables, {
|
|
66
|
+
pg, funcs, query, maxLimit, res,
|
|
67
|
+
}, getData);
|
|
68
|
+
|
|
69
|
+
if (query.sql) return res.sql.join(';\n');
|
|
70
|
+
|
|
71
|
+
return {
|
|
72
|
+
time: Date.now() - time, total: res.total, count: res.rows.length, rows: res.rows,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export default {};
|
|
1
|
+
export default {};
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
const formatMemoryUsage = (data) => `${Math.round((data / 1024 / 1024) * 100) / 100} MB`;
|
|
2
|
-
|
|
3
|
-
export default async function statusMonitor() {
|
|
4
|
-
const memoryUsage = process.memoryUsage();
|
|
5
|
-
const message = Object.keys(memoryUsage)
|
|
6
|
-
.reduce((acc, curr) => Object.assign(acc, { [curr]: formatMemoryUsage(memoryUsage[curr]) }), {});
|
|
7
|
-
return { message, status: 200 };
|
|
8
|
-
}
|
|
1
|
+
const formatMemoryUsage = (data) => `${Math.round((data / 1024 / 1024) * 100) / 100} MB`;
|
|
2
|
+
|
|
3
|
+
export default async function statusMonitor() {
|
|
4
|
+
const memoryUsage = process.memoryUsage();
|
|
5
|
+
const message = Object.keys(memoryUsage)
|
|
6
|
+
.reduce((acc, curr) => Object.assign(acc, { [curr]: formatMemoryUsage(memoryUsage[curr]) }), {});
|
|
7
|
+
return { message, status: 200 };
|
|
8
|
+
}
|
|
@@ -1,89 +1,89 @@
|
|
|
1
|
-
import config from '../../../../config.js';
|
|
2
|
-
import isFileExists from '../../../plugins/file/isFileExists.js';
|
|
3
|
-
import logChanges from '../../../plugins/crud/funcs/utils/logChanges.js';
|
|
4
|
-
import pgClients from '../../../plugins/pg/pgClients.js';
|
|
5
|
-
|
|
6
|
-
const isAdmin = (req) => process.env.NODE_ENV === 'admin'
|
|
7
|
-
|| config.admin
|
|
8
|
-
|| req?.hostname?.split?.(':')?.shift?.() === config.adminDomain
|
|
9
|
-
|| req?.hostname?.startsWith?.('admin');
|
|
10
|
-
|
|
11
|
-
async function checkAccess(pg, objectid, id) {
|
|
12
|
-
const { uid, filepath } = await pg.query('select uid, file_path as filepath from crm.files where entity_id=$1 and file_id=$2', [objectid, id])
|
|
13
|
-
.then(el => el.rows?.[0] || {});
|
|
14
|
-
return { uid, exists: filepath ? await isFileExists(filepath) : null };
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Дістає CRM дані для vue хешує ідентифікатори, підтягує селекти
|
|
19
|
-
*
|
|
20
|
-
* @method DELETE
|
|
21
|
-
* @summary CRM дані для обраного віджета.
|
|
22
|
-
* @priority 2
|
|
23
|
-
* @tag table
|
|
24
|
-
* @type api
|
|
25
|
-
* @requires setTokenById
|
|
26
|
-
* @requires getSelect
|
|
27
|
-
* @param {String} id Ідентифікатор для хешування
|
|
28
|
-
* @param {Any} sql Використовується для повернення sql запиту
|
|
29
|
-
* @param {String} type Тип для хешування даних
|
|
30
|
-
* @errors 400, 500
|
|
31
|
-
* @returns {Number} status Номер помилки
|
|
32
|
-
* @returns {String|Object} error Опис помилки
|
|
33
|
-
* @returns {String|Object} message Повідомлення про успішне виконання або об'єкт з параметрами
|
|
34
|
-
*/
|
|
35
|
-
|
|
36
|
-
export default async function widgetDel(req, reply) {
|
|
37
|
-
const {
|
|
38
|
-
pg = pgClients.client, params = {}, user = {},
|
|
39
|
-
} = req;
|
|
40
|
-
|
|
41
|
-
if (!user?.uid) {
|
|
42
|
-
return reply.status(401).send('access restricted: user not authorized');
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
const { type, objectid, id } = params;
|
|
46
|
-
|
|
47
|
-
if (!objectid) {
|
|
48
|
-
return reply.status(400).send('not enough params: id');
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// force delete db entry if file not exists
|
|
52
|
-
const { exists, uid } = ['file', 'gallery'].includes(type) ? await checkAccess(pg, objectid, id) : {};
|
|
53
|
-
|
|
54
|
-
if (exists && !isAdmin(req) && uid && user?.uid !== uid) {
|
|
55
|
-
return reply.status(403).send('access restricted: file exists, not an author');
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
const sqls = {
|
|
59
|
-
comment: `delete from crm.communications where entity_id=$1 and ${isAdmin(req) ? '$2=$2' : 'uid=$2'} and communication_id=$3`,
|
|
60
|
-
checklist: `delete from crm.checklists where entity_id=$1 and ${isAdmin(req) ? '$2=$2' : 'uid=$2'} and checklist_id=$3`,
|
|
61
|
-
file: `update crm.files set file_status=3 where entity_id=$1 and ${!exists || isAdmin(req) ? '$2=$2' : 'uid=$2'} and file_id=$3 returning uploaded_name`,
|
|
62
|
-
gallery: `update crm.files set file_status=3 where entity_id=$1 and ${!exists || isAdmin(req) ? '$2=$2' : 'uid=$2'} and file_id=$3 returning uploaded_name`,
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
const sql = sqls[type];
|
|
66
|
-
const table = {
|
|
67
|
-
comment: 'crm.communications',
|
|
68
|
-
checklist: 'crm.checklists',
|
|
69
|
-
file: 'crm.files',
|
|
70
|
-
gallery: 'crm.files',
|
|
71
|
-
}[type];
|
|
72
|
-
|
|
73
|
-
if (!sql) {
|
|
74
|
-
return reply.status(400).send('invalid widget type');
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
const { rows = [] } = await pg.query(sql, [objectid, user.uid, id]);
|
|
78
|
-
|
|
79
|
-
await logChanges({
|
|
80
|
-
pg,
|
|
81
|
-
table,
|
|
82
|
-
id,
|
|
83
|
-
data: rows[0],
|
|
84
|
-
uid: user?.uid,
|
|
85
|
-
type: 'DELETE',
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
return reply.status(200).send({ data: { id }, user: { uid: user.uid, name: user.user_name } });
|
|
89
|
-
}
|
|
1
|
+
import config from '../../../../config.js';
|
|
2
|
+
import isFileExists from '../../../plugins/file/isFileExists.js';
|
|
3
|
+
import logChanges from '../../../plugins/crud/funcs/utils/logChanges.js';
|
|
4
|
+
import pgClients from '../../../plugins/pg/pgClients.js';
|
|
5
|
+
|
|
6
|
+
const isAdmin = (req) => process.env.NODE_ENV === 'admin'
|
|
7
|
+
|| config.admin
|
|
8
|
+
|| req?.hostname?.split?.(':')?.shift?.() === config.adminDomain
|
|
9
|
+
|| req?.hostname?.startsWith?.('admin');
|
|
10
|
+
|
|
11
|
+
async function checkAccess(pg, objectid, id) {
|
|
12
|
+
const { uid, filepath } = await pg.query('select uid, file_path as filepath from crm.files where entity_id=$1 and file_id=$2', [objectid, id])
|
|
13
|
+
.then(el => el.rows?.[0] || {});
|
|
14
|
+
return { uid, exists: filepath ? await isFileExists(filepath) : null };
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Дістає CRM дані для vue хешує ідентифікатори, підтягує селекти
|
|
19
|
+
*
|
|
20
|
+
* @method DELETE
|
|
21
|
+
* @summary CRM дані для обраного віджета.
|
|
22
|
+
* @priority 2
|
|
23
|
+
* @tag table
|
|
24
|
+
* @type api
|
|
25
|
+
* @requires setTokenById
|
|
26
|
+
* @requires getSelect
|
|
27
|
+
* @param {String} id Ідентифікатор для хешування
|
|
28
|
+
* @param {Any} sql Використовується для повернення sql запиту
|
|
29
|
+
* @param {String} type Тип для хешування даних
|
|
30
|
+
* @errors 400, 500
|
|
31
|
+
* @returns {Number} status Номер помилки
|
|
32
|
+
* @returns {String|Object} error Опис помилки
|
|
33
|
+
* @returns {String|Object} message Повідомлення про успішне виконання або об'єкт з параметрами
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
export default async function widgetDel(req, reply) {
|
|
37
|
+
const {
|
|
38
|
+
pg = pgClients.client, params = {}, user = {},
|
|
39
|
+
} = req;
|
|
40
|
+
|
|
41
|
+
if (!user?.uid) {
|
|
42
|
+
return reply.status(401).send('access restricted: user not authorized');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const { type, objectid, id } = params;
|
|
46
|
+
|
|
47
|
+
if (!objectid) {
|
|
48
|
+
return reply.status(400).send('not enough params: id');
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// force delete db entry if file not exists
|
|
52
|
+
const { exists, uid } = ['file', 'gallery'].includes(type) ? await checkAccess(pg, objectid, id) : {};
|
|
53
|
+
|
|
54
|
+
if (exists && !isAdmin(req) && uid && user?.uid !== uid) {
|
|
55
|
+
return reply.status(403).send('access restricted: file exists, not an author');
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const sqls = {
|
|
59
|
+
comment: `delete from crm.communications where entity_id=$1 and ${isAdmin(req) ? '$2=$2' : 'uid=$2'} and communication_id=$3`,
|
|
60
|
+
checklist: `delete from crm.checklists where entity_id=$1 and ${isAdmin(req) ? '$2=$2' : 'uid=$2'} and checklist_id=$3`,
|
|
61
|
+
file: `update crm.files set file_status=3 where entity_id=$1 and ${!exists || isAdmin(req) ? '$2=$2' : 'uid=$2'} and file_id=$3 returning uploaded_name`,
|
|
62
|
+
gallery: `update crm.files set file_status=3 where entity_id=$1 and ${!exists || isAdmin(req) ? '$2=$2' : 'uid=$2'} and file_id=$3 returning uploaded_name`,
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const sql = sqls[type];
|
|
66
|
+
const table = {
|
|
67
|
+
comment: 'crm.communications',
|
|
68
|
+
checklist: 'crm.checklists',
|
|
69
|
+
file: 'crm.files',
|
|
70
|
+
gallery: 'crm.files',
|
|
71
|
+
}[type];
|
|
72
|
+
|
|
73
|
+
if (!sql) {
|
|
74
|
+
return reply.status(400).send('invalid widget type');
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const { rows = [] } = await pg.query(sql, [objectid, user.uid, id]);
|
|
78
|
+
|
|
79
|
+
await logChanges({
|
|
80
|
+
pg,
|
|
81
|
+
table,
|
|
82
|
+
id,
|
|
83
|
+
data: rows[0],
|
|
84
|
+
uid: user?.uid,
|
|
85
|
+
type: 'DELETE',
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
return reply.status(200).send({ data: { id }, user: { uid: user.uid, name: user.user_name } });
|
|
89
|
+
}
|
|
@@ -1,106 +1,106 @@
|
|
|
1
|
-
import path from 'node:path';
|
|
2
|
-
|
|
3
|
-
import getMeta from '../../../plugins/pg/funcs/getMeta.js';
|
|
4
|
-
import dataInsert from '../../../plugins/crud/funcs/dataInsert.js';
|
|
5
|
-
import dataUpdate from '../../../plugins/crud/funcs/dataUpdate.js';
|
|
6
|
-
import applyHook from '../../../plugins/hook/funcs/applyHook.js';
|
|
7
|
-
import uploadMultiPart from '../../../plugins/file/uploadMultiPart.js';
|
|
8
|
-
|
|
9
|
-
const tableList = {
|
|
10
|
-
comment: 'crm.communications',
|
|
11
|
-
gallery: 'crm.files',
|
|
12
|
-
file: 'crm.files',
|
|
13
|
-
checklist: 'crm.checklists',
|
|
14
|
-
};
|
|
15
|
-
const pkList = {
|
|
16
|
-
comment: 'communication_id',
|
|
17
|
-
checklist: 'checklist_id',
|
|
18
|
-
file: 'file_id',
|
|
19
|
-
gallery: 'file_id',
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
const galleryExtList = ['png', 'svg', 'jpg', 'jpeg', 'gif', 'mp4', 'mov', 'avi'];
|
|
23
|
-
|
|
24
|
-
export default async function widgetSet(req, reply) {
|
|
25
|
-
const {
|
|
26
|
-
pg, params = {}, session = {}, headers = {}, body = {}, user = {},
|
|
27
|
-
} = req;
|
|
28
|
-
const { type, id, objectid } = params;
|
|
29
|
-
|
|
30
|
-
if (!['comment', 'checklist', 'file', 'gallery'].includes(type)) {
|
|
31
|
-
return reply.status(400).send('param type not valid');
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
if (!objectid) {
|
|
35
|
-
return reply.status(400).send('not enough params: id');
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const table = tableList[type];
|
|
39
|
-
|
|
40
|
-
// dsadasdad
|
|
41
|
-
if (['gallery', 'file'].includes(type) && headers['content-type']?.split?.(';')?.shift?.() === 'multipart/form-data') {
|
|
42
|
-
const file = await uploadMultiPart(req);
|
|
43
|
-
const extName = path.extname(file.filepath).slice(1).toLowerCase();
|
|
44
|
-
|
|
45
|
-
const data = {
|
|
46
|
-
uploaded_name: file?.originalFilename?.toLocaleLowerCase()?.replace(/'/g, '\'\''),
|
|
47
|
-
file_path: file?.relativeFilepath?.replace(/\\/g, '/'),
|
|
48
|
-
ext: extName,
|
|
49
|
-
size: file?.size,
|
|
50
|
-
file_status: 1,
|
|
51
|
-
uid: user?.uid || 1,
|
|
52
|
-
entity_id: objectid,
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
if (type === 'gallery' && !galleryExtList.includes(extName.toLowerCase())) {
|
|
56
|
-
return reply.status(400).send('invalid file extension');
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const { rows = [] } = await dataInsert({
|
|
60
|
-
pg, table: 'crm.files', data, uid: user?.uid,
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
if (type === 'gallery') {
|
|
64
|
-
await pg.query(`update crm.files set ismain=true
|
|
65
|
-
where entity_id=$1
|
|
66
|
-
and file_id=$2
|
|
67
|
-
and (select count(*) = 0 from crm.files where entity_id=$1 and ismain)`, [objectid, rows[0]?.file_id]);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
return {
|
|
71
|
-
rowCount: 1, data: 'ok', command: 'UPLOAD', id: rows[0]?.file_id, entity_id: rows[0]?.entity_id,
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
|
-
const { pk } = await getMeta({ pg, table });
|
|
75
|
-
|
|
76
|
-
if (!pk) {
|
|
77
|
-
return reply.status(404).send('table not found');
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
const data = { ...body, uid: user?.uid, entity_id: objectid };
|
|
81
|
-
|
|
82
|
-
await applyHook('onWidgetSet', {
|
|
83
|
-
pg,
|
|
84
|
-
link: req.path,
|
|
85
|
-
id,
|
|
86
|
-
objectid,
|
|
87
|
-
session,
|
|
88
|
-
type,
|
|
89
|
-
payload: data,
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
const result = id
|
|
93
|
-
? await dataUpdate({
|
|
94
|
-
pg, table, data, id, uid: user?.uid,
|
|
95
|
-
})
|
|
96
|
-
: await dataInsert({
|
|
97
|
-
pg, table, data, uid: user?.uid,
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
return reply.status(200).send({
|
|
101
|
-
rowCount: result.rowCount,
|
|
102
|
-
data: 'ok',
|
|
103
|
-
command: result.command,
|
|
104
|
-
id: result.rows?.[0]?.[pkList[type]] || result?.[pkList[type]],
|
|
105
|
-
});
|
|
106
|
-
}
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
|
|
3
|
+
import getMeta from '../../../plugins/pg/funcs/getMeta.js';
|
|
4
|
+
import dataInsert from '../../../plugins/crud/funcs/dataInsert.js';
|
|
5
|
+
import dataUpdate from '../../../plugins/crud/funcs/dataUpdate.js';
|
|
6
|
+
import applyHook from '../../../plugins/hook/funcs/applyHook.js';
|
|
7
|
+
import uploadMultiPart from '../../../plugins/file/uploadMultiPart.js';
|
|
8
|
+
|
|
9
|
+
const tableList = {
|
|
10
|
+
comment: 'crm.communications',
|
|
11
|
+
gallery: 'crm.files',
|
|
12
|
+
file: 'crm.files',
|
|
13
|
+
checklist: 'crm.checklists',
|
|
14
|
+
};
|
|
15
|
+
const pkList = {
|
|
16
|
+
comment: 'communication_id',
|
|
17
|
+
checklist: 'checklist_id',
|
|
18
|
+
file: 'file_id',
|
|
19
|
+
gallery: 'file_id',
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const galleryExtList = ['png', 'svg', 'jpg', 'jpeg', 'gif', 'mp4', 'mov', 'avi'];
|
|
23
|
+
|
|
24
|
+
export default async function widgetSet(req, reply) {
|
|
25
|
+
const {
|
|
26
|
+
pg, params = {}, session = {}, headers = {}, body = {}, user = {},
|
|
27
|
+
} = req;
|
|
28
|
+
const { type, id, objectid } = params;
|
|
29
|
+
|
|
30
|
+
if (!['comment', 'checklist', 'file', 'gallery'].includes(type)) {
|
|
31
|
+
return reply.status(400).send('param type not valid');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (!objectid) {
|
|
35
|
+
return reply.status(400).send('not enough params: id');
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const table = tableList[type];
|
|
39
|
+
|
|
40
|
+
// dsadasdad
|
|
41
|
+
if (['gallery', 'file'].includes(type) && headers['content-type']?.split?.(';')?.shift?.() === 'multipart/form-data') {
|
|
42
|
+
const file = await uploadMultiPart(req);
|
|
43
|
+
const extName = path.extname(file.filepath).slice(1).toLowerCase();
|
|
44
|
+
|
|
45
|
+
const data = {
|
|
46
|
+
uploaded_name: file?.originalFilename?.toLocaleLowerCase()?.replace(/'/g, '\'\''),
|
|
47
|
+
file_path: file?.relativeFilepath?.replace(/\\/g, '/'),
|
|
48
|
+
ext: extName,
|
|
49
|
+
size: file?.size,
|
|
50
|
+
file_status: 1,
|
|
51
|
+
uid: user?.uid || 1,
|
|
52
|
+
entity_id: objectid,
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
if (type === 'gallery' && !galleryExtList.includes(extName.toLowerCase())) {
|
|
56
|
+
return reply.status(400).send('invalid file extension');
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const { rows = [] } = await dataInsert({
|
|
60
|
+
pg, table: 'crm.files', data, uid: user?.uid,
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
if (type === 'gallery') {
|
|
64
|
+
await pg.query(`update crm.files set ismain=true
|
|
65
|
+
where entity_id=$1
|
|
66
|
+
and file_id=$2
|
|
67
|
+
and (select count(*) = 0 from crm.files where entity_id=$1 and ismain)`, [objectid, rows[0]?.file_id]);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return {
|
|
71
|
+
rowCount: 1, data: 'ok', command: 'UPLOAD', id: rows[0]?.file_id, entity_id: rows[0]?.entity_id,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
const { pk } = await getMeta({ pg, table });
|
|
75
|
+
|
|
76
|
+
if (!pk) {
|
|
77
|
+
return reply.status(404).send('table not found');
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const data = { ...body, uid: user?.uid, entity_id: objectid };
|
|
81
|
+
|
|
82
|
+
await applyHook('onWidgetSet', {
|
|
83
|
+
pg,
|
|
84
|
+
link: req.path,
|
|
85
|
+
id,
|
|
86
|
+
objectid,
|
|
87
|
+
session,
|
|
88
|
+
type,
|
|
89
|
+
payload: data,
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
const result = id
|
|
93
|
+
? await dataUpdate({
|
|
94
|
+
pg, table, data, id, uid: user?.uid,
|
|
95
|
+
})
|
|
96
|
+
: await dataInsert({
|
|
97
|
+
pg, table, data, uid: user?.uid,
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
return reply.status(200).send({
|
|
101
|
+
rowCount: result.rowCount,
|
|
102
|
+
data: 'ok',
|
|
103
|
+
command: result.command,
|
|
104
|
+
id: result.rows?.[0]?.[pkList[type]] || result?.[pkList[type]],
|
|
105
|
+
});
|
|
106
|
+
}
|