@opengis/admin 0.1.59 → 0.1.61

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,228 +1,152 @@
1
- import fp from 'fastify-plugin';
2
- import fs from 'node:fs';
3
- import path from 'node:path';
4
-
5
- import { addHook, getTemplatePath, getTemplate, pgClients } from '@opengis/fastify-table/utils.js';
6
-
7
- import getMenu from '../routes/menu/controllers/getMenu.js';
8
-
9
- import config from '../../config.js';
10
-
11
- // to export the decorators to the outer scope
12
-
13
- async function plugin(fastify) {
14
- fastify.decorate('config', config);
15
-
16
- addHook('afterTemplate', async ({ req, data = {} }) => {
17
- const { uid } = req.session?.passport?.user || {};
18
- if (!uid || !data || req.params?.type !== 'form' || !req.params?.name) return null;
19
- const { pg, params = {} } = req;
20
- const { rows: properties = [] } = await pg.query(`select name, title, format, data from admin.custom_column
21
- where entity=$1 and uid=$2`, [params.name?.replace('.form', '.table'), uid]);
22
- await Promise.all(properties.map(async (el) => {
23
- const clsData = el.data ? await getTemplate(['cls', 'select'], el.data) : undefined;
24
- const type = clsData ? 'Select' : ({ date: 'DatePicker' }[el.format] || 'Text');
25
- Object.assign(data?.schema || data || {}, { [el.name]: { type, ua: el.title, data: el.data, options: type === 'Select' && Array.isArray(clsData) && clsData?.length ? clsData : undefined, extra: 1 } });
26
- }));
27
- });
28
-
29
- addHook('afterUpdate', async ({ req, res = {} }) => {
30
- const {
31
- pg, session = {}, params = {}, body = {},
32
- } = req;
33
- const { uid } = session?.passport?.user || {};
34
- if (!uid) return null;
35
-
36
- const loadTable = await getTemplate('table', params.table);
37
- if (!pg.pk[loadTable?.table || params.table]) return null;
38
- const pk = pg.pk[loadTable?.table || params.table];
39
- const id = res[pk];
40
-
41
- const { rows: properties = [] } = await pg.query(`select column_id, name, title, format, data from admin.custom_column
42
- where entity=$1 and uid=$2`, [params.table, uid]);
1
+ import { addHook, getToken, setToken, getTemplate } from '@opengis/fastify-table/utils.js';
2
+
3
+ export default async function plugin(fastify) {
4
+
5
+ addHook('preTable', async ({ req }) => {
6
+ if (!req?.routeOptions?.method) return;
7
+ const mode = { POST: 'a', DELETE: 'w', PUT: 'w', GET: 'w' }[req.routeOptions.method] || 'w';
8
+ const { funcs, params = {}, session = {} } = req;
9
+ const { uid } = funcs?.config?.auth?.disable ? { uid: '1' } : (session?.passport?.user || {});
10
+ const opt = await getToken({
11
+ uid, token: params.id || params.form || params.table, mode, json: 1,
12
+ });
13
+ if (opt) Object.assign(req, { opt });
14
+ if (!opt && !funcs?.config?.local) {
15
+ return { message: 'access restricted: edit', status: 403 };
16
+ }
17
+ return null;
18
+ });
19
+
20
+ addHook('afterTable', async ({ req = {}, res = {}, rows = [], table }) => {
21
+ const { pg, funcs, params = {}, session = {} } = req;
22
+ const { uid } = funcs?.config?.auth?.disable ? { uid: '1' } : (session?.passport?.user || {});
23
+ if (!uid || !table || !pg?.pk?.[table] || !rows.length || !params.table) return;
24
+
25
+ // admin.custom_column - user column data
26
+ const { rows: properties = [] } = await pg.query(`select column_id, name, title, format, data from admin.custom_column
27
+ where _table and entity=$1 and uid=$2`, [params.table, uid]);
28
+ const extraColumnList = properties.map((row) => ({ id: row.column_id, name: row.name, title: row.title, format: row.format, data: row.data }));
29
+ if (!extraColumnList?.length) return;
30
+
31
+ if (res?.columns?.length) {
32
+ extraColumnList.forEach((col) => res.columns.push(col));
33
+ }
43
34
 
44
- if (!id || !properties?.length) return null;
35
+ const { rows: extraData = [] } = await pg.query(`select object_id, json_object_agg( property_id, coalesce(value_date::text,value_text) ) as extra from crm.extra_data
36
+ where property_entity=$1 and property_id=any($2) and object_id=any($3) group by object_id`, [params.table, extraColumnList?.map((el) => el.id), rows.map((el) => el.id)]);
45
37
 
46
- const q = `delete from crm.extra_data where property_entity='${params.table}' and object_id='${id}';${properties
47
- .filter((el) => Object.keys(body).includes(el.name))
48
- .map((el) => `insert into crm.extra_data(property_id,property_key,property_entity,object_id,${el.format?.toLowerCase() === 'date' ? 'value_date' : 'value_text'})
38
+ if (!extraData?.length) {
39
+ // Object.assign(rows?.[0] || {}, { ...extraColumnList.reduce((acc, curr) => Object.assign(acc, { [curr.name]: null }), {}) });
40
+ return;
41
+ }
42
+
43
+ rows.filter((row) => extraData.map((el) => el?.object_id).includes(row.id)).forEach((row) => {
44
+ const { extra = {} } = extraData.find((el) => el.object_id === row.id);
45
+ Object.assign(row, { ...Object.fromEntries(Object.entries(extra).map((el) => [extraColumnList.find((col) => col.id === el[0]).name, el[1]])) });
46
+ });
47
+
48
+ // admin.custom_column - metaFormat
49
+ await Promise.all(extraColumnList.filter((el) => el?.data).map(async (attr) => {
50
+ const values = [...new Set(rows?.map((el) => el[attr.name]).flat())].filter((el) => el);
51
+ if (!values.length) return;
52
+ const cls = await getSelectVal({ name: attr.data, values });
53
+ if (!cls) return;
54
+ rows.forEach(el => {
55
+ const val = el[attr.name]?.map?.(c => cls[c] || c) || cls[el[attr.name]] || el[attr.name];
56
+ if (!val) return;
57
+ Object.assign(el, { [val?.color ? `${attr.name}_data` : `${attr.name}_text`]: (val.color ? val : val.text || val) });
58
+ });
59
+ }));
60
+ });
61
+
62
+ // extract table from form token for user columns - p.2 - read (refactor to global token)
63
+ /* addHook('preTemplate', async ({ req = {} }) => {
64
+ const { funcs, params = {}, session = {} } = req;
65
+ const { uid } = funcs?.config?.auth?.disable ? { uid: '1' } : (session?.passport?.user || {});
66
+ if (!uid || params?.type !== 'form' || !params?.name) return null;
67
+
68
+ const { table, form } = await getToken({
69
+ uid, token: params.name, mode: 'w', json: 1,
70
+ }) || {};
71
+ if (form) {
72
+ Object.assign(req.params || {}, { name: form, table });
73
+ }
74
+ }); */
75
+
76
+ addHook('afterTemplate', async ({ req = {}, data = {} }) => {
77
+ const { funcs, session = {} } = req;
78
+ const { uid } = funcs?.config?.auth?.disable ? { uid: '1' } : (session?.passport?.user || {});
79
+ const { pg, params = {} } = req;
80
+ // extract table from form token for user columns - p.1 - assign (refactor to global token)
81
+ /* if (params?.type === 'table') {
82
+ const { form } = await getTemplate('table', params.name) || {};
83
+ if (!form) return;
84
+ const editTokens = setToken({
85
+ ids: [{ table: params.name, form }],
86
+ mode: 'w',
87
+ uid,
88
+ array: 1,
89
+ });
90
+ Object.assign(data, { token: editTokens[0] });
91
+ } */
92
+ if (!uid || !data || params?.type !== 'form' || !params?.name) return null;
93
+
94
+ const { rows: properties = [] } = await pg.query(`select name, title, format, data from admin.custom_column
95
+ where entity=$1 and uid=$2`, [params.table || params.name, uid]);
96
+ await Promise.all(properties.map(async (el) => {
97
+ const clsData = el.data ? await getTemplate(['cls', 'select'], el.data) : undefined;
98
+ const type = clsData ? 'Select' : ({ date: 'DatePicker' }[el.format] || 'Text');
99
+ Object.assign(data?.schema || data || {}, { [el.name]: { type, ua: el.title, data: el.data, options: type === 'Select' && Array.isArray(clsData) && clsData?.length ? clsData : undefined, extra: 1 } });
100
+ }));
101
+ });
102
+
103
+ addHook('afterUpdate', async ({ req, res = {} }) => {
104
+ const {
105
+ pg, funcs, session = {}, params = {}, body = {},
106
+ } = req;
107
+ const { uid } = funcs?.config?.auth?.disable ? { uid: '1' } : (session?.passport?.user || {});
108
+ if (!uid) return null;
109
+
110
+ const loadTable = await getTemplate('table', params.table);
111
+ if (!pg.pk[loadTable?.table || params.table]) return null;
112
+ const pk = pg.pk[loadTable?.table || params.table];
113
+ const id = res[pk];
114
+
115
+ const { rows: properties = [] } = await pg.query(`select column_id, name, title, format, data from admin.custom_column
116
+ where entity=$1 and uid=$2`, [params.table, uid]);
117
+
118
+ if (!id || !properties?.length) return null;
119
+
120
+ const q = `delete from crm.extra_data where property_entity='${params.table}' and object_id='${id}';${properties
121
+ .filter((el) => Object.keys(body).includes(el.name))
122
+ .map((el) => `insert into crm.extra_data(property_id,property_key,property_entity,object_id,${el.format?.toLowerCase() === 'date' ? 'value_date' : 'value_text'})
49
123
  select '${el.column_id}', '${el.name}', '${params.table}', '${id}', ${el.format?.toLowerCase() === 'date' ? `'${body[el.name]}'::timestamp without time zone` : `'${body[el.name]}'::text`}`)
50
- .join(';\n') || ''}`;
51
- return pg.query(q);
52
- });
53
-
54
- addHook('afterInsert', async ({ req, res = {} }) => {
55
- const {
56
- pg, session = {}, params = {}, body = {},
57
- } = req;
58
- const { uid } = session?.passport?.user || {};
59
- if (!uid) return null;
60
-
61
- const loadTable = await getTemplate('table', params.table);
62
- if (!pg.pk[loadTable?.table || params.table]) return null;
63
- const pk = pg.pk[loadTable?.table || params.table];
64
- const id = res.rows?.[0]?.[pk];
65
-
66
- const { rows: properties = [] } = await pg.query(`select column_id, name, title, format, data from admin.custom_column
124
+ .join(';\n') || ''}`;
125
+ return pg.query(q);
126
+ });
127
+
128
+ addHook('afterInsert', async ({ req, res = {} }) => {
129
+ const {
130
+ pg, funcs, session = {}, params = {}, body = {},
131
+ } = req;
132
+ const { uid } = funcs?.config?.auth?.disable ? { uid: '1' } : (session?.passport?.user || {});
133
+ if (!uid) return null;
134
+
135
+ const loadTable = await getTemplate('table', params.table);
136
+ if (!pg.pk[loadTable?.table || params.table]) return null;
137
+ const pk = pg.pk[loadTable?.table || params.table];
138
+ const id = res.rows?.[0]?.[pk];
139
+
140
+ const { rows: properties = [] } = await pg.query(`select column_id, name, title, format, data from admin.custom_column
67
141
  where entity=$1 and uid=$2`, [params.table, uid]);
68
142
 
69
- if (!id || !properties?.length) return null;
143
+ if (!id || !properties?.length) return null;
70
144
 
71
- const q = properties
72
- .filter((el) => Object.keys(body).includes(el.name))
73
- .map((el) => `insert into crm.extra_data(property_id,property_key,property_entity,object_id,${el.format?.toLowerCase() === 'date' ? 'value_date' : 'value_text'})
145
+ const q = properties
146
+ .filter((el) => Object.keys(body).includes(el.name))
147
+ .map((el) => `insert into crm.extra_data(property_id,property_key,property_entity,object_id,${el.format?.toLowerCase() === 'date' ? 'value_date' : 'value_text'})
74
148
  select '${el.column_id}', '${el.name}', '${params.table}', '${id}', ${el.format?.toLowerCase() === 'date' ? `'${body[el.name]}'::timestamp without time zone` : `'${body[el.name]}'::text`}`)
75
- .join(';\n');
76
- return pg.query(q);
77
- });
78
-
79
- fastify.addHook('onListen', async () => {
80
- const { client } = pgClients;
81
- const json = await getMenu();
82
- // insert interface list to db (user access management)
83
- if (client?.pk?.['admin.routes'] && json?.length) {
84
- const menuList = json.filter((el) => el?.menu?.length && el?.ua || el?.en || el?.name);
85
- const interfaces = menuList.reduce((acc, curr) => { curr.menu.forEach((el) => acc.push(el.path)); return acc; }, []);
86
- await client.query('update admin.routes set enabled=false where not array[route_id] <@ $1::text[]', [interfaces]);
87
-
88
- const q = `insert into admin.menu(name, ord) values${menuList.map((el, i) => `('${(el?.ua || el?.en || el?.name).replace(/'/g, '’')}', ${i}) `).join(',')
89
- } on conflict (name) do update set ord=excluded.ord, enabled=true returning name, menu_id`;
90
- const { rows = [] } = await client.query(q);
91
- await client.query('update admin.menu set enabled=false where not array[menu_id] <@ $1::text[]', [rows.map((el) => el.menu_id)]);
92
-
93
- const menus = rows.reduce((acc, curr) => Object.assign(acc, { [curr.menu_id]: menuList.find((item) => (item?.ua || item?.en || item?.name) === curr.name) }), {});
94
- const values = Object.entries(menus).reduce((acc, curr) => { curr[1]?.menu?.forEach((el) => acc.push({ ...el, menuId: curr[0] })); return acc; }, []);
95
-
96
- await Promise.all(values.filter((el) => el?.table).map(async (el) => Object.assign(el, { table: (await getTemplate('table', el.table))?.table || el.table })));
97
-
98
- const q1 = `insert into admin.routes(route_id, alias, title, menu_id, table_name) values ${values.map((el) => `('${el.path}', '${el.path}', '${el.title}', '${el.menuId}', '${el.table}')`).join(',')}
99
- on conflict (route_id) do update set menu_id=excluded.menu_id, title=excluded.title, enabled=true,
100
- table_name=excluded.table_name returning route_id, table_name`;
101
- try {
102
- const { rowCount } = await client.query(q1);
103
- // console.log('interface insert ok', values, rowCount);
104
- } catch (err) {
105
- console.log('interface insert error', values, q1, err);
106
- }
107
- }
108
- });
109
-
110
- fastify.addHook('onListen', async () => {
111
- const { client: pg } = pgClients;
112
- const clsQuery = [];
113
- if (!pg.pk?.['admin.cls']) return;
114
-
115
- const selectList = await getTemplatePath('select');
116
- const clsList = (await getTemplatePath('cls'))?.filter((el) => !(selectList?.map((el) => el?.[0]) || []).includes(el[0]));
117
- const cls = (selectList || []).concat(clsList || [])
118
- ?.map((el) => ({ name: el[0], module: path.basename(path.dirname(path.dirname(el[1]))), type: { 'json': 'cls', 'sql': 'select' }[el[2]] }))
119
- if (!cls?.length) return;
120
-
121
- const dupes = cls.filter((el, idx, arr) => arr.map((item) => item.name).indexOf(el.name) !== idx);
122
- // console.log('cls insert skip dupes', dupes.map((el) => el.name));
123
-
124
- try {
125
- await Promise.all(cls.filter((el, idx, arr) => arr.map((item) => item.name).indexOf(el.name) === idx).map(async (el) => {
126
- const { name, module, type } = el;
127
- const loadTemplate = await getTemplate(type, name);
128
- //console.log(name, type);
129
- if (type === 'select') {
130
- clsQuery.push(`insert into admin.cls(name,type,data,module) values('${name}','sql','${(loadTemplate?.sql || loadTemplate)?.replace(/'/g, "''")}', '${module?.replace(/'/g, "''")}')`);
131
- } else if (type === 'cls' && loadTemplate?.length) {
132
- clsQuery.push(`insert into admin.cls(name,type, module) values('${name}','json', '${module?.replace(/'/g, "''")}');
133
- insert into admin.cls(code,name,parent,icon)
134
- select value->>'id',value->>'text','${name}',value->>'icon'
135
- from json_array_elements('${JSON.stringify(loadTemplate).replace(/'/g, "''")}'::json)`);
136
- } else {
137
- console.log(name, type, 'empty');
138
- }
139
- }));
140
-
141
- await pg.query('truncate admin.cls');
142
- if (clsQuery.filter((el) => el).length) {
143
- await pg.query(clsQuery.filter((el) => el).join(';'));
144
- console.log('cls insert ok', clsQuery?.length);
145
- }
146
- } catch (err) {
147
- console.error('cls insert error', err.toString());
148
- }
149
- });
150
-
151
- // pre Request
152
- fastify.addHook('onRequest', async (req) => {
153
- req.funcs = fastify;
154
- const { user } = req.session?.passport || {};
155
- req.user = user;
156
- });
157
-
158
- // preSerialization
159
- fastify.addHook('preSerialization', async (req, reply, payload) => {
160
- if (req.url.includes('/suggest/') && !req.query.json) {
161
- return payload?.data;
162
- }
163
- if (payload?.redirect) {
164
- return reply.redirect(payload.redirect);
165
- }
166
- if (reply.sent) {
167
- return null;
168
- }
169
-
170
- if (['200', '400', '500', '403', '404'].includes(payload?.status)) {
171
- reply.status(payload.status);
172
- }
173
- /* if (payload.headers) {
174
- reply.headers(payload.headers);
175
- } */
176
- if (payload?.buffer) {
177
- return payload.buffer;
178
- }
179
- if (payload?.file) {
180
- // const buffer = await readFile(payload.file);
181
- // return reply.send(buffer);
182
- const stream = fs.createReadStream(payload.file);
183
- return stream;
184
- // return reply.send(stream);
185
- }
186
-
187
- if (payload?.message) {
188
- return payload.message;
189
- }
190
- return payload;
191
- });
192
-
193
- // preValidation
194
- fastify.addHook('preValidation', async (req) => {
195
- const parseRawBody = ['POST', 'PUT'].includes(req.method) && req.body && typeof req.body === 'string'
196
- && req.body.trim(/\r\n/g).startsWith('{')
197
- && req.body.trim(/\r\n/g).endsWith('}');
198
- if (parseRawBody) {
199
- try {
200
- req.body = JSON.parse(req.body || '{}');
201
- }
202
- catch (err) {
203
- // throw new Error('invalid body');
204
- // return { error: 'invalid body', status: 400 };
205
- }
206
- }
207
- });
208
-
209
- // allow upload file
210
- const kIsMultipart = Symbol.for('[FastifyMultipart.isMultipart]');
211
- fastify.addContentTypeParser('multipart', (request, _, done) => {
212
- request[kIsMultipart] = true;
213
- done(null);
214
- });
215
-
216
- // parse Body
217
- function contentParser(req, body, done) {
218
- const parseBody = decodeURIComponent(body.toString()).split('&').reduce((acc, el) => {
219
- const [key, val] = el.split('=');
220
- return { ...acc, [key]: val };
221
- }, {});
222
- done(null, parseBody);
223
- }
224
-
225
- fastify.addContentTypeParser('application/x-www-form-urlencoded', { parseAs: 'buffer' }, contentParser);
226
- }
227
-
228
- export default fp(plugin);
149
+ .join(';\n');
150
+ return pg.query(q);
151
+ });
152
+ }
@@ -2,9 +2,10 @@ import { setToken, getTemplate } from "@opengis/fastify-table/utils.js";
2
2
  // import yaml from 'js-yaml';
