@opengis/fastify-table 1.2.39 → 1.2.41

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opengis/fastify-table",
3
- "version": "1.2.39",
3
+ "version": "1.2.41",
4
4
  "type": "module",
5
5
  "description": "core-plugins",
6
6
  "keywords": [
@@ -41,7 +41,7 @@ export default async function dataUpdate({
41
41
  }).map((el) => (typeof el[1] === 'object' && el[1] && (!Array.isArray(el[1]) || typeof el[1]?.[0] === 'object') ? JSON.stringify(el[1]) : el[1]));
42
42
 
43
43
  // update geometry with srid
44
- if (!srids[table]) {
44
+ if (!srids[table] && pg.tlist?.includes('public.geometry_columns')) {
45
45
  const { srids1 } = await pg.query(`select json_object_agg(_table,rel) as srids1 from (
46
46
  select f_table_schema||'.'||f_table_name as _table,
47
47
  json_object_agg(f_geometry_column, case when srid = 0 then 4326 else srid end) as rel
@@ -29,10 +29,10 @@ left join admin.user_roles d on
29
29
  end )
30
30
  where $1 in (a.route_id, a.alias, a.table_name) and $2 in (b.user_uid, d.user_uid)`;
31
31
 
32
- export default async function getAccess({ table, user = {} }) {
32
+ export default async function getAccess({ table, user = {} }, pg = pgClients.client) {
33
33
  if (!table) return null;
34
34
 
35
- const hookData = await applyHook('getAccess', { table, user });
35
+ const hookData = await applyHook('getAccess', { table, user, pg });
36
36
  if (hookData) return hookData;
37
37
 
38
38
  const { uid, user_type: userType = 'regular' } = user;
@@ -60,11 +60,11 @@ export default async function getAccess({ table, user = {} }) {
60
60
  return { actions: [], query: '1=1' };
61
61
  }
62
62
 
63
- const userAccess = pgClients.client?.pk?.['admin.routes']
64
- && pgClients.client?.pk?.['admin.role_access']
65
- && pgClients.client?.pk?.['admin.roles']
66
- && pgClients.client?.pk?.['admin.user_roles']
67
- ? await pgClients.client.query(q, [table, uid])
63
+ const userAccess = pg?.pk?.['admin.routes']
64
+ && pg.pk?.['admin.role_access']
65
+ && pg.pk?.['admin.roles']
66
+ && pg.pk?.['admin.user_roles']
67
+ ? await pg.query(q, [table, uid])
68
68
  .then(el => ({
69
69
  ...el.rows[0] || {},
70
70
  roles: el.rows?.map?.(row => row.role_id) || [],
@@ -64,16 +64,21 @@ export default function checkPolicy(req, reply) {
64
64
  return null;
65
65
  }
66
66
 
67
+ const validToken = (req.ip === '193.239.152.181' || req.ip === '127.0.0.1' || req.ip.startsWith('192.168.') || config.debug)
68
+ && req.headers?.uid
69
+ && req.headers?.token
70
+ && config.auth?.tokens?.includes?.(headers.token);
71
+
72
+ if (validToken && !req?.user?.uid) {
73
+ req.user = { uid: req.headers?.uid };
74
+ }
75
+
67
76
  /* === policy: public === */
68
77
  if (policy.includes('public')) {
69
78
  return null;
70
79
  }
71
80
 
72
81
  /* === 0. policy: unauthorized access from admin URL === */
73
- const validToken = (req.ip === '193.239.152.181' || req.ip === '127.0.0.1' || req.ip.startsWith('192.168.') || config.debug) && req.headers?.uid && req.headers?.token && config.auth?.tokens?.includes?.(headers.token);
74
- if (validToken && !req?.user?.uid) {
75
- req.user = { uid: req.headers?.uid };
76
- }
77
82
  if (!validToken && !user?.uid && !config.auth?.disable && isAdmin && !policy.includes('public') && !skipCheckPolicyRoutes.filter((el) => el).find(el => req.url.includes(el))) {
78
83
  logger.file('policy/unauthorized', {
79
84
  path, method, params, query, body, token: headers?.token, userId: headers?.uid, ip: req.ip, headers, message: 'unauthorized',
@@ -1,12 +1,12 @@
1
1
  import {
2
- dataDelete, getTemplate, getAccess, applyHook, getToken, config,
2
+ dataDelete, getTemplate, getAccess, applyHook, getToken, config, pgClients,
3
3
  } from '../../../../utils.js';
4
4
 
5
5
  export default async function deleteCrud(req) {
6
- const { user, params = {}, headers = {} } = req || {};
6
+ const { pg = pgClients.client, user, params = {}, headers = {} } = req || {};
7
7
 
8
8
  const hookData = await applyHook('preDelete', {
9
- table: params?.table, id: params?.id, user,
9
+ pg, table: params?.table, id: params?.id, user,
10
10
  });
11
11
 
12
12
  if (hookData?.message && hookData?.status) {
@@ -19,7 +19,7 @@ export default async function deleteCrud(req) {
19
19
  });
20
20
 
21
21
  const { table: del, id } = hookData || tokenData || (config.auth?.disable ? req.params : {});
22
- const { actions = [] } = await getAccess({ table: del, id, user }) || {};
22
+ const { actions = [] } = await getAccess({ table: del, id, user }, pg) || {};
23
23
 
24
24
  if (!actions.includes('del') && !config?.local && !tokenData) {
25
25
  return { message: 'access restricted', status: 403 };
@@ -32,7 +32,7 @@ export default async function deleteCrud(req) {
32
32
  if (!id) return { status: 404, message: 'id is required' };
33
33
 
34
34
  const data = await dataDelete({
35
- table, id, uid: user?.uid, tokenData, referer,
35
+ pg, table, id, uid: user?.uid, tokenData, referer,
36
36
  });
37
37
 
38
38
  return { rowCount: data.rowCount, msg: !data.rowCount ? data : null };
@@ -4,11 +4,11 @@ import {
4
4
 
5
5
  export default async function insert(req) {
6
6
  const {
7
- user = {}, params = {}, body = {}, headers = {},
7
+ pg = pgClients.client, user = {}, params = {}, body = {}, headers = {},
8
8
  } = req || {};
9
9
  if (!user) return { message: 'access restricted', status: 403 };
10
10
 
11
- const hookData = await applyHook('preInsert', { table: params?.table, user });
11
+ const hookData = await applyHook('preInsert', { pg, table: params?.table, user });
12
12
 
13
13
  if (hookData?.message && hookData?.status) {
14
14
  return { message: hookData?.message, status: hookData?.status };
@@ -21,7 +21,7 @@ export default async function insert(req) {
21
21
 
22
22
  const { form, table: add } = hookData || tokenData || (config.auth?.disable ? req.params : {});
23
23
 
24
- const { actions = [] } = await getAccess({ table: add, user }) || {};
24
+ const { actions = [] } = await getAccess({ table: add, user }, pg) || {};
25
25
 
26
26
  if (!actions.includes('add') && !config?.local && !tokenData) {
27
27
  return { message: 'access restricted', status: 403 };
@@ -68,6 +68,7 @@ export default async function insert(req) {
68
68
  }
69
69
 
70
70
  const res = await dataInsert({
71
+ pg,
71
72
  id: params?.id,
72
73
  table: loadTemplate?.table || table,
73
74
  data: body,
@@ -79,19 +80,19 @@ export default async function insert(req) {
79
80
 
80
81
  // admin.custom_column
81
82
  await applyHook('afterInsert', {
82
- table, token: params?.table, body, payload: res, user,
83
+ pg, table, token: params?.table, body, payload: res, user,
83
84
  });
84
85
  // form DataTable
85
86
  // to do: rewrite as single transaction
86
87
  const extraKeys = Object.keys(schema || {})?.filter((key) => schema?.[key]?.type === 'DataTable' && schema?.[key]?.table && schema?.[key]?.parent_id && body[key]?.length);
87
- const pkey = res?.rows?.[0]?.[loadTemplate?.key || pgClients.client?.pk?.[loadTemplate?.table || table]];
88
+ const pkey = res?.rows?.[0]?.[loadTemplate?.key || pg.pk?.[loadTemplate?.table || table]];
88
89
  if (extraKeys?.length) {
89
90
  res.extra = {};
90
91
  await Promise.all(extraKeys?.map(async (key) => {
91
92
  const objId = body[schema[key].parent_id] || req.body?.id || res?.rows?.[0]?.[schema[key].parent_id] || pkey;
92
93
  const extraRows = await Promise.all(body[key].map(async (row) => {
93
94
  const extraRes = await dataInsert({
94
- table: schema[key].table, data: { ...row, [schema[key].parent_id]: objId }, uid: user?.uid, tokenData, referer,
95
+ pg, table: schema[key].table, data: { ...row, [schema[key].parent_id]: objId }, uid: user?.uid, tokenData, referer,
95
96
  });
96
97
  return extraRes?.rows?.[0];
97
98
  }));
@@ -99,6 +100,6 @@ export default async function insert(req) {
99
100
  }));
100
101
  }
101
102
 
102
- const pk = pgClients.client?.pk?.[loadTemplate?.table || table];
103
+ const pk = pg.pk?.[loadTemplate?.table || table];
103
104
  return { id: res?.rows?.[0]?.[pk], rows: res.rows, extra: res.extra };
104
105
  }
@@ -1,15 +1,15 @@
1
1
  import {
2
- config, getAccess, getTemplate, getMeta, setToken, applyHook, getToken,
2
+ config, getAccess, getTemplate, getMeta, setToken, applyHook, getToken, pgClients,
3
3
  } from '../../../../utils.js';
4
4
 
5
5
  export default async function tableAPI(req) {
6
6
  const {
7
- pg, params, user = {}, query = {},
7
+ pg = pgClients.client, params, user = {}, query = {},
8
8
  } = req;
9
9
  const tokenData = await getToken({ token: params?.table, uid: user.uid, json: 1 }) || {};
10
10
 
11
11
  const hookData = await applyHook('preTable', {
12
- table: params?.table, id: params?.id, ...tokenData || {}, user,
12
+ pg, table: params?.table, id: params?.id, ...tokenData || {}, user,
13
13
  });
14
14
 
15
15
  if (hookData?.message && hookData?.status) {
@@ -24,7 +24,8 @@ export default async function tableAPI(req) {
24
24
 
25
25
  const { table, /* columns, */ form } = loadTable;
26
26
 
27
- const tableName = table || hookData?.table || tokenData.table || params.table;
27
+ const templateName = hookData?.table || tokenData.table || params.table;
28
+ const tableName = table || templateName;
28
29
 
29
30
  const id = hookData?.id || tokenData.id || params.id;
30
31
 
@@ -33,17 +34,13 @@ export default async function tableAPI(req) {
33
34
  return { message: 'not enough params', status: 400 };
34
35
  }
35
36
 
36
- const { actions = [], query: accessQuery } = await getAccess({
37
- table: hookData?.table || tokenData.table || params.table,
38
- id,
39
- user,
40
- }) || {};
37
+ const { actions = [], query: accessQuery } = await getAccess({ table: templateName, id, user }, pg) || {};
41
38
 
42
39
  if (!actions.includes('edit') && !config?.local && !tokenData) {
43
40
  return { message: 'access restricted', status: 403 };
44
41
  }
45
42
 
46
- const { pk, columns: dbColumns = [] } = await getMeta(tableName);
43
+ const { pk, columns: dbColumns = [] } = await getMeta({ pg, table: tableName });
47
44
  if (!pk) return { message: `table not found: ${table}`, status: 404 };
48
45
 
49
46
  // const cols = columns.map((el) => el.name || el).join(',');
@@ -85,7 +82,7 @@ export default async function tableAPI(req) {
85
82
  })[0];
86
83
  }
87
84
  const res = await applyHook('afterTable', {
88
- table: tableName, payload: [data], user,
85
+ pg, table: tableName, payload: [data], user,
89
86
  });
90
87
  return res || data || {};
91
88
  }
@@ -11,7 +11,7 @@ export default async function update(req) {
11
11
 
12
12
  if (!user) return { message: 'access restricted', status: 403 };
13
13
  const hookData = await applyHook('preUpdate', {
14
- table: params?.table, id: params?.id, user,
14
+ pg, table: params?.table, id: params?.id, user,
15
15
  });
16
16
 
17
17
  if (hookData?.message && hookData?.status) {
@@ -25,7 +25,7 @@ export default async function update(req) {
25
25
 
26
26
  const { form, table: edit, id } = hookData || tokenData || (config.auth?.disable ? params : {});
27
27
 
28
- const { actions = [] } = await getAccess({ table: edit, id, user }) || {};
28
+ const { actions = [] } = await getAccess({ table: edit, id, user }, pg) || {};
29
29
 
30
30
  if (!actions.includes('edit') && !config?.local && !tokenData) {
31
31
  return { message: 'access restricted', status: 403 };
@@ -75,6 +75,7 @@ export default async function update(req) {
75
75
  }
76
76
 
77
77
  const res = await dataUpdate({
78
+ pg,
78
79
  table: loadTemplate?.table || table,
79
80
  id,
80
81
  data: body,
@@ -85,7 +86,7 @@ export default async function update(req) {
85
86
 
86
87
  // admin.custom_column
87
88
  await applyHook('afterUpdate', {
88
- table: params?.table, body, payload: res, user,
89
+ pg, table: params?.table, body, payload: res, user,
89
90
  });
90
91
 
91
92
  // form DataTable
@@ -95,11 +96,11 @@ export default async function update(req) {
95
96
  await Promise.all(extraKeys?.map(async (key) => {
96
97
  const objId = body[schema[key].parent_id] || body?.id || res?.[schema[key]?.parent_id] || res?.[pg.pk?.[loadTemplate?.table || table] || ''];
97
98
  // delete old extra data
98
- await pgClients.client.query(`delete from ${schema[key].table} where ${schema[key].parent_id}=$1`, [objId]); // rewrite?
99
+ await pg.query(`delete from ${schema[key].table} where ${schema[key].parent_id}=$1`, [objId]); // rewrite?
99
100
  // insert new extra data
100
101
  if (Array.isArray(body[key]) && body[key]?.length) {
101
102
  const extraRows = await Promise.all(body[key]?.map?.(async (row) => {
102
- const extraRes = await dataInsert({ table: schema[key].table, data: { ...row, [schema[key].parent_id]: objId }, uid, tokenData, referer });
103
+ const extraRes = await dataInsert({ pg, table: schema[key].table, data: { ...row, [schema[key].parent_id]: objId }, uid, tokenData, referer });
103
104
  return extraRes?.rows?.[0];
104
105
  }));
105
106
  Object.assign(res.extra, { [key]: extraRows.filter((el) => el) });
@@ -1,5 +1,5 @@
1
1
  import {
2
- config, getTemplate, getFilterSQL, getMeta, metaFormat, getAccess, setToken, gisIRColumn, applyHook, handlebars, handlebarsSync, getSelect, setOpt, getOpt,
2
+ config, getTemplate, getFilterSQL, getMeta, metaFormat, getAccess, setToken, gisIRColumn, applyHook, handlebars, handlebarsSync, getSelect, setOpt, getOpt, pgClients,
3
3
  } from '../../../../utils.js';
4
4
 
5
5
  import conditions from './utils/conditions.js';
@@ -12,7 +12,7 @@ const components = {
12
12
  const maxLimit = 100;
13
13
  export default async function dataAPI(req, reply, called) {
14
14
  const {
15
- pg, params, query = {}, user = {},
15
+ pg = pgClients.client, params, query = {}, user = {},
16
16
  } = req;
17
17
 
18
18
  const time = Date.now();
@@ -20,7 +20,7 @@ export default async function dataAPI(req, reply, called) {
20
20
  const { uid } = user;
21
21
 
22
22
  const hookData = await applyHook('preData', {
23
- table: params?.table, id: params?.id, user,
23
+ pg, table: params?.table, id: params?.id, user,
24
24
  });
25
25
  if (hookData?.message && hookData?.status) {
26
26
  return { message: hookData?.message, status: hookData?.status };
@@ -32,7 +32,7 @@ export default async function dataAPI(req, reply, called) {
32
32
  if (!loadTable && !(tokenData?.table && pg.pk?.[tokenData?.table])) { return { message: 'template not found', status: 404 }; }
33
33
 
34
34
  const id = tokenData?.id || hookData?.id || params?.id;
35
- const { actions = [], query: accessQuery } = await getAccess({ table: tokenData?.table || hookData?.table || params.table, id, user }) || {};
35
+ const { actions = [], query: accessQuery } = await getAccess({ table: tokenData?.table || hookData?.table || params.table, id, user }, pg) || {};
36
36
 
37
37
  if (!actions.includes('view') && !config?.local && !called) {
38
38
  return { message: 'access restricted', status: 403 };
@@ -42,11 +42,13 @@ export default async function dataAPI(req, reply, called) {
42
42
  table, columns = [], sql, cardSql, filters, form, meta, sqlColumns, public: ispublic,
43
43
  } = loadTable || tokenData;
44
44
 
45
- const tableMeta = await getMeta(table);
45
+ const tableMeta = await getMeta({ pg, table });
46
+
46
47
  if (tableMeta?.view) {
47
48
  if (!loadTable?.key && !tokenData?.key) return { message: `key not found: ${table}`, status: 404 };
48
49
  Object.assign(tableMeta, { pk: loadTable?.key || tokenData?.key });
49
50
  }
51
+
50
52
  const { pk, columns: dbColumns = [] } = tableMeta || {};
51
53
 
52
54
  if (!pk) return { message: `table not found: ${table}`, status: 404 };
@@ -56,7 +58,7 @@ export default async function dataAPI(req, reply, called) {
56
58
  const cardSqlFiltered = hookData?.id || params.id ? (cardSql?.filter?.((el) => !el?.disabled && el?.name && el?.sql?.replace) || []) : [];
57
59
  const cardSqlTable = cardSqlFiltered.length ? cardSqlFiltered.map((el, i) => ` left join lateral (${el.sql.replace('{{uid}}', uid)}) ct${i} on 1=1 `).join('\n') || '' : '';
58
60
 
59
- const { fields } = await pg.queryCache(`select * from ${table} t ${sqlTable} ${cardSqlTable} limit 0`);
61
+ const { fields = [] } = pg.queryCache ? await pg.queryCache(`select * from ${table} t ${sqlTable} ${cardSqlTable} limit 0`) : {};
60
62
  const dbColumnsTable = fields.map(el => el.name);
61
63
  const cols = columns.filter((el) => el.name !== 'geom' && dbColumnsTable.includes(el.name)).map((el) => el.name || el).join(',');
62
64
  const metaCols = Object.keys(loadTable?.meta?.cls || {}).filter((el) => !cols.includes(el)).length
@@ -78,6 +80,7 @@ export default async function dataAPI(req, reply, called) {
78
80
 
79
81
  const checkFilter = [query.filter, query.search, query.state, query.custom].filter((el) => el).length;
80
82
  const fData = checkFilter ? await getFilterSQL({
83
+ pg,
81
84
  table: params.table,
82
85
  filter: query.filter,
83
86
  search: query.search,
@@ -112,7 +115,7 @@ export default async function dataAPI(req, reply, called) {
112
115
  ${params.id || query.key ? '*' : sqlColumns || cols || '*'}
113
116
  ${metaCols}
114
117
 
115
- ${dbColumns.find((el) => el.name === 'geom' && pg.pgType[el.dataTypeID] === 'geometry') ? ',st_asgeojson(geom)::json as geom' : ''}
118
+ ${dbColumns.find((el) => el.name === 'geom' && pg.pgType?.[el.dataTypeID] === 'geometry') ? ',st_asgeojson(geom)::json as geom' : ''}
116
119
  from (select * from ${table} where ${sqlTable ? 'true' : (where.join(' and ') || 'true')} ${order}) t
117
120
  ${sqlTable}
118
121
  ${params.id ? cardSqlTable : ''}
@@ -129,7 +132,7 @@ export default async function dataAPI(req, reply, called) {
129
132
  const filterWhere = [fData.q, search, bbox, queryPolyline, interfaceQuery, loadTable?.query, tokenData?.query].filter((el) => el);
130
133
 
131
134
  const aggColumns = columns.filter((el) => el.agg).reduce((acc, curr) => Object.assign(acc, { [curr.name]: curr.agg }), {});
132
- const aggregates = dbColumns.map((el) => ({ name: el.name, type: pg.pgType[el.dataTypeID] })).filter((el) => ['numeric', 'double precision'].includes(el.type) && aggColumns[el.name]);
135
+ const aggregates = dbColumns.map((el) => ({ name: el.name, type: pg.pgType?.[el.dataTypeID] })).filter((el) => ['numeric', 'double precision'].includes(el.type) && aggColumns[el.name]);
133
136
  const qCount = `select
134
137
  count(*)::int as total,
135
138
  count(*) FILTER(WHERE ${filterWhere.join(' and ') || 'true'})::int as filtered
@@ -142,9 +145,9 @@ export default async function dataAPI(req, reply, called) {
142
145
 
143
146
  const counts = keyQuery || tokenData?.id || hookData?.id || params.id
144
147
  ? { total: rows.length, filtered: rows.length }
145
- : await pg.queryCache(qCount, { table: loadTable?.table || tokenData?.table, time: 5 }).then(el => el?.rows[0] || {});
148
+ : await pg.queryCache?.(qCount, { table: loadTable?.table || tokenData?.table, time: 5 }).then(el => el?.rows[0] || {});
146
149
 
147
- const { total, filtered } = counts;
150
+ const { total, filtered } = counts || {};
148
151
  const agg = Object.keys(counts).filter(el => !['total', 'filtered'].includes(el)).reduce((acc, el) => ({ ...acc, [el]: counts[el] }), {});
149
152
 
150
153
  await metaFormat({ rows, table: tokenData?.table || hookData?.table || params.table });
@@ -215,8 +218,8 @@ export default async function dataAPI(req, reply, called) {
215
218
 
216
219
  // data result
217
220
  const data = {};
218
- const route = await pg.query(`select route_id as path, title from admin.routes where enabled and alias=$1 limit 1`, [table])
219
- .then(el => el.rows?.[0] || {});
221
+ const route = pg.pk?.['admin.routes'] ? await pg.query(`select route_id as path, title from admin.routes where enabled and alias=$1 limit 1`, [table])
222
+ .then(el => el.rows?.[0] || {}) : {};
220
223
  Object.assign(route, { tableTitle: loadTable?.title });
221
224
  if (index?.data && index?.data?.[0]?.name) {
222
225
  await Promise.all(index.data.filter((el) => el?.name && el?.sql).map(async (el) => {
@@ -266,7 +269,7 @@ export default async function dataAPI(req, reply, called) {
266
269
  }
267
270
 
268
271
  const result = await applyHook('afterData', {
269
- table: loadTable?.table || tokenData?.table, payload: res, user,
272
+ pg, table: loadTable?.table || tokenData?.table, payload: res, user,
270
273
  });
271
274
 
272
275
  return result || res;
@@ -47,7 +47,7 @@ export default async function suggest(req) {
47
47
  original: `with c(id,text) as (select ${column || 'row_number() over()'}, ${column}, count(*) from ${tableName} group by ${column} limit ${limit}) select * from c`,
48
48
  searchQuery: '(lower("text") ~ $1 )',
49
49
  }
50
- : await getSelectMeta({ name: selectName, nocache: query?.nocache });
50
+ : await getSelectMeta({ pg: pg1, name: selectName, nocache: query?.nocache });
51
51
 
52
52
  if (meta?.minLength && query.key && query.key.length < meta?.minLength) {
53
53
  return { message: `min length: ${meta.minLength}` };