@opengis/fastify-table 1.0.62 → 1.0.63

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 CHANGED
@@ -1,5 +1,9 @@
1
1
  # fastify-table
2
2
 
3
+ ## 1.0.63 - 23.07.2024
4
+
5
+ - add rest API token support
6
+
3
7
  ## 1.0.62 - 22.07.2024
4
8
 
5
9
  - add email notification func and test api
@@ -1,42 +1,48 @@
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
+ 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
+ if (!req.params?.table) {
8
+ return { message: 'table is required', status: 400 };
9
+ }
10
+ const loadTemplate = await getTemplate('table', req.params.table);
11
+ const { table, public: ispublic } = loadTemplate || req.params || {};
12
+ if (!table) {
13
+ return { message: 'table not found', status: 404 };
14
+ }
15
+
16
+ const { funcs = {}, user = {}, params = {} } = req;
17
+ const uid = funcs.config?.auth?.disable || ispublic ? '1' : user.uid;
18
+ const tokenDataString = await getToken({
19
+ funcs, uid, token: params.table, mode: 'a', json: 0,
20
+ });
21
+
22
+ const { form, add } = JSON.parse(tokenDataString || '{}');
23
+
24
+ const formData = form || loadTemplate?.form ? (await getTemplate('form', form || loadTemplate?.form) || {}) : {};
25
+
26
+ const xssCheck = checkXSS({ body: req.body, schema: formData?.schema });
27
+
28
+ if (xssCheck.error && formData?.xssCheck !== false) {
29
+ req.log.warn({ name: 'injection/xss', msg: xssCheck.error, table }, req);
30
+ return { message: 'Дані містять заборонені символи. Приберіть їх та спробуйте ще раз', status: 409 };
31
+ }
32
+
33
+ const res = await dataInsert({ table: add || table, data: req.body });
34
+
35
+ const extraKeys = Object.keys(formData)?.filter((key) => formData?.[key]?.type === 'DataTable' && formData?.[key]?.table && formData?.[key]?.parent_id && req.body[key].length);
36
+ if (extraKeys?.length) {
37
+ res.extra = {};
38
+ await Promise.all(extraKeys?.map(async (key) => {
39
+ const extraRows = await Promise.all(req.body[key].map(async (row) => {
40
+ const extraRes = await dataInsert({ table: formData[key].table, data: { ...row, [formData[key].parent_id]: req.body[formData[key].parent_id] } });
41
+ return extraRes?.rows?.[0];
42
+ }));
43
+ Object.assign(res.extra, { [key]: extraRows.filter((el) => el) });
44
+ }));
45
+ }
46
+
47
+ return { rows: res.rows, extra: res.extra };
48
+ }
@@ -1,49 +1,54 @@
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
- }
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
+ if (!req.params?.table) {
10
+ return { message: 'table is required', status: 400 };
11
+ }
12
+ if (!req.params?.id) {
13
+ return { message: 'id is required', status: 404 };
14
+ }
15
+ const loadTemplate = await getTemplate('table', req.params.table);
16
+ const { table, public: ispublic } = loadTemplate || req.params || {};
17
+ const { id } = req.params || {};
18
+
19
+ const { funcs = {}, user = {}, params = {} } = req;
20
+ const uid = funcs.config?.auth?.disable || ispublic ? '1' : user.uid;
21
+ const tokenDataString = await getToken({
22
+ funcs, uid, token: params.table, mode: 'w', json: 0,
23
+ });
24
+
25
+ const tokenData = JSON.parse(tokenDataString || '{}');
26
+
27
+ const formData = tokenData?.form || loadTemplate?.form ? await getTemplate('form', tokenData.form || loadTemplate?.form) : {};
28
+
29
+ const xssCheck = checkXSS({ body: req.body, schema: formData?.schema });
30
+
31
+ if (xssCheck.error && formData?.xssCheck !== false) {
32
+ req.log.warn({ name: 'injection/xss', msg: xssCheck.error, table }, req);
33
+ return { message: 'Дані містять заборонені символи. Приберіть їх та спробуйте ще раз', status: 409 };
34
+ }
35
+
36
+ const res = await dataUpdate({ table: tokenData?.table || table, id: tokenData?.id || id, data: req.body });
37
+
38
+ const extraKeys = Object.keys(formData)?.filter((key) => formData?.[key]?.type === 'DataTable' && formData?.[key]?.table && formData?.[key]?.parent_id && req.body[key].length);
39
+ if (extraKeys?.length) {
40
+ res.extra = {};
41
+ await Promise.all(extraKeys?.map(async (key) => {
42
+ // delete old extra data
43
+ await pgClients.client.query(`delete from ${formData[key].table} where ${formData[key].parent_id}=$1`, [req.body[formData[key].parent_id]]);
44
+ // insert new extra data
45
+ const extraRows = await Promise.all(req.body[key].map(async (row) => {
46
+ const extraRes = await dataInsert({ table: formData[key].table, data: { ...row, [formData[key].parent_id]: req.body[formData[key].parent_id] } });
47
+ return extraRes?.rows?.[0];
48
+ }));
49
+ Object.assign(res.extra, { [key]: extraRows.filter((el) => el) });
50
+ }));
51
+ }
52
+
53
+ return res;
54
+ }
@@ -1,15 +1,15 @@
1
- import getPG from '../../pg/funcs/getPG.js';
2
-
3
- import getMeta from '../../pg/funcs/getMeta.js';
4
-
5
- export default async function dataDelete({
6
- table, id, pg: pg1
7
- }) {
8
- const pg = pg1 || getPG({ name: 'client' });
9
- const { pk } = await getMeta(table);
10
- if (!pg.tlist?.includes(table)) return 'table not exist';
11
- const delQuery = `delete from ${table} WHERE ${pk} = $1 returning *`;
12
- // console.log(updateDataset);
13
- const res = await pg.one(delQuery, [id]) || {};
14
- return res;
15
- }
1
+ import getPG from '../../pg/funcs/getPG.js';
2
+
3
+ import getMeta from '../../pg/funcs/getMeta.js';
4
+
5
+ export default async function dataDelete({
6
+ table, id, pg: pg1,
7
+ }) {
8
+ const pg = pg1 || getPG({ name: 'client' });
9
+ const { pk } = await getMeta(table);
10
+ if (!pg.tlist?.includes(table)) return 'table not exist';
11
+ const delQuery = `delete from ${table} WHERE ${pk} = $1 returning *`;
12
+ // console.log(updateDataset);
13
+ const res = await pg.one(delQuery, [id]) || {};
14
+ return res;
15
+ }
@@ -1,24 +1,24 @@
1
- import getPG from '../../pg/funcs/getPG.js';
2
- import getMeta from '../../pg/funcs/getMeta.js';
3
-
4
- export default async function dataInsert({ table, data, pg: pg1 }) {
5
- const pg = pg1 || getPG({ name: 'client' });
6
- if (!data) return null;
7
- const { columns } = await getMeta(table);
8
- if (!columns) return null;
9
-
10
- const names = columns.map((el) => el.name);
11
- const filterData = Object.keys(data)
12
- .filter((el) => data[el] && names.includes(el)).map((el) => [el, data[el]]);
13
-
14
- const insertQuery = `insert into ${table}
15
-
16
- ( ${filterData?.map((key) => `"${key[0]}"`).join(',')})
17
-
18
- values (${filterData?.map((key, i) => key[0] === 'geom' ? `st_setsrid(st_geomfromgeojson($${i + 1}::json),4326)` : `$${i + 1}`).join(',')})
19
-
20
- returning *`;
21
-
22
- const res = await pg.query(insertQuery, [...filterData.map((el) => (typeof el[1] === 'object' && (!Array.isArray(el[1]) || typeof el[1]?.[0] === 'object') ? JSON.stringify(el[1]) : el[1]))]) || {};
23
- return res;
24
- }
1
+ import getPG from '../../pg/funcs/getPG.js';
2
+ import getMeta from '../../pg/funcs/getMeta.js';
3
+
4
+ export default async function dataInsert({ table, data, pg: pg1 }) {
5
+ const pg = pg1 || getPG({ name: 'client' });
6
+ if (!data) return null;
7
+ const { columns } = await getMeta(table);
8
+ if (!columns) return null;
9
+
10
+ const names = columns.map((el) => el.name);
11
+ const filterData = Object.keys(data)
12
+ .filter((el) => data[el] && names.includes(el)).map((el) => [el, data[el]]);
13
+
14
+ const insertQuery = `insert into ${table}
15
+
16
+ ( ${filterData?.map((key) => `"${key[0]}"`).join(',')})
17
+
18
+ values (${filterData?.map((key, i) => (key[0] === 'geom' ? `st_setsrid(st_geomfromgeojson($${i + 1}::json),4326)` : `$${i + 1}`)).join(',')})
19
+
20
+ returning *`;
21
+
22
+ const res = await pg.query(insertQuery, [...filterData.map((el) => (typeof el[1] === 'object' && (!Array.isArray(el[1]) || typeof el[1]?.[0] === 'object') ? JSON.stringify(el[1]) : el[1]))]) || {};
23
+ return res;
24
+ }
@@ -1,26 +1,24 @@
1
- import getPG from '../../pg/funcs/getPG.js';
2
-
3
- import getMeta from '../../pg/funcs/getMeta.js';
4
-
5
- export default async function dataUpdate({
6
- table, id, data, pg: pg1
7
- }) {
8
- if (!data || !table || !id) return null;
9
-
10
- const pg = pg1 || getPG({ name: 'client' });
11
- const { columns, pk } = await getMeta(table);
12
-
13
- const names = columns?.map((el) => el.name);
14
- const filterData = Object.keys(data)
15
- .filter((el) => {
16
- return typeof data[el] === 'boolean' ? true : data[el] && names?.includes(el);
17
- });
18
-
19
- const filterValue = filterData.map((el) => [el, data[el]]).map((el) => (typeof el[1] === 'object' && (!Array.isArray(el[1]) || typeof el[1]?.[0] === 'object') ? JSON.stringify(el[1]) : el[1]));
20
-
21
- const updateQuery = `UPDATE ${table} SET ${filterData?.map((key, i) => key === 'geom' ? `"${key}"=st_setsrid(st_geomfromgeojson($${i + 2}::json),4326)` : `"${key}"=$${i + 2}`).join(',')}
22
- WHERE ${pk} = $1 returning *`;
23
- // console.log(updateDataset);
24
- const res = await pg.query(updateQuery, [id, ...filterValue]).then(el => el?.rows?.[0]) || {};
25
- return res;
26
- }
1
+ import getPG from '../../pg/funcs/getPG.js';
2
+
3
+ import getMeta from '../../pg/funcs/getMeta.js';
4
+
5
+ export default async function dataUpdate({
6
+ table, id, data, pg: pg1,
7
+ }) {
8
+ if (!data || !table || !id) return null;
9
+
10
+ const pg = pg1 || getPG({ name: 'client' });
11
+ const { columns, pk } = await getMeta(table);
12
+
13
+ const names = columns?.map((el) => el.name);
14
+ const filterData = Object.keys(data)
15
+ .filter((el) => (typeof data[el] === 'boolean' ? true : data[el] && names?.includes(el)));
16
+
17
+ const filterValue = filterData.map((el) => [el, data[el]]).map((el) => (typeof el[1] === 'object' && (!Array.isArray(el[1]) || typeof el[1]?.[0] === 'object') ? JSON.stringify(el[1]) : el[1]));
18
+
19
+ const updateQuery = `UPDATE ${table} SET ${filterData?.map((key, i) => (key === 'geom' ? `"${key}"=st_setsrid(st_geomfromgeojson($${i + 2}::json),4326)` : `"${key}"=$${i + 2}`)).join(',')}
20
+ WHERE ${pk} = $1 returning *`;
21
+ // console.log(updateDataset);
22
+ const res = await pg.query(updateQuery, [id, ...filterValue]).then(el => el?.rows?.[0]) || {};
23
+ return res;
24
+ }
@@ -1,27 +1,27 @@
1
- import getRedis from '../../redis/funcs/getRedis.js';
2
- import config from '../../config.js';
3
-
4
- function sprintf(str, ...args) {
5
- return str.replace(/%s/g, () => args.shift());
6
- }
7
-
8
- const keys = {
9
- r: '%s:token:view:%s',
10
- a: '%s:token:add:%s',
11
- w: '%s:token:edit:%s',
12
- e: '%s:token:exec:%s',
13
- };
14
-
15
- async function getIdByToken({
16
- uid, token, mode = 'r', json,
17
- }) {
18
- if (mode === 'r') return token;
19
-
20
- const rclient = getRedis({ db: 0 });
21
-
22
- const key = sprintf(keys[mode], config?.pg?.database, uid?.toString());
23
- const id = await rclient.hget(key, token);
24
- return json && id[0] === '{' ? JSON.parse(id) : id;
25
- }
26
-
27
- export default getIdByToken;
1
+ import getRedis from '../../redis/funcs/getRedis.js';
2
+ import config from '../../config.js';
3
+
4
+ function sprintf(str, ...args) {
5
+ return str.replace(/%s/g, () => args.shift());
6
+ }
7
+
8
+ const keys = {
9
+ r: '%s:token:view:%s',
10
+ a: '%s:token:add:%s',
11
+ w: '%s:token:edit:%s',
12
+ e: '%s:token:exec:%s',
13
+ };
14
+
15
+ async function getToken({
16
+ uid, token, mode = 'r', json,
17
+ }) {
18
+ if (mode === 'r') return token;
19
+
20
+ const rclient = getRedis({ db: 0 });
21
+
22
+ const key = sprintf(keys[mode], config?.pg?.database, uid?.toString());
23
+ const id = await rclient.hget(key, token);
24
+ return json && id?.[0] === '{' ? JSON.parse(id) : id;
25
+ }
26
+
27
+ export default getToken;
package/crud/index.js CHANGED
@@ -1,27 +1,27 @@
1
- import getOPt from './funcs/getOpt.js';
2
- import setOpt from './funcs/setOpt.js';
3
- import isFileExists from './funcs/isFileExists.js';
4
- import dataUpdate from './funcs/dataUpdate.js';
5
- import dataInsert from './funcs/dataInsert.js';
6
-
7
- import update from './controllers/update.js';
8
- import insert from './controllers/insert.js';
9
- import deleteCrud from './controllers/deleteCrud.js';
10
-
11
- async function plugin(fastify, config = {}) {
12
- const prefix = config.prefix || '/api';
13
- // funcs
14
- fastify.decorate('setOpt', setOpt);
15
- fastify.decorate('getOpt', getOPt);
16
- fastify.decorate('dataUpdate', dataUpdate);
17
- fastify.decorate('dataInsert', dataInsert);
18
-
19
- fastify.decorate('isFileExists', isFileExists);
20
-
21
- // api
22
- fastify.put(`${prefix}/table/:table/:id`, {}, update);
23
- fastify.delete(`${prefix}/table/:table/:id`, {}, deleteCrud);
24
- fastify.post(`${prefix}/table/:table`, {}, insert);
25
- }
26
-
27
- export default plugin;
1
+ import getOpt from './funcs/getOpt.js';
2
+ import setOpt from './funcs/setOpt.js';
3
+ import isFileExists from './funcs/isFileExists.js';
4
+ import dataUpdate from './funcs/dataUpdate.js';
5
+ import dataInsert from './funcs/dataInsert.js';
6
+
7
+ import update from './controllers/update.js';
8
+ import insert from './controllers/insert.js';
9
+ import deleteCrud from './controllers/deleteCrud.js';
10
+
11
+ async function plugin(fastify, config = {}) {
12
+ const prefix = config.prefix || '/api';
13
+ // funcs
14
+ fastify.decorate('setOpt', setOpt);
15
+ fastify.decorate('getOpt', getOpt);
16
+ fastify.decorate('dataUpdate', dataUpdate);
17
+ fastify.decorate('dataInsert', dataInsert);
18
+
19
+ fastify.decorate('isFileExists', isFileExists);
20
+
21
+ // api
22
+ fastify.put(`${prefix}/table/:table/:id`, {}, update);
23
+ fastify.delete(`${prefix}/table/:table/:id`, {}, deleteCrud);
24
+ fastify.post(`${prefix}/table/:table`, {}, insert);
25
+ }
26
+
27
+ export default plugin;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opengis/fastify-table",
3
- "version": "1.0.62",
3
+ "version": "1.0.63",
4
4
  "type": "module",
