@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 +8 -0
- package/index.js +25 -0
- package/package.json +1 -1
- package/server/migrations/crm.sql +55 -5
- package/table/controllers/data.js +60 -57
package/Changelog.md
CHANGED
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,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
|
|
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
|
|
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
|
|
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
|
|
78
|
-
ALTER TABLE crm.checklists ADD COLUMN IF NOT EXISTS checklist_id text NOT NULL DEFAULT
|
|
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
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
const
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
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
|
+
}
|