@opengis/fastify-table 1.0.42 → 1.0.44

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,13 @@
1
1
  # fastify-table
2
2
 
3
+ ## 1.0.44 - 18.06.2024
4
+
5
+ - add extra data support (form DataTable)
6
+
7
+ ## 1.0.43 - 14.06.2024
8
+
9
+ - add settings api, funcs, migration
10
+
3
11
  ## 1.0.42 - 12.06.2024
4
12
 
5
13
  - table cardSql support
@@ -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
- return { rows: res.rows };
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 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 update(req) {
7
- const loadTemplate = await getTemplate('table', req.params.table);
8
- const { table } = loadTemplate || req.params || {};
9
- const { id } = req.params || {};
10
- if (!req.params?.table) return { message: 'table is required', status: 404 };
11
- if (!id) return { message: 'id is required', status: 404 };
12
-
13
- const { funcs, session, params } = req;
14
- const tokenDataString = await getToken({
15
- funcs, session, token: params.table, mode: 'w', json: 0,
16
- });
17
-
18
- const tokenData = JSON.parse(tokenDataString || '{}');
19
-
20
- const formData = tokenData?.form ? await getTemplate('form', tokenData.form) : {};
21
-
22
- const xssCheck = checkXSS({ body: req.body, schema: formData?.schema });
23
-
24
- if (xssCheck.error && formData?.xssCheck !== false) {
25
- req.log.warn({ name: 'injection/xss', msg: xssCheck.error, table }, req);
26
- return { message: 'Дані містять заборонені символи. Приберіть їх та спробуйте ще раз', status: 409 };
27
- }
28
-
29
- const res = await dataUpdate({ table: tokenData?.table || table, id: tokenData?.id || id, data: req.body });
30
- return res;
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/index.js CHANGED
@@ -13,6 +13,7 @@ import notificationPlugin from './notification/index.js';
13
13
  import widgetPlugin from './widget/index.js';
14
14
  import crudPlugin from './crud/index.js';
15
15
  import policyPlugin from './policy/index.js';
16
+ import settingsPlugin from './settings/index.js';
16
17
 
17
18
  import pgClients from './pg/pgClients.js';
18
19
 
@@ -59,6 +60,10 @@ async function plugin(fastify, opt) {
59
60
  if (!fastify.funcs) {
60
61
  fastify.addHook('onRequest', async (req) => {
61
62
  req.funcs = fastify;
63
+ if (!req.user && req.session?.passport?.user) {
64
+ const { user } = req.session?.passport || {};
65
+ req.user = user;
66
+ }
62
67
  });
63
68
  // fastify.decorateRequest('funcs', fastify);
64
69
  }
@@ -70,6 +75,7 @@ async function plugin(fastify, opt) {
70
75
  crudPlugin(fastify, opt);
71
76
  notificationPlugin(fastify, opt);
72
77
  widgetPlugin(fastify, opt);
78
+ settingsPlugin(fastify, opt);
73
79
  }
74
80
  export default fp(plugin);
75
81
  // export { rclient };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opengis/fastify-table",
3
- "version": "1.0.42",
3
+ "version": "1.0.44",
4
4
  "type": "module",
5
5
  "description": "core-plugins",
6
6
  "main": "index.js",
@@ -22,6 +22,14 @@ export default function checkPolicy(req) {
22
22
  const isServer = process.argv[2];
23
23
  const { policy = [] } = req.routeOptions?.config || {};
24
24
 
25
+ /*= == 0.Check superadmin access === */
26
+ if (policy.includes('superadmin') && user?.user_type !== 'superadmin') {
27
+ log.warn({
28
+ name: 'api/superadmin', params, query, body: JSON.stringify(req?.body || {}).substring(30), message: 'access restricted: 0',
29
+ });
30
+ return { message: 'access restricted: 0', status: 403 };
31
+ }
32
+
25
33
  /*= == 1.File injection === */
26
34
  if (JSON.stringify(params || {})?.includes('../') || JSON.stringify(query || {})?.includes('../') || path?.includes('../')) {
27
35
  log.warn({
@@ -0,0 +1,13 @@
1
+ CREATE EXTENSION if not exists "uuid-ossp";
2
+
3
+ CREATE OR REPLACE FUNCTION next_id()
4
+ RETURNS text AS
5
+ $BODY$
6
+ DECLARE
7
+
8
+ BEGIN
9
+ return replace(uuid_generate_v4()::text, '-', '');
10
+ END;
11
+ $BODY$
12
+ LANGUAGE plpgsql VOLATILE
13
+ COST 100;
@@ -1,37 +1,8 @@
1
- -- next_id()
2
- CREATE SEQUENCE crm.cls_clsid_seq
3
- INCREMENT 1
4
- MINVALUE 1
5
- MAXVALUE 9223372036854775807
6
- START 128142470
7
- CACHE 1;
8
- CREATE OR REPLACE FUNCTION crm.next_id()
9
- RETURNS bigint AS
10
- $BODY$
11
- DECLARE
12
- our_epoch bigint := 1314220021721;
13
- seq_id bigint;
14
- now_millis bigint;
15
- shard_id int := 1;
16
- result bigint;
17
- BEGIN
18
- SELECT nextval('crm.cls_clsid_seq') % 1024 INTO seq_id;
19
-
20
- SELECT FLOOR(EXTRACT(EPOCH FROM clock_timestamp()) * 1000) INTO now_millis;
21
- result := (now_millis - our_epoch) << 23;
22
- result := result | (shard_id << 10);
23
- result := result | (seq_id);
24
- return result;
25
- END;
26
- $BODY$
27
- LANGUAGE plpgsql VOLATILE
28
- COST 100;
29
-
30
1
  -- crm.notifications
31
2
  -- DROP TABLE IF EXISTS crm.notifications;
32
3
  CREATE TABLE IF NOT EXISTS crm.notifications();
33
4
  ALTER TABLE crm.notifications DROP CONSTRAINT IF EXISTS crm_notifications_pkey;
34
- ALTER TABLE crm.notifications ADD COLUMN IF NOT EXISTS notification_id text NOT NULL DEFAULT crm.next_id();
5
+ ALTER TABLE crm.notifications ADD COLUMN IF NOT EXISTS notification_id text NOT NULL DEFAULT next_id();
35
6
 
36
7
  ALTER TABLE crm.notifications ADD COLUMN IF NOT EXISTS notification_user_id text;
37
8
  ALTER TABLE crm.notifications ADD COLUMN IF NOT EXISTS notification_type text DEFAULT 'notify'::text;
@@ -51,7 +22,7 @@ ALTER TABLE crm.notifications ADD CONSTRAINT crm_notifications_pkey PRIMARY KEY
51
22
  -- DROP TABLE IF EXISTS crm.files;
52
23
  CREATE TABLE IF NOT EXISTS crm.files();
53
24
  ALTER TABLE crm.files DROP CONSTRAINT IF EXISTS crm_files_pkey;
54
- ALTER TABLE crm.files ADD COLUMN IF NOT EXISTS file_id text NOT NULL DEFAULT crm.next_id();
25
+ ALTER TABLE crm.files ADD COLUMN IF NOT EXISTS file_id text NOT NULL DEFAULT next_id();
55
26
 
56
27
  ALTER TABLE crm.files ADD COLUMN IF NOT EXISTS uploaded_name text;
57
28
  ALTER TABLE crm.files ADD COLUMN IF NOT EXISTS entity_id text; -- object_id
@@ -77,7 +48,7 @@ ALTER TABLE crm.files ADD CONSTRAINT crm_files_pkey PRIMARY KEY (file_id);
77
48
  -- DROP TABLE IF EXISTS crm.communications;
78
49
  CREATE TABLE IF NOT EXISTS crm.communications();
79
50
  ALTER TABLE crm.communications DROP CONSTRAINT IF EXISTS crm_communications_pkey;
80
- ALTER TABLE crm.communications ADD COLUMN IF NOT EXISTS communication_id text NOT NULL DEFAULT crm.next_id();
51
+ ALTER TABLE crm.communications ADD COLUMN IF NOT EXISTS communication_id text NOT NULL DEFAULT next_id();
81
52
 
82
53
  ALTER TABLE crm.communications ADD COLUMN IF NOT EXISTS entity_id text; -- object_id
83
54
  ALTER TABLE crm.communications ADD COLUMN IF NOT EXISTS entity_type text; -- table_name
@@ -104,7 +75,7 @@ ALTER TABLE crm.communications ADD CONSTRAINT crm_communications_pkey PRIMARY KE
104
75
  -- DROP TABLE IF EXISTS crm.checklists;
105
76
  CREATE TABLE IF NOT EXISTS crm.checklists();
106
77
  ALTER TABLE crm.checklists DROP CONSTRAINT IF EXISTS crm_checklists_pkey;
107
- ALTER TABLE crm.checklists ADD COLUMN IF NOT EXISTS checklist_id text NOT NULL DEFAULT crm.next_id();
78
+ ALTER TABLE crm.checklists ADD COLUMN IF NOT EXISTS checklist_id text NOT NULL DEFAULT next_id();
108
79
 
109
80
  ALTER TABLE crm.checklists ADD COLUMN IF NOT EXISTS entity_id text; -- object_id
110
81
  ALTER TABLE crm.checklists ADD COLUMN IF NOT EXISTS entity_type text; -- table_name
@@ -126,7 +97,9 @@ ALTER TABLE crm.checklists ADD CONSTRAINT crm_checklists_pkey PRIMARY KEY (check
126
97
  -- crm.cls
127
98
  -- DROP TABLE IF EXISTS crm.cls;
128
99
  CREATE TABLE IF NOT EXISTS crm.cls();
129
- ALTER TABLE crm.cls ADD COLUMN IF NOT EXISTS cls_id text NOT NULL DEFAULT crm.next_id();
100
+ ALTER TABLE crm.cls DROP CONSTRAINT IF EXISTS crm_cls_pkey;
101
+ ALTER TABLE crm.cls DROP CONSTRAINT IF EXISTS crm_cls_unique;
102
+ ALTER TABLE crm.cls ADD COLUMN IF NOT EXISTS cls_id text NOT NULL DEFAULT next_id();
130
103
 
131
104
  ALTER TABLE crm.cls ADD COLUMN IF NOT EXISTS name text;
132
105
  ALTER TABLE crm.cls ADD COLUMN IF NOT EXISTS data text;
@@ -1,6 +1,6 @@
1
1
  CREATE TABLE IF NOT EXISTS log.table_changes();
2
2
  ALTER TABLE log.table_changes DROP CONSTRAINT IF EXISTS log_table_changes_pkey;
3
- ALTER TABLE log.table_changes ADD COLUMN IF NOT EXISTS table_change_id text NOT NULL DEFAULT admin.next_id();
3
+ ALTER TABLE log.table_changes ADD COLUMN IF NOT EXISTS table_change_id text NOT NULL DEFAULT next_id();
4
4
 
5
5
  ALTER TABLE log.table_changes ADD COLUMN IF NOT EXISTS entity_type text;
6
6
  ALTER TABLE log.table_changes ADD COLUMN IF NOT EXISTS entity_id text; -- object_id
@@ -28,7 +28,7 @@ CREATE TABLE IF NOT EXISTS log.user_auth();
28
28
  ALTER TABLE log.user_auth DROP CONSTRAINT IF EXISTS log_user_auth_pkey;
29
29
  ALTER TABLE log.user_auth DROP CONSTRAINT IF EXISTS log_user_auth_user_id_fkey;
30
30
 
31
- ALTER TABLE log.user_auth ADD COLUMN IF NOT EXISTS user_auth_id text NOT NULL DEFAULT admin.next_id();
31
+ ALTER TABLE log.user_auth ADD COLUMN IF NOT EXISTS user_auth_id text NOT NULL DEFAULT next_id();
32
32
 
33
33
  ALTER TABLE log.user_auth ADD COLUMN IF NOT EXISTS user_id text;
34
34
  ALTER TABLE log.user_auth ADD COLUMN IF NOT EXISTS user_auth_date timestamp without time zone;
@@ -39,4 +39,4 @@ ALTER TABLE log.user_auth ADD COLUMN IF NOT EXISTS cdate timestamp without time
39
39
  ALTER TABLE log.user_auth ADD COLUMN IF NOT EXISTS editor_id text;
40
40
  ALTER TABLE log.user_auth ADD COLUMN IF NOT EXISTS editor_date timestamp without time zone;
41
41
  ALTER TABLE log.user_auth ADD CONSTRAINT log_user_auth_pkey PRIMARY KEY (user_auth_id);
42
- ALTER TABLE log.user_auth ADD CONSTRAINT log_user_auth_user_id_fkey FOREIGN KEY (user_id) REFERENCES admin.users (uid) MATCH SIMPLE;
42
+ -- ALTER TABLE log.user_auth ADD CONSTRAINT log_user_auth_user_id_fkey FOREIGN KEY (user_id) REFERENCES admin.users (uid) MATCH SIMPLE;
@@ -0,0 +1,27 @@
1
+ -- DROP TABLE setting.property;
2
+ CREATE TABLE IF NOT EXISTS setting.property();
3
+ ALTER TABLE setting.property DROP CONSTRAINT IF EXISTS setting_property_pkey;
4
+ ALTER TABLE setting.property ADD COLUMN IF NOT EXISTS property_id text NOT NULL DEFAULT next_id();
5
+
6
+ ALTER TABLE setting.property ADD COLUMN IF NOT EXISTS property_entity text;
7
+ COMMENT ON COLUMN setting.property.property_entity IS 'Сутність';
8
+ ALTER TABLE setting.property ADD COLUMN IF NOT EXISTS property_key text;
9
+ COMMENT ON COLUMN setting.property.property_key IS 'Ключ';
10
+ ALTER TABLE setting.property ADD COLUMN IF NOT EXISTS property_text text;
11
+ COMMENT ON COLUMN setting.property.property_text IS 'Текстове значення налаштування';
12
+ ALTER TABLE setting.property ADD COLUMN IF NOT EXISTS property_int integer;
13
+ COMMENT ON COLUMN setting.property.property_int IS 'Цілочислове значения';
14
+ ALTER TABLE setting.property ADD COLUMN IF NOT EXISTS property_json json;
15
+ COMMENT ON COLUMN setting.property.property_json IS 'Значення налаштування';
16
+ ALTER TABLE setting.property ADD COLUMN IF NOT EXISTS level text;
17
+ COMMENT ON COLUMN setting.property.level IS 'Рівень (user/system)';
18
+ ALTER TABLE setting.property ADD COLUMN IF NOT EXISTS object_id text;
19
+ COMMENT ON COLUMN setting.property.object_id IS 'ID Об''єкту';
20
+
21
+ ALTER TABLE setting.property ADD COLUMN IF NOT EXISTS uid text NOT NULL DEFAULT '1'::text;
22
+ ALTER TABLE setting.property ADD COLUMN IF NOT EXISTS editor_id text;
23
+ ALTER TABLE setting.property ADD COLUMN IF NOT EXISTS editor_date timestamp without time zone;
24
+ ALTER TABLE setting.property ADD COLUMN IF NOT EXISTS cdate timestamp without time zone DEFAULT now();
25
+ ALTER TABLE setting.property ADD COLUMN IF NOT EXISTS files json;
26
+
27
+ ALTER TABLE setting.property ADD CONSTRAINT setting_property_pkey PRIMARY KEY(property_id);
@@ -0,0 +1,24 @@
1
+ export default async function getSettingsAPI({
2
+ pg, funcs, params = {},
3
+ }) {
4
+ const { key } = params;
5
+
6
+ if (!pg?.pk?.['setting.property']) {
7
+ return { message: 'table not found', status: 404 };
8
+ }
9
+
10
+ try {
11
+ const res = await funcs.getSettings({ pg, key });
12
+ if (key) {
13
+ if (!res) {
14
+ return { message: `settings not found: ${key}`, status: 404 };
15
+ }
16
+ return { message: res, status: 200 };
17
+ }
18
+ const { rows } = res || { };
19
+ return { message: { total: rows.length || 0, rows }, status: 200 };
20
+ }
21
+ catch (err) {
22
+ return { error: err.toString(), status: 500 };
23
+ }
24
+ }
@@ -0,0 +1,14 @@
1
+ export default async function postSettingsAPI({
2
+ pg, funcs, body = {},
3
+ }) {
4
+ const { key, val } = body;
5
+ if (!key || !val) {
6
+ return { message: 'not enough params', status: 400 };
7
+ }
8
+ const res = await funcs.setSettings({
9
+ pg, funcs, key, val,
10
+ });
11
+ if (res?.error) return res;
12
+
13
+ return { message: res, status: 200 };
14
+ }
@@ -0,0 +1,12 @@
1
+ export default async function getSettings({ pg, key }) {
2
+ const sql = `select property_id as id, property_entity, property_key, property_text,
3
+ property_int, property_json, level, object_id, uid, cdate, editor_id, editor_date from setting.property
4
+ where ${key ? 'property_key=$1' : '1=1'}`;
5
+
6
+ if (!key) {
7
+ const { rows } = await pg.query(sql);
8
+ return { rows };
9
+ }
10
+ const data = await pg.one(sql, [key]);
11
+ return data;
12
+ }
@@ -0,0 +1,29 @@
1
+ const table = 'setting.property';
2
+
3
+ function checkValueType(val) {
4
+ if (val) {
5
+ if (typeof val === 'object') {
6
+ return 'property_json';
7
+ }
8
+ if (typeof val === 'number' || (!/\D/.test(val.toString()) && val.length <= 10)) {
9
+ return 'property_int';
10
+ }
11
+ }
12
+ return 'property_text';
13
+ }
14
+
15
+ export default async function setSettings({
16
+ pg, funcs, key, val,
17
+ }) {
18
+ try {
19
+ const columnType = checkValueType(val);
20
+ const data = { property_key: key, [columnType]: val };
21
+
22
+ await pg.query('delete from setting.property where property_key=$1', [key]);
23
+ const { rows } = await funcs.dataInsert({ pg, table, data });
24
+ return { key, val, data: rows };
25
+ }
26
+ catch (err) {
27
+ return { error: err.toString(), status: 500 };
28
+ }
29
+ }
@@ -0,0 +1,22 @@
1
+ import getSettingsAPI from './controllers/settings.get.js';
2
+ import postSettingsAPI from './controllers/settings.post.js';
3
+ import getSettingsFunc from './funcs/getSettings.js';
4
+ import setSettingsFunc from './funcs/setSettings.js';
5
+
6
+ async function plugin(fastify, config = { }) {
7
+ const prefix = config.prefix || '/api';
8
+ fastify.decorate('getSettings', getSettingsFunc);
9
+ fastify.decorate('setSettings', setSettingsFunc);
10
+ fastify.get(`${prefix}/settings/:key?`, {}, getSettingsAPI);
11
+
12
+ fastify.route({
13
+ method: 'POST',
14
+ path: `${prefix}/settings`,
15
+ config: {
16
+ policy: ['superadmin'],
17
+ },
18
+ handler: postSettingsAPI,
19
+ });
20
+ }
21
+
22
+ export default plugin;
@@ -1,60 +1,72 @@
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 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('') || '';
24
- const cardSqlFiltered = params.id ? cardSql?.filter?.((el) => !el?.disabled && el?.name && el?.sql?.replace) : [];
25
- 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('') || '' : '';
26
-
27
- const fData = query.filter ? await getFilterSQL({
28
- filter: query.filter,
29
- table: params.table,
30
- json: 1,
31
- }) : {};
32
-
33
- const keyQuery = query.key && loadTable.key && !params.id ? `${loadTable.key}=$1` : null;
34
-
35
- const limit = Math.min(maxLimit, +(query.limit || 10));
36
-
37
- const offset = query.page && query.page > 0 ? ` offset ${(query.page - 1) * limit}` : '';
38
- // id, query, filter
39
- const [orderColumn, orderDir] = (query.order || loadTable.order || '').split(/[- ]/);
40
-
41
- const order = cols.includes(orderColumn) && orderColumn?.length ? `order by ${orderColumn} ${query.desc || orderDir === 'desc' ? 'desc' : ''}` : '';
42
- const state = loadTable.filterState && query.state ? loadTable.filterState[query.state]?.sql : null;
43
- const custom = loadTable.filterCustom && query.custom ? loadTable.filterCustom[query.custom]?.sql : null;
44
- const search = loadTable.meta?.search && query.search ? `(${loadTable.meta?.search.split(',').map(el => `${el} ilike '%${query.search}%'`).join(' or ')})` : null;
45
-
46
- const where = [(params.id ? ` "${pk}" = $1` : null), keyQuery, loadTable.query, fData.q, state, custom, search].filter((el) => el);
47
- const cardColumns = cardSqlFiltered.length ? `,${cardSqlFiltered.map((el) => el.name)}` : '';
48
- 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}`;
49
-
50
- if (query.sql === '1') { return q; }
51
-
52
- const { rows } = await pg.query(q, (params.id ? [params.id] : null) || (query.key && loadTable.key ? [query.key] : []));
53
-
54
- 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);
55
-
56
- await metaFormat({ rows, table: params.table });
57
- return {
58
- time: Date.now() - time, total, count: rows.length, pk, form, rows, meta, columns, filters,
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 } = await getMeta(table);
21
+
22
+ const cols = columns.map((el) => el.name || el).join(',');
23
+ 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('') || '';
24
+ const cardSqlFiltered = params.id ? cardSql?.filter?.((el) => !el?.disabled && el?.name && el?.sql?.replace) : [];
25
+ 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('') || '' : '';
26
+
27
+ const fData = query.filter ? await getFilterSQL({
28
+ filter: query.filter,
29
+ table: params.table,
30
+ json: 1,
31
+ }) : {};
32
+
33
+ const keyQuery = query.key && loadTable.key && !params.id ? `${loadTable.key}=$1` : null;
34
+
35
+ const limit = Math.min(maxLimit, +(query.limit || 10));
36
+
37
+ const offset = query.page && query.page > 0 ? ` offset ${(query.page - 1) * limit}` : '';
38
+ // id, query, filter
39
+ const [orderColumn, orderDir] = (query.order || loadTable.order || '').split(/[- ]/);
40
+
41
+ const order = cols.includes(orderColumn) && orderColumn?.length ? `order by ${orderColumn} ${query.desc || orderDir === 'desc' ? 'desc' : ''}` : '';
42
+ const state = loadTable.filterState && query.state ? loadTable.filterState[query.state]?.sql : null;
43
+ const custom = loadTable.filterCustom && query.custom ? loadTable.filterCustom[query.custom]?.sql : null;
44
+ const search = loadTable.meta?.search && query.search ? `(${loadTable.meta?.search.split(',').map(el => `${el} ilike '%${query.search}%'`).join(' or ')})` : null;
45
+
46
+ const where = [(params.id ? ` "${pk}" = $1` : null), keyQuery, loadTable.query, fData.q, state, custom, search].filter((el) => el);
47
+ const cardColumns = cardSqlFiltered.length ? `,${cardSqlFiltered.map((el) => el.name)}` : '';
48
+ 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}`;
49
+
50
+ if (query.sql === '1') { return q; }
51
+
52
+ const { rows } = await pg.query(q, (params.id ? [params.id] : null) || (query.key && loadTable.key ? [query.key] : []));
53
+
54
+ const formData = form ? (await getTemplate('form', form) || {}) : {};
55
+ const extraKeys = Object.keys(formData)?.filter((key) => formData?.[key]?.type === 'DataTable' && formData?.[key]?.table && formData?.[key]?.parent_id && formData[key]?.colModel);
56
+ if (extraKeys?.length) {
57
+ await Promise.all(rows?.map(async (row) => {
58
+ await Promise.all(extraKeys?.map(async (key) => {
59
+ const { colModel, table: extraTable, parent_id: parentId } = formData[key];
60
+ const { rows: extraRows } = await pg.query(`select ${parentId} as parent, ${colModel.map((col) => col.name).join(',')} from ${extraTable} a where ${parentId}=$1`, [row.id]);
61
+ Object.assign(row, { [key]: extraRows });
62
+ }));
63
+ }));
64
+ }
65
+
66
+ 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);
67
+
68
+ await metaFormat({ rows, table: params.table });
69
+ return {
70
+ time: Date.now() - time, total, count: rows.length, pk, form, rows, meta, columns, filters,
71
+ };
72
+ }