@opengis/admin 0.1.59 → 0.1.60
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/dist/{add-page-BOXd-uV_.js → add-page-C3Wh2-Ml.js} +13 -11
- package/dist/admin-interface-C4m7uvG2.js +896 -0
- package/dist/admin-view-YtM2-LrW.js +225 -0
- package/dist/admin.js +1 -1
- package/dist/admin.umd.cjs +95 -207
- package/dist/card-page-WrYs8chM.js +133 -0
- package/dist/{card-view-NLu0N8TA.js → card-view-BwtqUSFP.js} +1 -1
- package/dist/{edit-page-z7ozrw2C.js → edit-page-CMTqsvJf.js} +34 -33
- package/dist/import-file-bdYAo8iW.js +29759 -0
- package/dist/style.css +1 -1
- package/dist/userMenu-CT1xO2Pt.js +5 -0
- package/module/settings/select/core.roles.json +2 -2
- package/package.json +4 -4
- package/plugin.js +5 -69
- package/server/plugins/adminHook.js +165 -0
- package/server/plugins/cron.js +11 -0
- package/server/plugins/hook.js +145 -221
- package/server/routes/data/controllers/cardData.js +5 -4
- package/server/routes/data/controllers/funcs/getFilterSQL/util/formatValue.js +5 -5
- package/server/routes/data/controllers/tableData.js +9 -42
- package/server/routes/data/controllers/tableFilter.js +3 -3
- package/server/routes/data/controllers/utils/getColumns.js +2 -15
- package/server/routes/templates/controllers/getTemplate.js +5 -1
- package/dist/admin-interface-DbfKIAnw.js +0 -1347
- package/dist/admin-view-DDDd_P1v.js +0 -461
- package/dist/card-page-Bq7fUm5C.js +0 -230
- package/dist/import-file-BBA7riUK.js +0 -47257
package/server/plugins/hook.js
CHANGED
@@ -1,228 +1,152 @@
|
|
1
|
-
import
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
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
|
-
|
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
|
-
|
47
|
-
|
48
|
-
|
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
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
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
|
-
|
143
|
+
if (!id || !properties?.length) return null;
|
70
144
|
|
71
|
-
|
72
|
-
|
73
|
-
|
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
|
-
|
76
|
-
|
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
|
-
|
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
|
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
|
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
|
97
|
-
: `${pk} in (select object_id from
|
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
|
140
|
-
: `${pk} in (select object_id from
|
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,
|
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
|
40
|
-
|
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
|
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
|
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
|
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
|
-
|
8
|
-
|
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
|
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)
|