3
3
  import getTableData from "./tableData.js";
4
4
 
5
- export default async function getCardData({
6
- pg, funcs, params = {}, session = {},
7
- }) {
5
+ export default async function getCardData(req) {
6
+ const {
7
+ pg, funcs, params = {}, session = {},
8
+ } = req;
8
9
  const { table, id } = params;
9
10
  const { uid } = session.passport?.user || {};
10
11
 
@@ -40,7 +41,7 @@ export default async function getCardData({
40
41
  }
41
42
 
42
43
  const data = {};
43
- const { message, rows = [] } = await getTableData({ pg, funcs, params: { table, id }, session, mode: 'card' });
44
+ const { message, rows = [] } = await getTableData({ pg, funcs, params: { table, id }, session });
44
45
 
45
46
  if (message) return { message };
46
47
 
@@ -54,7 +54,7 @@ function formatValue({
54
54
  const min = formatDateISOString(startDate);
55
55
  const max = formatDateISOString(endDate);
56
56
  const query = extra && pk
57
- ? `${pk} in (select object_id from setting.extra_data where property_key='${name}' and value_date::date >= '${min}'::date and value_date::date <= '${max}'::date)`
57
+ ? `${pk} in (select object_id from crn.extra_data where property_key='${name}' and value_date::date >= '${min}'::date and value_date::date <= '${max}'::date)`
58
58
  : `${name}::date >= '${min}'::date and ${name}::date <= '${max}'::date`;
59
59
  return { op: 'between', query, extra };
60
60
  }
@@ -93,8 +93,8 @@ function formatValue({
93
93
  const values = value.split(',').filter((el) => el !== 'null');
94
94
  if (extra && pk) {
95
95
  const query = value?.indexOf('null') !== -1
96
- ? `${pk} in (select object_id from setting.extra_data where property_key='${name}' and ( value_text is null or value_text in (${values?.map((el) => `'"${el}"'`).join(',')}) ) )`
97
- : `${pk} in (select object_id from setting.extra_data where property_key='${name}' and value_text in (${values?.map((el) => `'"${el}"'`).join(',')}) )`;
96
+ ? `${pk} in (select object_id from crm.extra_data where property_key='${name}' and ( value_text is null or value_text in (${values?.map((el) => `'"${el}"'`).join(',')}) ) )`
97
+ : `${pk} in (select object_id from crm.extra_data where property_key='${name}' and value_text in (${values?.map((el) => `'"${el}"'`).join(',')}) )`;
98
98
  return { op: 'in', query, extra };
99
99
  }
100
100
  const query = value?.indexOf('null') !== -1
@@ -136,8 +136,8 @@ function formatValue({
136
136
  const match = operator1 === '=' ? `='${value}'` : `ilike '%${value}%'`;
137
137
  if (extra && pk) {
138
138
  const query = data && sql
139
- ? `${pk} in (select object_id from setting.extra_data where property_key='${name}' and value_text in ( ( with q(id,name) as (${sql}) select id from q where name ${match})))`
140
- : `${pk} in (select object_id from setting.extra_data where property_key='${name}' and value_text ${match})`
139
+ ? `${pk} in (select object_id from crm.extra_data where property_key='${name}' and value_text in ( ( with q(id,name) as (${sql}) select id from q where name ${match})))`
140
+ : `${pk} in (select object_id from crm.extra_data where property_key='${name}' and value_text ${match})`
141
141
  return { op: 'ilike', query, extra };
142
142
  }
143
143
 
@@ -1,5 +1,5 @@
1
1
  import {
2
- getMeta, getAccess, getTemplate, metaFormat, getSelectVal, gisIRColumn,
2
+ applyHook, getMeta, getAccess, getTemplate, metaFormat, getTableColumn,
3
3
  } from "@opengis/fastify-table/utils.js";
4
4
 
5
5
  import getFilterSQL from "./funcs/getFilterSQL/index.js";
@@ -36,9 +36,8 @@ export default async function getTableData(req) {
36
36
  return { message: `table not found: ${table}`, status: 404 };
37
37
  }
38
38
 
39
- const { cols, columnList, extraKeys, schema } = await getColumns({
40
- pg, columns, params, opt, loadTable, form, table, dbColumns, mode, uid,
41
- });
39
+ const columnList = dbColumns.map((el) => el.name || el).join(',');
40
+ const cols = columns.filter((el) => columnList.includes(el?.name) && el?.name !== 'geom').map((el) => el?.name || el).join(',');
42
41
 
43
42
  const metaCols = Object.keys(loadTable?.meta?.cls || {}).filter((el) => !cols.includes(el)).length
44
43
  ? `,${Object.keys(loadTable?.meta?.cls || {})?.filter((el) => !cols.includes(el)).join(',')}`
@@ -47,7 +46,7 @@ export default async function getTableData(req) {
47
46
  const sqlTable = sql?.filter?.((el) => !el?.disabled && el?.sql?.replace).map((el, i) => ` left join lateral (${el.sql.replace('{{uid}}', uid)}) ${el.name || `t${i}`} on 1=1 `)?.join('') || '';
48
47
 
49
48
  if (params.id && columnList.includes(params.id)) {
50
- return gisIRColumn({
49
+ return getTableColumn({
51
50
  pg, funcs, layer: params.table, column: params.id, sql: query.sql,
52
51
  });
53
52
  }
@@ -95,47 +94,15 @@ export default async function getTableData(req) {
95
94
 
96
95
  const total = keyQuery || opt?.id || params.id ? rows.length : await pg.queryCache(`select count(*) from ${table} t ${sqlTable} where ${where.join(' and ') || 'true'}`).then((el) => (el?.rows[0]?.count || 0) - 0);
97
96
 
98
- // form DataTable
99
- if (extraKeys?.length && mode === 'card') {
100
- await Promise.all(rows?.map(async (row) => {
101
- await Promise.all(extraKeys?.map(async (key) => {
102
- const { colModel, table: extraTable, parent_id: parentId } = schema[key];
103
- const { rows: extraRows } = await pg.query(`select ${parentId} as parent, ${colModel.map((col) => col.name).join(',')} from ${extraTable} a where ${parentId}=$1`, [row.id]);
104
- Object.assign(row, { [key]: extraRows });
105
- }));
106
- }));
107
- }
108
-
109
- // admin.custom_column - user column data
110
- const { rows: properties = [] } = await pg.query(`select column_id, name, title, format, data from admin.custom_column
111
- where _table and entity=$1 and uid=$2`, [params.table, uid]);
112
- const extraColumnList = properties.map((row) => ({ id: row.column_id, name: row.name, title: row.title, format: row.format, data: row.data }));
113
- extraColumnList.forEach((col) => columns.push(col));
114
- const { rows: extraData = [] } = await pg.query(`select object_id, json_object_agg( property_id, coalesce(value_date::text,value_text) ) as extra from crm.extra_data
115
- where property_entity=$1 and property_id=any($2) and object_id=any($3) group by object_id`, [params.table, extraColumnList?.map((el) => el.id), rows.map((el) => el.id)]);
116
- rows.filter((row) => extraData.map((el) => el?.object_id).includes(row.id)).forEach((row) => {
117
- const { extra = {} } = extraData.find((el) => el.object_id === row.id);
118
- Object.assign(row, { ...Object.fromEntries(Object.entries(extra).map((el) => [extraColumnList.find((col) => col.id === el[0]).name, el[1]])) });
119
- });
120
-
121
- // admin.custom_column - metaFormat
122
- await Promise.all(extraColumnList.filter((el) => el?.data).map(async (attr) => {
123
- const values = [...new Set(rows?.map((el) => el[attr.name]).flat())].filter((el) => el);
124
- if (!values.length) return;
125
- const cls = await getSelectVal({ name: attr.data, values });
126
- if (!cls) return;
127
- rows.forEach(el => {
128
- const val = el[attr.name]?.map?.(c => cls[c] || c) || cls[el[attr.name]] || el[attr.name];
129
- if (!val) return;
130
- Object.assign(el, { [val?.color ? `${attr.name}_data` : `${attr.name}_text`]: (val.color ? val : val.text || val) });
131
- });
132
- }));
133
-
134
- await metaFormat({ funcs, rows, table: params.table });
135
97
 
136
98
  const res = {
137
99
  time: Date.now() - time, card: loadTable.card, actions: loadTable.actions, access, total, count: rows.length, pk, form, meta, columns,
138
100
  };
101
+
102
+ await applyHook('afterTable', { req, res, table, rows });
103
+ await metaFormat({ funcs, rows, table: params.table });
104
+
105
+
139
106
  const addToken = assignTokens({ rows, funcs, ispublic, uid: 1, loadTable });
140
107
  Object.assign(res, { rows, addToken });
141
108
 
@@ -15,7 +15,7 @@ export default async function tableFilter(req) {
15
15
  return { message: 'table not found', status: 404 };
16
16
  }
17
17
 
18
- const { columns } = await funcs.getMeta({ table: loadTable.table });
18
+ const { columns = [] } = await funcs.getMeta({ table: loadTable.table });
19
19
 
20
20
  const filters = loadTable?.filter_list || loadTable?.filters || loadTable?.filterList || [];
21
21
  filters.forEach(el => {
@@ -32,9 +32,9 @@ export default async function tableFilter(req) {
32
32
  const name = el.name || el.id;
33
33
 
34
34
  if (!cls?.length || !Array.isArray(cls) || !loadTable.table || !name) return;
35
- const { dataTypeID } = columns.find((item) => item.name === name) || {};
35
+ const { dataTypeID } = columns.find((item) => item?.name === name) || {};
36
36
  if (el.extra && el.type === 'select' && Array.isArray(cls)) {
37
- const countArr = await pg.query(`select value_text as id, count(*) from setting.extra_data where property_key='${name}' and property_entity='${params.name}' group by value_text`);
37
+ const countArr = await pg.query(`select value_text as id, count(*) from crm.extra_data where property_key='${name}' and property_entity='${params.name}' group by value_text`);
38
38
  const options = countArr.rows.map(cel => {
39
39
  const data = cls.find(c => c.id === cel.id);
40
40
  return { ...cel, ...data };
@@ -4,19 +4,6 @@ export default async function getColumns({
4
4
  columns = [], params = {}, opt = {}, loadTable = {}, form, table, dbColumns = [], mode = 'table',
5
5
  }) {
6
6
  const columnList = dbColumns.map((el) => el.name || el).join(',');
7
- if (mode === 'table') {
8
- const cols = columns.filter((el) => columnList.includes(el?.name) && el?.name !== 'geom').map((el) => el?.name || el).join(',');
9
- return { cols, columnList };
10
- }
11
- // card / edit form
12
- const schema = await getTemplate('form', opt?.form || form) || {};
13
- // skip DataTable from another table
14
- const extraKeys = Object.keys(schema)?.filter((key) => schema[key]?.type === 'DataTable' && schema[key]?.table && schema[key]?.parent_id && schema[key]?.colModel?.length);
15
- // skip non-existing columns
16
- const { fields = [] } = !loadTable?.table ? await pg.query(`select * from ${table || opt?.table || params.table} limit 0`) : {};
17
- const cols = loadTable?.table
18
- ? Object.keys(schema || {}).filter((col) => columnList.includes(col) && !extraKeys.includes(col))?.join(',')
19
- : fields.map((el) => (el?.name?.includes('geom') ? `st_asgeojson(${el.name})::json as "${el.name}"` : `"${el?.name}"`)).join(',');
20
-
21
- return { cols, columnList, extraKeys, schema };
7
+ const cols = columns.filter((el) => columnList.includes(el?.name) && el?.name !== 'geom').map((el) => el?.name || el).join(',');
8
+ return { cols, columnList };
22
9
  }
@@ -1,8 +1,12 @@
1
1
  import { applyHook, getTemplate } from "@opengis/fastify-table/utils.js";
2
2
 
3
3
  export default async function getTemplateApi(req) {
4
- const { funcs, params, session = {} } = req;
4
+ const res = await applyHook('preTemplate', { req });
5
+ if (res) return res;
6
+
7
+ const { funcs, params = {}, session = {} } = req;
5
8
  const { type, name } = params;
9
+
6
10
  const data = await getTemplate(type, name);
7
11
  if (!['interface', 'table'].includes(type)
8
12
  && (!data?.public || !data?.ispublic)