@opengis/fastify-table 1.0.43 → 1.0.45
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/Changelog.md +8 -0
- package/crud/controllers/insert.js +42 -29
- package/crud/controllers/update.js +49 -31
- package/package.json +1 -1
- package/table/controllers/data.js +73 -60
package/Changelog.md
CHANGED
|
@@ -1,29 +1,42 @@
|
|
|
1
|
-
import dataInsert from '../funcs/dataInsert.js';
|
|
2
|
-
import getToken from '../funcs/getToken.js';
|
|
3
|
-
import checkXSS from './utils/checkXSS.js';
|
|
4
|
-
import getTemplate from '../../table/controllers/utils/getTemplate.js';
|
|
5
|
-
|
|
6
|
-
export default async function insert(req) {
|
|
7
|
-
const loadTemplate = await getTemplate('table', req.params.table);
|
|
8
|
-
const { table } = loadTemplate || req.params || {};
|
|
9
|
-
if (!table) return { status: 404, message: 'table is required' };
|
|
10
|
-
|
|
11
|
-
const { funcs, session, params } = req;
|
|
12
|
-
const tokenDataString = await getToken({
|
|
13
|
-
funcs, session, token: params.table, mode: 'a', json: 0,
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
const { form, add } = JSON.parse(tokenDataString || '{}');
|
|
17
|
-
|
|
18
|
-
const formData = form ? await getTemplate('form', form) : {};
|
|
19
|
-
|
|
20
|
-
const xssCheck = checkXSS({ body: req.body, schema: formData?.schema });
|
|
21
|
-
|
|
22
|
-
if (xssCheck.error && formData?.xssCheck !== false) {
|
|
23
|
-
req.log.warn({ name: 'injection/xss', msg: xssCheck.error, table }, req);
|
|
24
|
-
return { message: 'Дані містять заборонені символи. Приберіть їх та спробуйте ще раз', status: 409 };
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const res = await dataInsert({ table: add || table, data: req.body });
|
|
28
|
-
|
|
29
|
-
|
|
1
|
+
import dataInsert from '../funcs/dataInsert.js';
|
|
2
|
+
import getToken from '../funcs/getToken.js';
|
|
3
|
+
import checkXSS from './utils/checkXSS.js';
|
|
4
|
+
import getTemplate from '../../table/controllers/utils/getTemplate.js';
|
|
5
|
+
|
|
6
|
+
export default async function insert(req) {
|
|
7
|
+
const loadTemplate = await getTemplate('table', req.params.table);
|
|
8
|
+
const { table } = loadTemplate || req.params || {};
|
|
9
|
+
if (!table) return { status: 404, message: 'table is required' };
|
|
10
|
+
|
|
11
|
+
const { funcs, session, params } = req;
|
|
12
|
+
const tokenDataString = await getToken({
|
|
13
|
+
funcs, session, token: params.table, mode: 'a', json: 0,
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
const { form, add } = JSON.parse(tokenDataString || '{}');
|
|
17
|
+
|
|
18
|
+
const formData = form || loadTemplate?.form ? (await getTemplate('form', form || loadTemplate?.form) || {}) : {};
|
|
19
|
+
|
|
20
|
+
const xssCheck = checkXSS({ body: req.body, schema: formData?.schema });
|
|
21
|
+
|
|
22
|
+
if (xssCheck.error && formData?.xssCheck !== false) {
|
|
23
|
+
req.log.warn({ name: 'injection/xss', msg: xssCheck.error, table }, req);
|
|
24
|
+
return { message: 'Дані містять заборонені символи. Приберіть їх та спробуйте ще раз', status: 409 };
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const res = await dataInsert({ table: add || table, data: req.body });
|
|
28
|
+
|
|
29
|
+
const extraKeys = Object.keys(formData)?.filter((key) => formData?.[key]?.type === 'DataTable' && formData?.[key]?.table && formData?.[key]?.parent_id && req.body[key].length);
|
|
30
|
+
if (extraKeys?.length) {
|
|
31
|
+
res.extra = {};
|
|
32
|
+
await Promise.all(extraKeys?.map(async (key) => {
|
|
33
|
+
const extraRows = await Promise.all(req.body[key].map(async (row) => {
|
|
34
|
+
const extraRes = await dataInsert({ table: formData[key].table, data: { ...row, [formData[key].parent_id]: req.body[formData[key].parent_id] } });
|
|
35
|
+
return extraRes?.rows?.[0];
|
|
36
|
+
}));
|
|
37
|
+
Object.assign(res.extra, { [key]: extraRows.filter((el) => el) });
|
|
38
|
+
}));
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return { rows: res.rows, extra: res.extra };
|
|
42
|
+
}
|
|
@@ -1,31 +1,49 @@
|
|
|
1
|
-
import dataUpdate from '../funcs/dataUpdate.js';
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
}
|
|
1
|
+
import dataUpdate from '../funcs/dataUpdate.js';
|
|
2
|
+
import dataInsert from '../funcs/dataInsert.js';
|
|
3
|
+
import pgClients from '../../pg/pgClients.js';
|
|
4
|
+
import getToken from '../funcs/getToken.js';
|
|
5
|
+
import checkXSS from './utils/checkXSS.js';
|
|
6
|
+
import getTemplate from '../../table/controllers/utils/getTemplate.js';
|
|
7
|
+
|
|
8
|
+
export default async function update(req) {
|
|
9
|
+
const loadTemplate = await getTemplate('table', req.params.table);
|
|
10
|
+
const { table } = loadTemplate || req.params || {};
|
|
11
|
+
const { id } = req.params || {};
|
|
12
|
+
if (!req.params?.table) return { message: 'table is required', status: 404 };
|
|
13
|
+
if (!id) return { message: 'id is required', status: 404 };
|
|
14
|
+
|
|
15
|
+
const { funcs, session, params } = req;
|
|
16
|
+
const tokenDataString = await getToken({
|
|
17
|
+
funcs, session, token: params.table, mode: 'w', json: 0,
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
const tokenData = JSON.parse(tokenDataString || '{}');
|
|
21
|
+
|
|
22
|
+
const formData = tokenData?.form || loadTemplate?.form ? await getTemplate('form', tokenData.form || loadTemplate?.form) : {};
|
|
23
|
+
|
|
24
|
+
const xssCheck = checkXSS({ body: req.body, schema: formData?.schema });
|
|
25
|
+
|
|
26
|
+
if (xssCheck.error && formData?.xssCheck !== false) {
|
|
27
|
+
req.log.warn({ name: 'injection/xss', msg: xssCheck.error, table }, req);
|
|
28
|
+
return { message: 'Дані містять заборонені символи. Приберіть їх та спробуйте ще раз', status: 409 };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const res = await dataUpdate({ table: tokenData?.table || table, id: tokenData?.id || id, data: req.body });
|
|
32
|
+
|
|
33
|
+
const extraKeys = Object.keys(formData)?.filter((key) => formData?.[key]?.type === 'DataTable' && formData?.[key]?.table && formData?.[key]?.parent_id && req.body[key].length);
|
|
34
|
+
if (extraKeys?.length) {
|
|
35
|
+
res.extra = {};
|
|
36
|
+
await Promise.all(extraKeys?.map(async (key) => {
|
|
37
|
+
// delete old extra data
|
|
38
|
+
await pgClients.client.query(`delete from ${formData[key].table} where ${formData[key].parent_id}=$1`, [req.body[formData[key].parent_id]]);
|
|
39
|
+
// insert new extra data
|
|
40
|
+
const extraRows = await Promise.all(req.body[key].map(async (row) => {
|
|
41
|
+
const extraRes = await dataInsert({ table: formData[key].table, data: { ...row, [formData[key].parent_id]: req.body[formData[key].parent_id] } });
|
|
42
|
+
return extraRes?.rows?.[0];
|
|
43
|
+
}));
|
|
44
|
+
Object.assign(res.extra, { [key]: extraRows.filter((el) => el) });
|
|
45
|
+
}));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return res;
|
|
49
|
+
}
|
package/package.json
CHANGED
|
@@ -1,60 +1,73 @@
|
|
|
1
|
-
import getTemplate from './utils/getTemplate.js';
|
|
2
|
-
import getFilterSQL from '../funcs/getFilterSQL/index.js';
|
|
3
|
-
import getMeta from '../../pg/funcs/getMeta.js';
|
|
4
|
-
import metaFormat from '../funcs/metaFormat/index.js';
|
|
5
|
-
|
|
6
|
-
const maxLimit = 100;
|
|
7
|
-
export default async function data(req) {
|
|
8
|
-
const time = Date.now();
|
|
9
|
-
const {
|
|
10
|
-
pg, params, query = {},
|
|
11
|
-
} = req;
|
|
12
|
-
|
|
13
|
-
const loadTable = await getTemplate('table', params.table);
|
|
14
|
-
|
|
15
|
-
if (!loadTable) { return { status: 404, message: 'not found' }; }
|
|
16
|
-
|
|
17
|
-
const {
|
|
18
|
-
table, columns, sql, cardSql, filters, form, meta,
|
|
19
|
-
} = loadTable;
|
|
20
|
-
const { pk } = await getMeta(table);
|
|
21
|
-
|
|
22
|
-
const cols = columns.map((el) => el.name || el).join(',');
|
|
23
|
-
const
|
|
24
|
-
const
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
const
|
|
43
|
-
const
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
const
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
}
|
|
1
|
+
import getTemplate from './utils/getTemplate.js';
|
|
2
|
+
import getFilterSQL from '../funcs/getFilterSQL/index.js';
|
|
3
|
+
import getMeta from '../../pg/funcs/getMeta.js';
|
|
4
|
+
import metaFormat from '../funcs/metaFormat/index.js';
|
|
5
|
+
|
|
6
|
+
const maxLimit = 100;
|
|
7
|
+
export default async function data(req) {
|
|
8
|
+
const time = Date.now();
|
|
9
|
+
const {
|
|
10
|
+
pg, params, query = {},
|
|
11
|
+
} = req;
|
|
12
|
+
|
|
13
|
+
const loadTable = await getTemplate('table', params.table);
|
|
14
|
+
|
|
15
|
+
if (!loadTable) { return { status: 404, message: 'not found' }; }
|
|
16
|
+
|
|
17
|
+
const {
|
|
18
|
+
table, columns, sql, cardSql, filters, form, meta,
|
|
19
|
+
} = loadTable;
|
|
20
|
+
const { pk, columns: dbColumns } = await getMeta(table);
|
|
21
|
+
|
|
22
|
+
const cols = columns.map((el) => el.name || el).join(',');
|
|
23
|
+
const columnList = dbColumns.map((el) => el.name || el).join(',');
|
|
24
|
+
const sqlTable = sql?.filter?.((el) => !el?.disabled && el?.sql?.replace).map((el, i) => ` left join lateral (${el.sql}) ${el.name || `t${i}`} on 1=1 `)?.join('') || '';
|
|
25
|
+
const cardSqlFiltered = params.id ? cardSql?.filter?.((el) => !el?.disabled && el?.name && el?.sql?.replace) : [];
|
|
26
|
+
const cardSqlTable = cardSqlFiltered.length ? cardSqlFiltered.map((el, i) => ` left join lateral (select json_agg(row_to_json(q)) as ${el.name} from (${el.sql})q) ct${i} on 1=1 `).join('') || '' : '';
|
|
27
|
+
|
|
28
|
+
const fData = query.filter ? await getFilterSQL({
|
|
29
|
+
filter: query.filter,
|
|
30
|
+
table: params.table,
|
|
31
|
+
json: 1,
|
|
32
|
+
}) : {};
|
|
33
|
+
|
|
34
|
+
const keyQuery = query.key && loadTable.key && !params.id ? `${loadTable.key}=$1` : null;
|
|
35
|
+
|
|
36
|
+
const limit = Math.min(maxLimit, +(query.limit || 10));
|
|
37
|
+
|
|
38
|
+
const offset = query.page && query.page > 0 ? ` offset ${(query.page - 1) * limit}` : '';
|
|
39
|
+
// id, query, filter
|
|
40
|
+
const [orderColumn, orderDir] = (query.order || loadTable.order || '').split(/[- ]/);
|
|
41
|
+
|
|
42
|
+
const order = columnList.includes(orderColumn) && orderColumn?.length ? `order by ${orderColumn} ${query.desc || orderDir === 'desc' ? 'desc' : ''}` : '';
|
|
43
|
+
const state = loadTable.filterState && query.state ? loadTable.filterState[query.state]?.sql : null;
|
|
44
|
+
const custom = loadTable.filterCustom && query.custom ? loadTable.filterCustom[query.custom]?.sql : null;
|
|
45
|
+
const search = loadTable.meta?.search && query.search ? `(${loadTable.meta?.search.split(',').map(el => `${el} ilike '%${query.search}%'`).join(' or ')})` : null;
|
|
46
|
+
|
|
47
|
+
const where = [(params.id ? ` "${pk}" = $1` : null), keyQuery, loadTable.query, fData.q, state, custom, search].filter((el) => el);
|
|
48
|
+
const cardColumns = cardSqlFiltered.length ? `,${cardSqlFiltered.map((el) => el.name)}` : '';
|
|
49
|
+
const q = `select ${pk ? `"${pk}" as id,` : ''} ${query.id || query.key ? '*' : cols || '*'} ${cardColumns} from ${table} t ${sqlTable} ${cardSqlTable} where ${where.join(' and ') || 'true'} ${order} ${offset} limit ${limit}`;
|
|
50
|
+
|
|
51
|
+
if (query.sql === '1') { return q; }
|
|
52
|
+
|
|
53
|
+
const { rows } = await pg.query(q, (params.id ? [params.id] : null) || (query.key && loadTable.key ? [query.key] : []));
|
|
54
|
+
|
|
55
|
+
const formData = form ? (await getTemplate('form', form) || {}) : {};
|
|
56
|
+
const extraKeys = Object.keys(formData)?.filter((key) => formData?.[key]?.type === 'DataTable' && formData?.[key]?.table && formData?.[key]?.parent_id && formData[key]?.colModel);
|
|
57
|
+
if (extraKeys?.length) {
|
|
58
|
+
await Promise.all(rows?.map(async (row) => {
|
|
59
|
+
await Promise.all(extraKeys?.map(async (key) => {
|
|
60
|
+
const { colModel, table: extraTable, parent_id: parentId } = formData[key];
|
|
61
|
+
const { rows: extraRows } = await pg.query(`select ${parentId} as parent, ${colModel.map((col) => col.name).join(',')} from ${extraTable} a where ${parentId}=$1`, [row.id]);
|
|
62
|
+
Object.assign(row, { [key]: extraRows });
|
|
63
|
+
}));
|
|
64
|
+
}));
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const total = keyQuery || params.id ? rows.length : await pg.queryCache(`select count(*) from ${table} t where ${where.join(' and ') || 'true'}`).then((el) => el?.rows[0]?.count);
|
|
68
|
+
|
|
69
|
+
await metaFormat({ rows, table: params.table });
|
|
70
|
+
return {
|
|
71
|
+
time: Date.now() - time, total, count: rows.length, pk, form, rows, meta, columns, filters,
|
|
72
|
+
};
|
|
73
|
+
}
|