@opengis/fastify-table 1.4.6 → 1.4.7

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/config.js CHANGED
@@ -1,11 +1,33 @@
1
- import fs from 'fs';
1
+ import dotenv from 'dotenv';
2
2
 
3
- const fileName = ['config.json', '/data/local/config.json'].find(el => (fs.existsSync(el) ? el : null));
4
- const config = fileName ? JSON.parse(fs.readFileSync(fileName)) : {};
3
+ import { existsSync, readFileSync } from 'node:fs';
5
4
 
5
+ import unflattenObject from './server/plugins/util/funcs/unflattenObject.js';
6
+
7
+ const fileName = ['config.json', '/data/local/config.json'].find(el => (existsSync(el) ? el : null));
8
+ const config = fileName ? JSON.parse(readFileSync(fileName)) : {};
9
+
10
+ // npm run dev === cross-env NODE_ENV=development
11
+ // alt: node --env=development
6
12
  Object.assign(config, {
7
13
  allTemplates: config?.allTemplates || {},
8
14
  skipCheckPolicyRoutes: [],
15
+ env: process.env?.NODE_ENV || process.argv[2]?.split?.('=')?.pop?.(),
9
16
  });
10
17
 
18
+ if (config.env && existsSync(`.env.${config.env}`)) {
19
+ const { parsed } = dotenv.config({ path: `.env.${config.env}` });
20
+ console.log('start with env:', config.env);
21
+
22
+ const obj = unflattenObject(parsed);
23
+
24
+ Object.keys(obj)
25
+ .filter(key => typeof obj[key] === 'string'
26
+ && (obj[key].startsWith('[') || ['true', 'false'].includes(obj[key]))) // json array / boolean
27
+ .forEach(key => {
28
+ obj[key] = JSON.parse(obj[key]);
29
+ });
30
+ Object.assign(config, { ...obj });
31
+ }
32
+
11
33
  export default config;
package/index.js CHANGED
@@ -113,5 +113,6 @@ async function plugin(fastify, opt) {
113
113
  templatesRoutes(fastify, opt);
114
114
 
115
115
  fastify.get('/api/test-proxy', {}, (req) => ({ ...req.headers || {}, sessionId: req.session?.sessionId }));
116
+ fastify.get('/api/config', { config: { policy: ['admin', 'site'] } }, () => config);
116
117
  }
117
118
  export default fp(plugin);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opengis/fastify-table",
3
- "version": "1.4.6",
3
+ "version": "1.4.7",
4
4
  "type": "module",
5
5
  "description": "core-plugins",
6
6
  "keywords": [
@@ -25,10 +25,12 @@
25
25
  "test:helpers": "node --test .\\test\\helpers",
26
26
  "test:routes": "node --test .\\test\\routes",
27
27
  "test:functions": "node --test .\\test\\functions",
28
- "compress": "node compress.js"
28
+ "compress": "node compress.js",
29
+ "dev": "cross-env NODE_ENV=development node server.js"
29
30
  },
30
31
  "dependencies": {
31
32
  "@fastify/http-proxy": "11.1.2",
33
+ "dotenv": "16.5.0",
32
34
  "fastify": "5.3.3",
33
35
  "fastify-plugin": "5.0.1",
34
36
  "handlebars": "4.7.8",
@@ -43,6 +45,7 @@
43
45
  "uglify-js": "3.19.3"
44
46
  },
45
47
  "devDependencies": {
48
+ "cross-env": "7.0.3",
46
49
  "eslint": "8.49.0",
47
50
  "eslint-config-airbnb": "19.0.4"
48
51
  },
@@ -46,7 +46,7 @@ export default async function dataDelete({
46
46
  });
47
47
  throw err;
48
48
  })
49
- .then(el => el.rows?.[0] || {});
49
+ .then(el => (el.rows?.[0] ? { rowCount: 1, ...el.rows[0] } : {}));
50
50
 
51
51
  await logChanges({
52
52
  pg, table, tokenData, referer, id, uid, type: 'DELETE',
@@ -1,4 +1,4 @@
1
- import { config, getTemplate, pgClients } from "../../../utils.js";
1
+ import { config, getTemplate, pgClients } from '../../../utils.js';
2
2
 
3
3
  const defaultTable = 'crm.extra_data';
4
4
 
@@ -24,7 +24,7 @@ export default async function extraDataGet({
24
24
 
25
25
  const extraDataTable = config.extraData?.[table]
26
26
  || config.extraData?.[table.split('.').shift()]
27
- || config.extraData?.['default']
27
+ || config.extraData?.default
28
28
  || config.extraData
29
29
  || defaultTable;
30
30
 
@@ -44,9 +44,10 @@ export default async function extraDataGet({
44
44
  ...extraRows
45
45
  .filter(el => el.object_id === row.id)
46
46
  .reduce((acc, curr) => Object.assign(acc, {
47
- [curr.property_key]: format(curr.property_key, curr.value_text, loadTemplate?.schema)
48
- }), {})
47
+ [curr.property_key]: format(curr.property_key, curr.value_text, loadTemplate?.schema),
48
+ }), {}),
49
49
  });
50
50
  });