5
5
  "description": "core-plugins",
6
6
  "main": "index.js",
@@ -1,41 +1,44 @@
1
- import getTemplate from './utils/getTemplate.js';
2
- import getMeta from '../../pg/funcs/getMeta.js';
3
- import metaFormat from '../funcs/metaFormat/index.js';
4
-
5
- export default async function card(req) {
6
- const time = Date.now();
7
- const {
8
- pg, params, query = {},
9
- } = req;
10
-
11
- const loadTable = await getTemplate('table', params.table);
12
-
13
- if (!loadTable) { return { status: 404, message: 'not found' }; }
14
-
15
- const { table, columns, meta, sql, cardSql } = loadTable;
16
- const { pk, columns: dbColumns = [] } = await getMeta(table);
17
-
18
- if (!pk) return { message: `table not found: ${table}`, status: 404 };
19
-
20
- const cols = columns.map((el) => el.name || el).join(',');
21
- const columnList = dbColumns.map((el) => el.name || el).join(',');
22
- 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('') || '';
23
- const cardSqlFiltered = params.id ? cardSql?.filter?.((el) => !el?.disabled && el?.name && el?.sql?.replace) : [];
24
- 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('') || '' : '';
25
-
26
- const where = [`"${pk}" = $1`, loadTable.query].filter((el) => el);
27
- const cardColumns = cardSqlFiltered.length ? `,${cardSqlFiltered.map((el) => el.name)}` : '';
28
- const q = `select ${pk ? `"${pk}" as id,` : ''} ${columnList.includes('geom') ? `st_asgeojson(geom)::json as geom,` : ''} ${cols || '*'} ${cardColumns} from ${table} t ${sqlTable} ${cardSqlTable}
29
- where ${where.join(' and ') || 'true'} limit 1`;
30
-
31
- if (query.sql === '1') { return q; }
32
-
33
- const { rows } = await pg.query(q, [params.id]);
34
-
35
- await metaFormat({ rows, table: params.table });
36
-
37
- const data = meta.card?.length ? meta.card.reduce((acc, curr) => Object.assign(acc, { [columns.find((col) => col.name === curr)?.ua || '']: rows[0][curr] }), {}) : {};
38
- return {
39
- time: Date.now() - time, data,
40
- };
41
- }
1
+ import getTemplate from './utils/getTemplate.js';
2
+ import getMeta from '../../pg/funcs/getMeta.js';
3
+ import metaFormat from '../funcs/metaFormat/index.js';
4
+
5
+ export default async function card(req) {
6
+ const time = Date.now();
7
+ const {
8
+ pg, params = {}, query = {}, opt = {},
9
+ } = req;
10
+
11
+ const loadTable = await getTemplate('table', params.table);
12
+
13
+ if (!loadTable) { return { message: 'template not found', status: 404 }; }
14
+
15
+ const {
16
+ table, columns, meta, sql, cardSql,
17
+ } = loadTable;
18
+
19
+ const { pk, columns: dbColumns = [] } = await getMeta(table);
20
+
21
+ if (!pk) return { message: `table not found: ${table}`, status: 404 };
22
+
23
+ const cols = columns.map((el) => el.name || el).join(',');
24
+ const columnList = dbColumns.map((el) => el.name || el).join(',');
25
+ 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('') || '';
26
+ const cardSqlFiltered = opt.id || params.id ? cardSql?.filter?.((el) => !el?.disabled && el?.name && el?.sql?.replace) : [];
27
+ 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('') || '' : '';
28
+
29
+ const where = [`"${pk}" = $1`, loadTable.query].filter((el) => el);
30
+ const cardColumns = cardSqlFiltered.length ? `,${cardSqlFiltered.map((el) => el.name)}` : '';
31
+ const q = `select ${pk ? `"${pk}" as id,` : ''} ${columnList.includes('geom') ? 'st_asgeojson(geom)::json as geom,' : ''} ${cols || '*'} ${cardColumns} from ${table} t ${sqlTable} ${cardSqlTable}
32
+ where ${where.join(' and ') || 'true'} limit 1`;
33
+
34
+ if (query.sql === '1') { return q; }
35
+
36
+ const { rows } = await pg.query(q, [opt.id || params.id]);
37
+
38
+ await metaFormat({ rows, table: params.table });
39
+
40
+ const data = meta.card?.length ? meta.card.reduce((acc, curr) => Object.assign(acc, { [columns.find((col) => col.name === curr)?.ua || '']: rows[0][curr] }), {}) : {};
41
+ return {
42
+ time: Date.now() - time, data,
43
+ };
44
+ }
@@ -2,17 +2,18 @@ import getTemplate from './utils/getTemplate.js';
2
2
  import getFilterSQL from '../funcs/getFilterSQL/index.js';
