@opengis/fastify-table 1.0.40 → 1.0.42

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.42 - 12.06.2024
4
+
5
+ - table cardSql support
6
+
7
+ ## 1.0.41 - 12.06.2024
8
+
9
+ - cls to db (crm.cls)
10
+
3
11
  ## 1.0.40 - 12.06.2024
4
12
 
5
13
  - fix widget GET API
package/index.js CHANGED
@@ -1,4 +1,6 @@
1
1
  import path from 'path';
2
+ import { readdir, readFile } from 'fs/promises';
3
+ import { existsSync } from 'fs';
2
4
 
3
5
  import fp from 'fastify-plugin';
4
6
  import config from './config.js';
@@ -12,6 +14,8 @@ import widgetPlugin from './widget/index.js';
12
14
  import crudPlugin from './crud/index.js';
13
15
  import policyPlugin from './policy/index.js';
14
16
 
17
+ import pgClients from './pg/pgClients.js';
18
+
15
19
  async function plugin(fastify, opt) {
16
20
  // console.log(opt);
17
21
  config.pg = opt.pg;
@@ -31,6 +35,27 @@ async function plugin(fastify, opt) {
31
35
  return filepath;
32
36
  });
33
37
 
38
+ fastify.addHook('onListen', async () => {
39
+ const { client } = pgClients;
40
+ if (client?.pk?.['crm.cls']) {
41
+ const clsDir = path.join(process.cwd(), 'server/templates/cls');
42
+ const files = existsSync(clsDir) ? await readdir(clsDir) : [];
43
+ if (files.length) {
44
+ const res = await Promise.all(files.map(async (filename) => {
45
+ const filepath = path.join(clsDir, filename);
46
+ const data = JSON.parse(await readFile(filepath));
47
+ return { name: path.parse(filename).name, data };
48
+ }));
49
+ await client.query('truncate table crm.cls');
50
+ const { rows } = await client.query(`insert into crm.cls(name, type)
51
+ select value->>'name', 'json' from json_array_elements($1) returning cls_id as id, name`, [JSON.stringify(res).replace(/'/g, "''")]);
52
+ rows.forEach((row) => Object.assign(row, { data: res.find((cls) => row.name === cls.name)?.data }));
53
+ const sql = `insert into crm.cls(code, name, parent)
54
+ select json_array_elements(value->'data')->>'id', json_array_elements(value->'data')->>'text', value->>'name' from json_array_elements($1)`;
55
+ await client.query(sql, [JSON.stringify(rows).replace(/'/g, "''")]);
56
+ }
57
+ }
58
+ });
34
59
  if (!fastify.funcs) {
35
60
  fastify.addHook('onRequest', async (req) => {
36
61
  req.funcs = fastify;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opengis/fastify-table",
3
- "version": "1.0.40",
3
+ "version": "1.0.42",
4
4
  "type": "module",
5
5
  "description": "core-plugins",
6
6
  "main": "index.js",
@@ -1,8 +1,37 @@
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
+
1
30
  -- crm.notifications
2
31
  -- DROP TABLE IF EXISTS crm.notifications;
3
32
  CREATE TABLE IF NOT EXISTS crm.notifications();
4
33
  ALTER TABLE crm.notifications DROP CONSTRAINT IF EXISTS crm_notifications_pkey;
5
- ALTER TABLE crm.notifications ADD COLUMN IF NOT EXISTS notification_id text NOT NULL DEFAULT admin.next_id();
34
+ ALTER TABLE crm.notifications ADD COLUMN IF NOT EXISTS notification_id text NOT NULL DEFAULT crm.next_id();
6
35
 
7
36
  ALTER TABLE crm.notifications ADD COLUMN IF NOT EXISTS notification_user_id text;
8
37
  ALTER TABLE crm.notifications ADD COLUMN IF NOT EXISTS notification_type text DEFAULT 'notify'::text;
@@ -22,7 +51,7 @@ ALTER TABLE crm.notifications ADD CONSTRAINT crm_notifications_pkey PRIMARY KEY
22
51
  -- DROP TABLE IF EXISTS crm.files;
23
52
  CREATE TABLE IF NOT EXISTS crm.files();
24
53
  ALTER TABLE crm.files DROP CONSTRAINT IF EXISTS crm_files_pkey;
25
- ALTER TABLE crm.files ADD COLUMN IF NOT EXISTS file_id text NOT NULL DEFAULT admin.next_id();
54
+ ALTER TABLE crm.files ADD COLUMN IF NOT EXISTS file_id text NOT NULL DEFAULT crm.next_id();
26
55
 
27
56
  ALTER TABLE crm.files ADD COLUMN IF NOT EXISTS uploaded_name text;
28
57
  ALTER TABLE crm.files ADD COLUMN IF NOT EXISTS entity_id text; -- object_id
@@ -48,7 +77,7 @@ ALTER TABLE crm.files ADD CONSTRAINT crm_files_pkey PRIMARY KEY (file_id);
48
77
  -- DROP TABLE IF EXISTS crm.communications;
49
78
  CREATE TABLE IF NOT EXISTS crm.communications();
50
79
  ALTER TABLE crm.communications DROP CONSTRAINT IF EXISTS crm_communications_pkey;
51
- ALTER TABLE crm.communications ADD COLUMN IF NOT EXISTS communication_id text NOT NULL DEFAULT admin.next_id();
80
+ ALTER TABLE crm.communications ADD COLUMN IF NOT EXISTS communication_id text NOT NULL DEFAULT crm.next_id();
52
81
 
53
82
  ALTER TABLE crm.communications ADD COLUMN IF NOT EXISTS entity_id text; -- object_id
54
83
  ALTER TABLE crm.communications ADD COLUMN IF NOT EXISTS entity_type text; -- table_name
@@ -74,8 +103,8 @@ ALTER TABLE crm.communications ADD CONSTRAINT crm_communications_pkey PRIMARY KE
74
103
  -- crm.checklists
75
104
  -- DROP TABLE IF EXISTS crm.checklists;
76
105
  CREATE TABLE IF NOT EXISTS crm.checklists();
77
- ALTER TABLE crm.checklists DROP CONSTRAINT IF EXISTS checklists_pkey;
78
- ALTER TABLE crm.checklists ADD COLUMN IF NOT EXISTS checklist_id text NOT NULL DEFAULT admin.next_id();
106
+ 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();
79
108
 
80
109
  ALTER TABLE crm.checklists ADD COLUMN IF NOT EXISTS entity_id text; -- object_id
81
110
  ALTER TABLE crm.checklists ADD COLUMN IF NOT EXISTS entity_type text; -- table_name
@@ -93,3 +122,24 @@ ALTER TABLE crm.checklists ADD COLUMN IF NOT EXISTS cdate timestamp without time
93
122
  ALTER TABLE crm.checklists ADD COLUMN IF NOT EXISTS editor_id text;
94
123
  ALTER TABLE crm.checklists ADD COLUMN IF NOT EXISTS editor_date timestamp without time zone;
95
124
  ALTER TABLE crm.checklists ADD CONSTRAINT crm_checklists_pkey PRIMARY KEY (checklist_id);
125
+
126
+ -- crm.cls
127
+ -- DROP TABLE IF EXISTS crm.cls;
128
+ 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();
130
+
131
+ ALTER TABLE crm.cls ADD COLUMN IF NOT EXISTS name text;
132
+ ALTER TABLE crm.cls ADD COLUMN IF NOT EXISTS data text;
133
+ ALTER TABLE crm.cls ADD COLUMN IF NOT EXISTS type text;
134
+ ALTER TABLE crm.cls ADD COLUMN IF NOT EXISTS parent text;
135
+ ALTER TABLE crm.cls ADD COLUMN IF NOT EXISTS code text;
136
+ ALTER TABLE crm.cls ADD COLUMN IF NOT EXISTS color text;
137
+ ALTER TABLE crm.cls ADD COLUMN IF NOT EXISTS icon text;
138
+
139
+ ALTER TABLE crm.cls ADD COLUMN IF NOT EXISTS uid text;
140
+ ALTER TABLE crm.cls ADD COLUMN IF NOT EXISTS files json;
141
+ ALTER TABLE crm.cls ADD COLUMN IF NOT EXISTS cdate timestamp without time zone DEFAULT (now())::timestamp without time zone;
142
+ ALTER TABLE crm.cls ADD COLUMN IF NOT EXISTS editor_id text;
143
+ ALTER TABLE crm.cls ADD COLUMN IF NOT EXISTS editor_date timestamp without time zone;
144
+ ALTER TABLE crm.cls ADD CONSTRAINT crm_cls_pkey PRIMARY KEY (cls_id);
145
+ ALTER TABLE crm.cls ADD CONSTRAINT crm_cls_unique UNIQUE (code, parent);
@@ -1,57 +1,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, 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
-
25
- const fData = query.filter ? await getFilterSQL({
26
- filter: query.filter,
27
- table: params.table,
28
- json: 1,
29
- }) : {};
30
-
31
- const keyQuery = query.key && loadTable.key && !params.id ? `${loadTable.key}=$1` : null;
32
-
33
- const limit = Math.min(maxLimit, +(query.limit || 10));
34
-
35
- const offset = query.page && query.page > 0 ? ` offset ${(query.page - 1) * limit}` : '';
36
- // id, query, filter
37
- const [orderColumn, orderDir] = (query.order || loadTable.order || '').split(/[- ]/);
38
-
39
- const order = cols.includes(orderColumn) && orderColumn?.length ? `order by ${orderColumn} ${query.desc || orderDir === 'desc' ? 'desc' : ''}` : '';
40
- const state = loadTable.filterState && query.state ? loadTable.filterState[query.state]?.sql : null;
41
- const custom = loadTable.filterCustom && query.custom ? loadTable.filterCustom[query.custom]?.sql : null;
42
- const search = loadTable.meta?.search && query.search ? `(${loadTable.meta?.search.split(',').map(el => `${el} ilike '%${query.search}%'`).join(' or ')})` : null;
43
-
44
- const where = [(params.id ? ` "${pk}" = $1` : null), keyQuery, loadTable.query, fData.q, state, custom, search].filter((el) => el);
45
- const q = `select ${pk ? `"${pk}" as id,` : ''} ${query.id || query.key ? '*' : cols || '*'} from ${table} t ${sqlTable} where ${where.join(' and ') || 'true'} ${order} ${offset} limit ${limit}`;
46
-
47
- if (query.sql === '1') { return q; }
48
-
49
- const { rows } = await pg.query(q, (params.id ? [params.id] : null) || (query.key && loadTable.key ? [query.key] : []));
50
-
51
- 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);
52
-
53
- await metaFormat({ rows, table: params.table });
54
- return {
55
- time: Date.now() - time, total, count: rows.length, pk, form, rows, meta, columns, filters,
56
- };
57
- }
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
+ }