51
51
  }
52
- };
52
+ return null;
53
+ }
@@ -0,0 +1,11 @@
1
+ export default function flattenObject(obj = {}, keys, parent = '') {
2
+ return Object.keys(obj).reduce((acc, key) => {
3
+ const newKey = parent ? `${parent}.${key}` : key;
4
+ if (typeof obj[key] === 'object' && obj[key] !== null && !Array.isArray(obj[key])) {
5
+ Object.assign(acc, flattenObject(obj[key], keys, newKey));
6
+ } else if (keys ? keys.includes(newKey) : true) {
7
+ acc[newKey] = obj[key];
8
+ }
9
+ return acc;
10
+ }, {});
11
+ }
@@ -0,0 +1,14 @@
1
+ export default function unflattenObject(flatObj) {
2
+ return Object.keys(flatObj).reduce((acc, key) => {
3
+ const keys = key.split('.');
4
+ keys.reduce((nestedObj, part, index) => {
5
+ if (index === keys.length - 1) {
6
+ nestedObj[part] = flatObj[key];
7
+ } else {
8
+ nestedObj[part] = nestedObj[part] || {};
9
+ }
10
+ return nestedObj[part];
11
+ }, acc);
12
+ return acc;
13
+ }, {});
14
+ }
@@ -17,21 +17,31 @@ export default async function deleteCrud(req, reply) {
17
17
 
18
18
  const { referer } = headers;
19
19
  const tokenData = await getToken({
20
- uid: user.uid, token: params.table, json: 1,
20
+ uid: user.uid, token: params.id || params.table, json: 1,
21
21
  });
22
22
 
23
- const { table: del, id } = hookData || tokenData || (config.auth?.disable ? req.params : {});
23
+ const { table: del, id } = hookData || tokenData || (config.security?.disableToken || config.local || config.auth?.disable ? req.params : {});
24
24
  const { actions = [] } = await getAccess({ table: del, id, user }, pg) || {};
25
25
 
26
- if (!actions.includes('del') && !config?.local && !tokenData) {
26
+ if (!tokenData && !config?.local && !config.security?.disableToken && !config.auth?.disable) {
27
+ return reply.status(400).send('invalid token');
28
+ }
29
+
30
+ if (!actions.includes('del') && !config?.local) {
27
31
  return reply.status(403).send('access restricted');
28
32
  }
33
+
29
34
  const loadTemplate = await getTemplate('table', del);
30
35
 
31
36
  const { table } = loadTemplate || hookData || tokenData || req.params || {};
32
37
 
33
- if (!table) reply.status(404).send('table is required');
34
- if (!id) reply.status(404).send('id is required');
38
+ if (!table) {
39
+ return reply.status(404).send('table is required');
40
+ }
41
+
42
+ if (!id) {
43
+ return reply.status(404).send('id is required');
44
+ }
35
45
 
36
46
  const data = await dataDelete({
37
47
  pg, table, id, uid: user?.uid, tokenData, referer,
@@ -19,15 +19,20 @@ export default async function insert(req, reply) {
19
19
  }
20
20
 
21
21
  const { referer } = headers;
22
+
22
23
  const tokenData = await getToken({
23
24
  uid: user?.uid, token: params.table, mode: 'a', json: 1,
24
25
  });
25
26
 
26
- const { form, table: add } = hookData || tokenData || (config.auth?.disable ? req.params : {});
27
+ const { form, table: add } = hookData || tokenData || (config.security?.disableToken || config.local || config.auth?.disable ? req.params : {});
27
28
 
28
29
  const { actions = [] } = await getAccess({ table: add, user }, pg) || {};
29
30
 
30
- if (!actions.includes('add') && !config.local && !config.debug && !tokenData) {
31
+ if (!tokenData && !config.local && !config.security?.disableToken && !config.auth?.disable) {
32
+ return reply.status(400).send('invalid token');
33
+ }
34
+
35
+ if (!actions.includes('add') && !config.local) {
31
36
  return reply.status(403).send('access restricted');
32
37
  }
33
38
 
@@ -37,8 +42,9 @@ export default async function insert(req, reply) {
37
42
 
38
43
  const loadTemplate = await getTemplate('table', add);
39
44
  const { table } = loadTemplate || hookData || tokenData || req.params || {};
45
+
40
46
  if (!table) {
41
- return { message: 'table not found', status: 404 };
47
+ return reply.status(404).send('table not found');
42
48
  }
43
49
 
44
50
  const formData = form || loadTemplate?.form ? (await getTemplate('form', form || loadTemplate?.form) || {}) : {};
@@ -110,5 +116,5 @@ export default async function insert(req, reply) {
110
116
  }
111
117
 
112
118
  const pk = pg.pk?.[loadTemplate?.table || table];
113
- return { id: res?.rows?.[0]?.[pk], rows: res.rows, extra: res.extra };
119
+ return reply.status(200).send({ id: res?.rows?.[0]?.[pk], rows: res.rows, extra: res.extra });
114
120
  }
@@ -4,7 +4,7 @@ import {
4
4
 
5
5
  import extraDataGet from '../../../plugins/extra/extraDataGet.js';
6
6
 
7
- export default async function tableAPI(req) {
7
+ export default async function tableAPI(req, reply) {
8
8
  const {
9
9
  pg = pgClients.client, params, user = {}, query = {},
10
10
  } = req;
@@ -38,7 +38,11 @@ export default async function tableAPI(req) {
38
38
 
39
39
  const { actions = [], query: accessQuery } = await getAccess({ table: templateName, id, user }, pg) || {};
40
40
 
41
- if (!actions.includes('edit') && !config?.local && !tokenData) {
41
+ if (!tokenData && !config?.local && !config.security?.disableToken) {
42
+ return reply.status(400).send('invalid token');
43
+ }
44
+
45
+ if (!actions.includes('edit') && !config?.local) {
42
46
  return reply.status(403).send('access restricted');
43
47
  }
44
48
 
@@ -94,7 +98,7 @@ export default async function tableAPI(req) {
94
98
  Object.assign(data, { [key]: extraRows });
95
99
  }));
96
100
  }
97
- if (user?.uid && actions?.includes?.('edit')) {
101
+ if (user?.uid && !config.security?.disableToken && actions.includes('edit')) {
98
102
  data.token = tokenData?.table ? params.table : setToken({
99
103
  ids: [JSON.stringify({ id, table: tableName, form: loadTable.form })],
100
104
  uid: user.uid,
@@ -23,14 +23,18 @@ export default async function update(req, reply) {
23
23
 
24
24
  const { referer } = headers;
25
25
  const tokenData = await getToken({
26
- uid: user.uid, token: body.token || params.table, mode: 'w', json: 1,
26
+ uid: user.uid, token: body.token || params.id || params.table, mode: 'w', json: 1,
27
27
  });
28
28
 
29
- const { form, table: edit, id } = hookData || tokenData || (config.auth?.disable ? params : {});
29
+ const { form, table: edit, id } = hookData || tokenData || (config.security?.disableToken || config.local || config.auth?.disable ? params : {});
30
30
 
31
31
  const { actions = [] } = await getAccess({ table: edit, id, user }, pg) || {};
32
32
 
33
- if (!actions.includes('edit') && !config.local && !config.debug && !tokenData) {
33
+ if (!tokenData && !config.local && !config.security?.disableToken && !config.auth?.disable) {
34
+ return reply.status(400).send('invalid token');
35
+ }
36
+
37
+ if (!actions.includes('edit') && !config.local) {
34
38
  return reply.status(403).send('access restricted');
35
39
  }
36
40
 
@@ -114,5 +118,5 @@ export default async function update(req, reply) {
114
118
  }));
115
119
  }
116
120
 
117
- return res;
121
+ return reply.status(200).send(res);
118
122
  }
@@ -5,7 +5,7 @@ import { join } from 'node:path';
5
5
  import { existsSync, readdirSync, readFileSync } from 'node:fs';
6
6
 
7
7
  import {
8
- menuDirs, pgClients, applyHook, config,
8
+ menuDirs, pgClients, applyHook, config,
9
9
  } from '../../../../utils.js';
10
10
 
11
11
  const menuCache = [];
@@ -63,7 +63,7 @@ export default async function adminMenu({
63
63
  };
64
64
  await applyHook('userMenu', result);
65
65
 
66
- if (session && user?.uid && !user.user_type?.includes?.('admin') && !user.type?.includes?.('admin') && pg.pk['admin.role_access']) {
66
+ if (session && user?.uid && !user.user_type?.includes?.('admin') && !user.type?.includes?.('admin') && pg.pk?.['admin.role_access']) {
67
67
  const { type, gl = [], routes = [] } = await pg.query(`select user_type as type, b.gl,routes from admin.users a
68
68
  left join lateral (
69
69
  select array_agg(role_id) as gl from admin.user_roles
@@ -2,10 +2,9 @@
2
2
  import path from 'node:path';
3
3
 
4
4
  import {
5
- getAccess, handlebars, setOpt, setToken, getTemplate, handlebarsSync, applyHook,
5
+ config, getAccess, handlebars, setOpt, setToken, getTemplate, handlebarsSync, applyHook, getData,
6
6
  } from '../../../../utils.js';
7
7
 
8
- import getTableData from './tableData.js';
9
8
  import conditions from './utils/conditions.js';
10
9
 
11
10
  const components = {
@@ -15,8 +14,9 @@ const components = {
15
14
 
16
15
  export default async function getCardData(req, reply) {
17
16
  const {
18
- pg, params = {}, session = {}, user = {},
17
+ pg, params = {}, user = {},
19
18
  } = req;
19
+
20
20
  const { table, id } = params;
21
21
  const { uid } = user;
22
22
 
@@ -40,11 +40,11 @@ export default async function getCardData(req, reply) {
40
40
  ? await pg.query(
41
41
  `select * from ${index.table} where ${handlebarsSync.compile(index.query)({ uid, user })}`,
42
42
  )
43
- : await getTableData({
44
- pg, params: { table, id }, session, user,
45
- });
43
+ : await getData({
44
+ pg, table, id, user,
45
+ }, reply);
46
46
 
47
- if (message) return { message };
47
+ if (message) return message;
48
48
 
49
49
  // conditions
50
50
  index.panels?.filter(el => el.items).forEach(el1 => {
@@ -72,7 +72,7 @@ export default async function getCardData(req, reply) {
72
72
 
73
73
  // tokens result
74
74
  const tokens = {};
75
- if (index?.tokens && typeof index?.tokens === 'object' && !Array.isArray(index?.tokens)) {
75
+ if (!config.security?.disableToken && index?.tokens && typeof index?.tokens === 'object' && !Array.isArray(index?.tokens)) {
76
76
  Object.keys(index.tokens || {})
77
77
  .filter(key => index?.tokens[key]?.public
78
78
  || access.actions?.includes?.('edit')
@@ -188,7 +188,7 @@ export default async function dataAPI(req, reply, called) {
188
188
 
189
189
  timeArr.push(Date.now());
190
190
 
191
- if (uid && rows.length && editable) {
191
+ if (uid && rows.length && !config.security?.disableToken && (editable || actions.includes('edit') || actions.includes('del'))) {
192
192
  rows.forEach(row => {
193
193
  row.token = setToken({
194
194
  ids: [JSON.stringify({ id: row.id, table: tokenData?.table || hookData?.table || params.table, form: loadTable?.form })],
@@ -245,7 +245,7 @@ export default async function dataAPI(req, reply, called) {
245
245
  const tokens = {};
246
246
  if (template && objectId) {
247
247
  // tokens result
248
- if (index?.tokens && typeof index?.tokens === 'object' && !Array.isArray(index?.tokens)) {
248
+ if (!config.security?.disableToken && index?.tokens && typeof index?.tokens === 'object' && !Array.isArray(index?.tokens)) {
249
249
  Object.keys(index.tokens || {})
250
250
  .filter(key => index?.tokens[key]?.public
251
251
  || actions?.includes?.('edit')
@@ -347,7 +347,7 @@ export default async function dataAPI(req, reply, called) {
347
347
  }
348
348
 
349
349
  // console.log({ add: loadTable.table, form: loadTable.form });
350
- if (uid && actions.includes('add')) {
350
+ if (uid && !config.security?.disableToken && actions.includes('add')) {
351
351
  const addTokens = setToken({
352
352
  ids: [
353
353
  JSON.stringify({
package/utils.js CHANGED
@@ -83,6 +83,9 @@ import json2yml from './server/plugins/yml/funcs/json2yml.js';
83
83
  import formatMdoc from './server/plugins/md/funcs/formatMdoc.js';
84
84
  import mdToHTML from './server/plugins/md/funcs/mdToHTML.js';
85
85
 
86
+ import flattenObject from './server/plugins/util/funcs/flattenObject.js';
87
+ import unflattenObject from './server/plugins/util/funcs/unflattenObject.js';
88
+
86
89
  export default null;
87
90
  export {
88
91
  config,
@@ -160,4 +163,7 @@ export {
160
163
 
161
164
  formatMdoc,
162
165
  mdToHTML,
166
+
167
+ flattenObject,
168
+ unflattenObject,
163
169
  };