3
3
  import getMeta from '../../pg/funcs/getMeta.js';
4
4
  import metaFormat from '../funcs/metaFormat/index.js';
5
+ import setToken from '../../crud/funcs/setToken.js';
5
6
 
6
7
  const maxLimit = 100;
7
- export default async function data(req) {
8
+ export default async function dataAPI(req) {
8
9
  const time = Date.now();
9
10
  const {
10
- pg, params, query = {},
11
+ pg, params, funcs = {}, query = {}, opt = {}, uid,
11
12
  } = req;
12
13
 
13
14
  const loadTable = await getTemplate('table', params.table);
14
15
 
15
- if (!loadTable) { return { status: 404, message: 'not found' }; }
16
+ if (!loadTable) { return { message: 'template not found', status: 404 }; }
16
17
 
17
18
  const {
18
19
  table, columns, sql, cardSql, filters, form, meta,
@@ -24,7 +25,7 @@ export default async function data(req) {
24
25
  const cols = columns.filter((el) => el.name !== 'geom').map((el) => el.name || el).join(',');
25
26
  const columnList = dbColumns.map((el) => el.name || el).join(',');
26
27
  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('') || '';
27
- const cardSqlFiltered = params.id ? (cardSql?.filter?.((el) => !el?.disabled && el?.name && el?.sql?.replace) || []) : [];
28
+ const cardSqlFiltered = opt?.id || params.id ? (cardSql?.filter?.((el) => !el?.disabled && el?.name && el?.sql?.replace) || []) : [];
28
29
  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('') || '' : '';
29
30
 
30
31
  const fData = query.filter ? await getFilterSQL({
@@ -33,7 +34,7 @@ export default async function data(req) {
33
34
  json: 1,
34
35
  }) : {};
35
36
 
36
- const keyQuery = query.key && loadTable.key && !params.id ? `${loadTable.key}=$1` : null;
37
+ const keyQuery = query.key && loadTable.key && !(opt?.id || params.id) ? `${loadTable.key}=$1` : null;
37
38
 
38
39
  const limit = Math.min(maxLimit, +(query.limit || 20));
39
40
 
@@ -46,18 +47,41 @@ export default async function data(req) {
46
47
  const custom = loadTable.filterCustom && query.custom ? loadTable.filterCustom[query.custom]?.sql : null;
47
48
  const search = loadTable.meta?.search && query.search ? `(${loadTable.meta?.search.split(',').map(el => `${el} ilike '%${query.search}%'`).join(' or ')})` : null;
48
49
 
49
- const where = [(params.id ? ` "${pk}" = $1` : null), keyQuery, loadTable.query, fData.q, state, custom, search].filter((el) => el);
50
+ const where = [(opt?.id || params.id ? ` "${pk}" = $1` : null), keyQuery, loadTable.query, fData.q, state, custom, search].filter((el) => el);
50
51
  const cardColumns = cardSqlFiltered.length ? `,${cardSqlFiltered.map((el) => el.name)}` : '';
51
52
  const q = `select ${pk ? `"${pk}" as id,` : ''} ${columnList.includes('geom') ? 'st_asgeojson(geom)::json as geom,' : ''} ${query.id || query.key ? '*' : cols || '*'} ${cardColumns} from ${table} t ${sqlTable} ${cardSqlTable} where ${where.join(' and ') || 'true'} ${order} ${offset} limit ${limit}`;
52
53
 
53
54
  if (query.sql === '1') { return q; }
54
55
 
55
- const { rows } = await pg.query(q, (params.id ? [params.id] : null) || (query.key && loadTable.key ? [query.key] : []));
56
+ const { rows } = await pg.query(q, (opt?.id || params.id ? [opt?.id || params.id] : null) || (query.key && loadTable.key ? [query.key] : []));
56
57
 
57
- 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);
58
+ const total = keyQuery || opt?.id || params.id ? rows.length : await pg.queryCache(`select count(*) from ${table} t where ${where.join(' and ') || 'true'}`).then((el) => el?.rows[0]?.count);
58
59
 
59
60
  await metaFormat({ rows, table: params.table });
60
- return {
61
+
62
+ const res = {
61
63
  time: Date.now() - time, card: loadTable.card, actions: loadTable.actions, total, count: rows.length, pk, form, rows, meta, columns, filters,
62
64
  };
65
+
66
+ if (!funcs.config?.security?.disableToken) {
67
+ const addTokens = setToken({
68
+ ids: [JSON.stringify({ add: loadTable.table, form: loadTable.form })],
69
+ mode: 'a',
70
+ uid,
71
+ array: 1,
72
+ });
73
+ Object.assign(res, { addToken: addTokens[0] });
74
+
75
+ rows.forEach((row) => {
76
+ const editTokens = setToken({
77
+ ids: [JSON.stringify({ id: row.id, table: loadTable.table, form: loadTable.form })],
78
+ mode: 'w',
79
+ uid,
80
+ array: 1,
81
+ });
82
+ Object.assign(row, { token: editTokens[0] });
83
+ });
84
+ }
85
+
86
+ return res;
63
87
  }
@@ -1,40 +1,44 @@
1
- import getTemplate from './utils/getTemplate.js';
2
- import getMeta from '../../pg/funcs/getMeta.js';
3
-
4
- export default async function table(req) {
5
- const { pg, params = {}, query = {} } = req;
6
- if (!params.id) return { message: 'not enough params', status: 400 };
7
-
8
- const loadTable = await getTemplate('table', params.table);
9
- if (!loadTable) { return { message: 'not found', status: 404 }; }
10
-
11
- const { table, columns, form } = loadTable;
12
-
13
- const { pk, columns: dbColumns = [] } = await getMeta(table);
14
- if (!pk) return { message: `table not found: ${table}`, status: 404 };
15
-
16
- const cols = columns.map((el) => el.name || el).join(',');
17
- const columnList = dbColumns.map((el) => el.name || el).join(',');
18
-
19
- const where = [`"${pk}" = $1`, loadTable.query].filter((el) => el);
20
- const geom = columnList.includes('geom') ? `st_asgeojson(geom)::json as geom,` : '';
21
- const q = `select "${pk}" as id, ${geom} ${cols || '*'} from ${table} t where ${where.join(' and ') || 'true'} limit 1`;
22
-
23
- if (query.sql === '1') return q;
24
-
25
- const { rows } = await pg.query(q, [params.id]);
26
-
27
- const formData = form ? (await getTemplate('form', form) || {}) : {};
28
- const extraKeys = Object.keys(formData)?.filter((key) => formData?.[key]?.type === 'DataTable' && formData?.[key]?.table && formData?.[key]?.parent_id && formData[key]?.colModel);
29
- if (extraKeys?.length) {
30
- await Promise.all(rows?.map(async (row) => {
31
- await Promise.all(extraKeys?.map(async (key) => {
32
- const { colModel, table: extraTable, parent_id: parentId } = formData[key];
33
- const { rows: extraRows } = await pg.query(`select ${parentId} as parent, ${colModel.map((col) => col.name).join(',')} from ${extraTable} a where ${parentId}=$1`, [row.id]);
34
- Object.assign(row, { [key]: extraRows });
35
- }));
36
- }));
37
- }
38
-
39
- return rows?.[0] || {};
40
- }
1
+ import getTemplate from './utils/getTemplate.js';
2
+ import getMeta from '../../pg/funcs/getMeta.js';
3
+
4
+ export default async function tableAPI(req) {
5
+ const {
6
+ pg, params = {}, query = {}, opt = {},
7
+ } = req;
8
+ if (!params.id) return { message: 'not enough params', status: 400 };
9
+
10
+ const loadTable = await getTemplate('table', params.table);
11
+ if (!loadTable) { return { message: 'not found', status: 404 }; }
12
+
13
+ const {
14
+ table, columns, form,
15
+ } = loadTable;
16
+
17
+ const { pk, columns: dbColumns = [] } = await getMeta(table);
18
+ if (!pk) return { message: `table not found: ${table}`, status: 404 };
19
+
20
+ const cols = columns.map((el) => el.name || el).join(',');
21
+ const columnList = dbColumns.map((el) => el.name || el).join(',');
22
+
23
+ const where = [`"${pk}" = $1`, loadTable.query].filter((el) => el);
24
+ const geom = columnList.includes('geom') ? 'st_asgeojson(geom)::json as geom,' : '';
25
+ const q = `select "${pk}" as id, ${geom} ${cols || '*'} from ${table} t where ${where.join(' and ') || 'true'} limit 1`;
26
+
27
+ if (query.sql === '1') return q;
28
+
29
+ const { rows } = await pg.query(q, [opt.id || params.id]);
30
+
31
+ const formData = form ? (await getTemplate('form', form) || {}) : {};
32
+ const extraKeys = Object.keys(formData)?.filter((key) => formData?.[key]?.type === 'DataTable' && formData?.[key]?.table && formData?.[key]?.parent_id && formData[key]?.colModel);
33
+ if (extraKeys?.length) {
34
+ await Promise.all(rows?.map(async (row) => {
35
+ await Promise.all(extraKeys?.map(async (key) => {
36
+ const { colModel, table: extraTable, parent_id: parentId } = formData[key];
37
+ const { rows: extraRows } = await pg.query(`select ${parentId} as parent, ${colModel.map((col) => col.name).join(',')} from ${extraTable} a where ${parentId}=$1`, [row.id]);
38
+ Object.assign(row, { [key]: extraRows });
39
+ }));
40
+ }));
41
+ }
42
+
43
+ return rows?.[0] || {};
44
+ }
package/table/index.js CHANGED
@@ -36,9 +36,9 @@ async function plugin(fastify, config = {}) {
36
36
 
37
37
  fastify.get(`${prefix}/suggest/:data`, {}, suggest);
38
38
  fastify.get(`${prefix}/data/:table/:id?`, { schema: tableSchema }, data); // vs.crm.data.api с node
39
- fastify.get(`${prefix}/table/:table/:id`, { schema: tableSchema }, table);
40
- fastify.get(`${prefix}/card/:table/:id`, { schema: tableSchema }, card);
41
- fastify.get(`${prefix}/search`, { schema: searchTableSchema }, search);
39
+ fastify.get(`${prefix}/table/:table/:id`, { schema: tableSchema }, table); // get data for edit
40
+ fastify.get(`${prefix}/card/:table/:id`, { schema: tableSchema }, card); // get data for card mode
41
+ fastify.get(`${prefix}/search`, { schema: searchTableSchema }, search); // table search
42
42
  fastify.get(`${prefix}/filter/:table`, {}, filter);
43
43
  fastify.get(`${prefix}/form/:form`, {}, form);
44
44
  }