@opengis/admin 0.1.81 → 0.1.83
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/README.md +29 -29
- package/config.js +4 -4
- package/dist/{IconChevronDown-D5JGurpf.js → IconChevronDown-B7bJL5rk.js} +1 -1
- package/dist/{add-page-DjmhHq_I.js → add-page-D5TrUydC.js} +1 -1
- package/dist/{admin-interface-CIA9Y-Dz.js → admin-interface-Cv1M1Cmt.js} +370 -352
- package/dist/{admin-view-DABvcRfg.js → admin-view-vVXbKHih.js} +45 -45
- package/dist/admin.js +1 -1
- package/dist/admin.umd.cjs +63 -61
- package/dist/assets/logo.svg +41 -41
- package/dist/{card-page-B_Vr2qvx.js → card-page-BbP6doYG.js} +146 -126
- package/dist/{card-view-XmNFtrP8.js → card-view-C2YWCaFs.js} +1 -1
- package/dist/{edit-page-ELlQWVFa.js → edit-page-BRKTh4MZ.js} +1 -1
- package/dist/{import-file-CsnR0UjH.js → import-file-DA-DiiaH.js} +6024 -5941
- package/dist/style.css +1 -1
- package/module/settings/card/admin.roles.table/access.hbs +27 -27
- package/module/settings/card/admin.roles.table/general_info.hbs +16 -16
- package/module/settings/card/admin.roles.table/index.yml +14 -14
- package/module/settings/card/admin.roles.table/users.hbs +27 -27
- package/module/settings/card/admin.routes.table/general_info.hbs +40 -40
- package/module/settings/card/admin.routes.table/index.yml +8 -8
- package/module/settings/card/admin.routes.table/users.hbs +33 -33
- package/module/settings/card/admin.users.table/general_info.hbs +25 -25
- package/module/settings/card/admin.users.table/index.yml +12 -12
- package/module/settings/card/admin.users.table/logs.hbs +30 -30
- package/module/settings/card/admin.users.table/user_roles.hbs +24 -24
- package/module/settings/cls/core.actions.json +17 -13
- package/module/settings/cls/core.scope.json +13 -13
- package/module/settings/cls/properties.site_status.json +13 -13
- package/module/settings/cls/properties.widget_status.json +13 -13
- package/module/settings/cls/users.user_type.json +13 -13
- package/module/settings/form/admin.access.form.json +36 -36
- package/module/settings/form/admin.custom_column.form.json +71 -71
- package/module/settings/form/admin.properties.form.json +15 -15
- package/module/settings/form/admin.roles.form.json +19 -19
- package/module/settings/form/admin.routes.form.json +25 -25
- package/module/settings/form/admin.user_properties.form.json +15 -15
- package/module/settings/form/admin.user_roles.form.json +21 -21
- package/module/settings/form/admin.users.form.json +153 -153
- package/module/settings/form/user.user_roles.form.json +13 -13
- package/module/settings/interface/admin.properties.json +4 -4
- package/module/settings/interface/admin.roles.json +4 -4
- package/module/settings/interface/admin.routes.json +4 -4
- package/module/settings/interface/admin.users.json +4 -4
- package/module/settings/menu.json +50 -50
- package/module/settings/select/core.roles.json +2 -2
- package/module/settings/select/core.routes.sql +1 -1
- package/module/settings/select/core.user_mentioned.sql +1 -1
- package/module/settings/select/core.user_uid.sql +1 -1
- package/module/settings/table/admin.access.table.json +83 -83
- package/module/settings/table/admin.custom_column.table.json +99 -99
- package/module/settings/table/admin.properties.table.json +39 -39
- package/module/settings/table/admin.roles.table.json +64 -64
- package/module/settings/table/admin.routes.table.json +68 -68
- package/module/settings/table/admin.user_properties.table.json +34 -34
- package/module/settings/table/admin.user_roles.table.json +72 -72
- package/module/settings/table/admin.users.table.json +121 -121
- package/package.json +81 -81
- package/plugin.js +29 -27
- package/server/helpers/controllers/badge.js +11 -11
- package/server/helpers/controllers/hb.js +2 -2
- package/server/helpers/controllers/map.js +2 -2
- package/server/helpers/controllers/mls.js +2 -2
- package/server/helpers/controllers/token.js +15 -15
- package/server/helpers/controllers/vue.js +2 -2
- package/server/helpers/index.mjs +17 -17
- package/server/plugins/access/funcs/getAdminAccess.js +15 -0
- package/server/plugins/access/index.mjs +6 -0
- package/server/plugins/adminHook.js +93 -93
- package/server/plugins/cron.js +10 -10
- package/server/plugins/docs.js +28 -28
- package/server/plugins/hook.js +223 -223
- package/server/plugins/vite.js +71 -71
- package/server/routes/access/controllers/access.group.js +30 -0
- package/server/routes/access/controllers/access.group.post.js +44 -0
- package/server/routes/access/index.mjs +63 -0
- package/server/routes/calendar/controllers/calendar.data.js +87 -87
- package/server/routes/calendar/index.mjs +7 -7
- package/server/routes/calendar/schema.js +21 -21
- package/server/routes/data/controllers/cardData.js +81 -79
- package/server/routes/data/controllers/cardTabData.js +49 -49
- package/server/routes/data/controllers/funcs/getFilterSQL/index.js +92 -92
- package/server/routes/data/controllers/funcs/getFilterSQL/util/formatValue.js +170 -170
- package/server/routes/data/controllers/funcs/getFilterSQL/util/getCustomQuery.js +13 -13
- package/server/routes/data/controllers/funcs/getFilterSQL/util/getFilterQuery.js +64 -64
- package/server/routes/data/controllers/funcs/getFilterSQL/util/getOptimizedQuery.js +12 -12
- package/server/routes/data/controllers/funcs/getFilterSQL/util/getTableSql.js +34 -34
- package/server/routes/data/controllers/tableData.js +20 -20
- package/server/routes/data/controllers/tableDataId.js +27 -27
- package/server/routes/data/controllers/tableFilter.js +63 -63
- package/server/routes/data/controllers/tokenInfo.js +10 -0
- package/server/routes/data/controllers/utils/assignTokens.js +30 -30
- package/server/routes/data/controllers/utils/getColumns.js +8 -8
- package/server/routes/data/index.mjs +17 -15
- package/server/routes/data/schema.js +54 -54
- package/server/routes/menu/controllers/getMenu.js +67 -67
- package/server/routes/menu/index.mjs +5 -5
- package/server/routes/notifications/controllers/readNotifications.js +27 -27
- package/server/routes/notifications/controllers/testEmail.js +35 -35
- package/server/routes/notifications/controllers/userNotifications.js +53 -53
- package/server/routes/notifications/funcs/addNotification.js +21 -21
- package/server/routes/notifications/funcs/sendNotification.js +105 -105
- package/server/routes/notifications/hook/onWidgetSet.js +57 -57
- package/server/routes/notifications/index.mjs +27 -27
- package/server/routes/notifications/schema.js +16 -16
- package/server/routes/properties/controllers/admin.properties.get.js +29 -29
- package/server/routes/properties/controllers/user.properties.get.js +30 -30
- package/server/routes/properties/controllers/user.properties.post.js +30 -30
- package/server/routes/properties/funcs/getSettings.js +56 -56
- package/server/routes/properties/funcs/setSettings.js +44 -44
- package/server/routes/properties/funcs/utils/dataInsert.js +26 -26
- package/server/routes/properties/index.mjs +14 -14
- package/server/routes/properties/schema.js +10 -10
- package/server/routes/root.mjs +3 -3
- package/server/routes/templates/controllers/getTemplate.js +26 -26
- package/server/routes/templates/index.mjs +16 -16
- package/server/routes/templates/schema.js +8 -8
- package/server/routes/user/controllers/user.cls.id.js +14 -14
- package/server/routes/user/controllers/user.cls.js +71 -71
- package/server/routes/user/controllers/user.cls.post.js +52 -52
- package/server/routes/user/controllers/user.info.js +17 -17
- package/server/routes/user/schema.js +14 -14
- package/server/routes/widget/controllers/utils/historyFormat.js +75 -75
- package/server/routes/widget/controllers/utils/obj2db.js +13 -13
- package/server/routes/widget/controllers/widget.del.js +41 -41
- package/server/routes/widget/controllers/widget.get.js +96 -96
- package/server/routes/widget/controllers/widget.set.js +76 -76
- package/server/routes/widget/index.mjs +11 -11
- package/server/routes/widget/schema.js +12 -12
- package/server/templates/cls/itree.recrzone_category.json +73 -73
- package/server/templates/cls/test.json +9 -9
- package/server/templates/form/admin.user_cls.data.form.json +49 -49
- package/server/templates/form/admin.user_group_rel.form.json +21 -21
- package/server/templates/form/cp_building.form.json +32 -32
- package/server/templates/form/form-user-pass.json +10 -10
- package/server/templates/form/form-user_group.json +39 -39
- package/server/templates/form/form-users.json +156 -156
- package/server/templates/form/user_group_access.form.json +22 -22
- package/server/templates/select/account_id.json +2 -2
- package/server/templates/table/gis.dataset.table.json +43 -43
- package/server/templates/table/management.user_group.table.json +112 -112
- package/server/templates/table/management.users.table.json +126 -126
- package/utils.js +29 -26
package/server/plugins/hook.js
CHANGED
@@ -1,224 +1,224 @@
|
|
1
|
-
import path from 'node:path';
|
2
|
-
|
3
|
-
import {
|
4
|
-
getTemplatePath, addHook, getToken, getTemplate, config, pgClients,
|
5
|
-
initPG,
|
6
|
-
} from '@opengis/fastify-table/utils.js';
|
7
|
-
|
8
|
-
import getMenu from '../routes/menu/controllers/getMenu.js';
|
9
|
-
|
10
|
-
const { client } = pgClients;
|
11
|
-
|
12
|
-
export default async function plugin(fastify) {
|
13
|
-
const user1 = config?.auth?.disable || process.env.NODE_ENV !== 'admin' ? { uid: '1' } : null;
|
14
|
-
await initPG(client);
|
15
|
-
|
16
|
-
fastify.addHook('onListen', async () => {
|
17
|
-
const json = await getMenu({ user: { uid: 1 } });
|
18
|
-
// insert interface list to db (user access management)
|
19
|
-
if (client?.pk?.['admin.routes'] && json?.length) {
|
20
|
-
const menuList = json.filter((el) => (el?.table || el?.menu?.length) && el?.ua || el?.en || el?.name);
|
21
|
-
const interfaces = menuList.reduce((acc, curr) => {
|
22
|
-
if (curr.table) { acc.push(curr.path); }
|
23
|
-
curr.menu?.forEach((el) => acc.push(el.path));
|
24
|
-
return acc;
|
25
|
-
}, []);
|
26
|
-
await client.query('update admin.routes set enabled=false where not array[route_id] <@ $1::text[]', [interfaces]);
|
27
|
-
|
28
|
-
const q = `insert into admin.menu(name, ord) values${menuList.map((el, i) => `('${(el?.ua || el?.en || el?.name).replace(/'/g, '’')}', ${i}) `).join(',')
|
29
|
-
} on conflict (name) do update set ord=excluded.ord, enabled=true returning name, menu_id`;
|
30
|
-
const { rows = [] } = menuList?.length ? await client.query(q) : {};
|
31
|
-
await client.query('update admin.menu set enabled=false where not array[menu_id] <@ $1::text[]', [rows.map((el) => el.menu_id)]);
|
32
|
-
|
33
|
-
const menus = rows.reduce((acc, curr) => Object.assign(acc, { [curr.menu_id]: menuList.find((item) => (item?.ua || item?.en || item?.name) === curr.name) }), {});
|
34
|
-
const values = Object.entries(menus).reduce((acc, curr) => {
|
35
|
-
if (curr[1]?.table) { acc.push({ ...curr[1], menuId: curr[0] }); }
|
36
|
-
curr[1]?.menu?.forEach((el) => acc.push({ ...el, menuId: curr[0] }));
|
37
|
-
return acc;
|
38
|
-
}, []);
|
39
|
-
|
40
|
-
await Promise.all(values.filter((el) => el?.table).map(async (el) => {
|
41
|
-
const loadTable = await getTemplate('table', el.table);
|
42
|
-
Object.assign(el, {
|
43
|
-
table1: loadTable?.table || el.table,
|
44
|
-
actions: loadTable?.actions,
|
45
|
-
access: loadTable?.access,
|
46
|
-
});
|
47
|
-
}));
|
48
|
-
|
49
|
-
const q1 = `insert into admin.routes(route_id, alias, title, menu_id, table_name, actions, access, query)
|
50
|
-
values ${values.filter((el) => el?.table).map((el) => `('${el.path}', '${el.table}', ${(el.title || el.ua) ? `'${el.title || el.ua}'` : 'null'}, '${el.menuId || null}', '${el.table1}',
|
51
|
-
${el.actions?.length ? `'{ ${el.actions} }'::text[]` : 'null'}, ${el.access ? `'${el.access}'` : 'null'}, ${el.query ? `'${el.query.replace(/'/g,"''")}'` : '\'1=1\''})`).join(',')}
|
52
|
-
on conflict (route_id) do update set menu_id=excluded.menu_id, alias=excluded.alias, title=excluded.title, enabled=true, query=excluded.query,
|
53
|
-
table_name=excluded.table_name, actions=excluded.actions, access=excluded.access returning route_id, table_name`;
|
54
|
-
|
55
|
-
try {
|
56
|
-
const { rowCount } = values?.length ? await client.query(q1) : {};
|
57
|
-
console.log('interface insert ok', /* values, */ rowCount);
|
58
|
-
} catch (err) {
|
59
|
-
console.log('interface insert error', values, q1, err);
|
60
|
-
}
|
61
|
-
}
|
62
|
-
});
|
63
|
-
|
64
|
-
fastify.addHook('onListen', async () => {
|
65
|
-
const clsQuery = [];
|
66
|
-
if (!client?.pk?.['admin.cls']) return;
|
67
|
-
|
68
|
-
const selectList = await getTemplatePath('select');
|
69
|
-
const clsList = (await getTemplatePath('cls'))?.filter((el) => !(selectList?.map((el) => el?.[0]) || []).includes(el[0]));
|
70
|
-
const cls = (selectList || []).concat(clsList || [])
|
71
|
-
?.map((el) => ({ name: el[0], module: path.basename(path.dirname(path.dirname(el[1]))), type: { 'json': 'cls', 'sql': 'select' }[el[2]] }))
|
72
|
-
if (!cls?.length) return;
|
73
|
-
|
74
|
-
const dupes = cls.filter((el, idx, arr) => arr.map((item) => item.name).indexOf(el.name) !== idx);
|
75
|
-
//console.log('cls insert skip dupes', dupes.map((el) => el.name));
|
76
|
-
|
77
|
-
try {
|
78
|
-
await Promise.all(cls.filter((el, idx, arr) => arr.map((item) => item.name).indexOf(el.name) === idx).map(async (el) => {
|
79
|
-
const { name, module, type } = el;
|
80
|
-
const loadTemplate = await getTemplate(type, name);
|
81
|
-
// console.log(name, type);
|
82
|
-
if (type === 'select') {
|
83
|
-
clsQuery.push(`insert into admin.cls(name,type,data,module) values('${name}','sql','${(loadTemplate?.sql || loadTemplate)?.replace(/'/g, "''")}', '${module?.replace(/'/g, "''")}')`);
|
84
|
-
} else if (type === 'cls' && loadTemplate?.length) {
|
85
|
-
clsQuery.push(`insert into admin.cls(name,type, module) values('${name}','json', '${module?.replace(/'/g, "''")}');
|
86
|
-
insert into admin.cls(code,name,parent,icon)
|
87
|
-
select value->>'id',value->>'text','${name}',value->>'icon'
|
88
|
-
from json_array_elements('${JSON.stringify(loadTemplate).replace(/'/g, "''")}'::json)`);
|
89
|
-
} else {
|
90
|
-
console.log(name, type, 'empty');
|
91
|
-
}
|
92
|
-
}));
|
93
|
-
|
94
|
-
await client.query('truncate admin.cls');
|
95
|
-
if (clsQuery.filter((el) => el).length) {
|
96
|
-
await client.query(clsQuery.filter((el) => el).join(';'));
|
97
|
-
console.log('cls insert ok', clsQuery?.length);
|
98
|
-
}
|
99
|
-
} catch (err) {
|
100
|
-
console.error('cls insert error', err.toString());
|
101
|
-
}
|
102
|
-
});
|
103
|
-
|
104
|
-
addHook('afterTable', async ({ table, res = {}, payload: rows = [], user = {} }) => {
|
105
|
-
const loadTable = await getTemplate('table', table);
|
106
|
-
const { uid } = user1 || user;
|
107
|
-
if (!uid || !table || !client?.pk?.[table] || !rows.length || !loadTable?.table) return;
|
108
|
-
|
109
|
-
// admin.custom_column - user column data
|
110
|
-
const { rows: properties = [] } = await client.query(`select column_id, name, title, format, data from admin.custom_column
|
111
|
-
where _table and entity=$1 and uid=$2`, [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
|
-
if (!extraColumnList?.length) return;
|
114
|
-
|
115
|
-
if (res?.columns?.length) {
|
116
|
-
extraColumnList.forEach((col) => res.columns.push(col));
|
117
|
-
}
|
118
|
-
|
119
|
-
const { rows: extraData = [] } = await client.query(`select object_id, json_object_agg( property_id, coalesce(value_date::text,value_text) ) as extra from crm.extra_data
|
120
|
-
where property_entity=$1 and property_id=any($2) and object_id=any($3) group by object_id`, [table, extraColumnList?.map((el) => el.id), rows.map((el) => el.id)]);
|
121
|
-
|
122
|
-
if (!extraData?.length) {
|
123
|
-
// Object.assign(rows?.[0] || {}, { ...extraColumnList.reduce((acc, curr) => Object.assign(acc, { [curr.name]: null }), {}) });
|
124
|
-
return;
|
125
|
-
}
|
126
|
-
|
127
|
-
rows.filter((row) => extraData.map((el) => el?.object_id).includes(row.id)).forEach((row) => {
|
128
|
-
const { extra = {} } = extraData.find((el) => el.object_id === row.id);
|
129
|
-
Object.assign(row, { ...Object.fromEntries(Object.entries(extra).map((el) => [extraColumnList.find((col) => col.id === el[0]).name, el[1]])) });
|
130
|
-
});
|
131
|
-
|
132
|
-
// admin.custom_column - metaFormat
|
133
|
-
await Promise.all(extraColumnList.filter((el) => el?.data).map(async (attr) => {
|
134
|
-
const values = [...new Set(rows?.map((el) => el[attr.name]).flat())].filter((el) => el);
|
135
|
-
if (!values.length) return;
|
136
|
-
const cls = await getSelectVal({ name: attr.data, values });
|
137
|
-
if (!cls) return;
|
138
|
-
rows.forEach(el => {
|
139
|
-
const val = el[attr.name]?.map?.(c => cls[c] || c) || cls[el[attr.name]] || el[attr.name];
|
140
|
-
if (!val) return;
|
141
|
-
Object.assign(el, { [val?.color ? `${attr.name}_data` : `${attr.name}_text`]: (val.color ? val : val.text || val) });
|
142
|
-
});
|
143
|
-
}));
|
144
|
-
});
|
145
|
-
|
146
|
-
// extract table from form token for user columns - p.2 - read (refactor to global token)
|
147
|
-
addHook('preTemplate', async ({ name, type, user = {} }) => {
|
148
|
-
if (!name || !type) return;
|
149
|
-
const { uid } = user1 || user;
|
150
|
-
const tokenData = await getToken({
|
151
|
-
uid, token: name, mode: 'w', json: 1,
|
152
|
-
}) // edit?
|
153
|
-
|| await getToken({
|
154
|
-
uid, token: name, mode: 'a', json: 1,
|
155
|
-
}) || {}; // add?
|
156
|
-
return { name: tokenData?.[type] };
|
157
|
-
});
|
158
|
-
|
159
|
-
addHook('afterTemplate', async ({ name, type, payload: data = {}, user = {} }) => {
|
160
|
-
const { uid } = user1 || user;
|
161
|
-
// extract table from form token for user columns - p.1 - assign (refactor to global token)
|
162
|
-
if (!uid || !data || type !== 'form' || !name) return null;
|
163
|
-
|
164
|
-
const { form, id, table } = await getToken({
|
165
|
-
uid, token: name, mode: 'w', json: 1,
|
166
|
-
}) // edit?
|
167
|
-
|| await getToken({
|
168
|
-
uid, token: name, mode: 'a', json: 1,
|
169
|
-
}) || {}; // add?
|
170
|
-
|
171
|
-
const { rows: properties = [] } = await client.query(`select name, title, format, data from admin.custom_column
|
172
|
-
where entity=$1 and uid=$2`, [table || name, uid]);
|
173
|
-
|
174
|
-
await Promise.all(properties.map(async (el) => {
|
175
|
-
const clsData = el.data ? await getTemplate(['cls', 'select'], el.data) : undefined;
|
176
|
-
const type = clsData ? 'Select' : ({ date: 'DatePicker' }[el.format] || 'Text');
|
177
|
-
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 } });
|
178
|
-
}));
|
179
|
-
});
|
180
|
-
|
181
|
-
addHook('afterUpdate', async ({ table, body = {}, payload: res = {}, user = {} }) => {
|
182
|
-
const { uid } = user1 || user;
|
183
|
-
if (!uid || !table || !Object.keys(body)?.length) return null;
|
184
|
-
|
185
|
-
const loadTable = await getTemplate('table', table);
|
186
|
-
if (!client?.pk?.[loadTable?.table || table]) return null;
|
187
|
-
const pk = client?.pk?.[loadTable?.table || table];
|
188
|
-
const id = res[pk];
|
189
|
-
|
190
|
-
const { rows: properties = [] } = await client.query(`select column_id, name, title, format, data from admin.custom_column
|
191
|
-
where entity=$1 and uid=$2`, [table, uid]);
|
192
|
-
|
193
|
-
if (!id || !properties?.length) return null;
|
194
|
-
|
195
|
-
const q = `delete from crm.extra_data where property_entity='${table}' and object_id='${id}';${properties
|
196
|
-
.filter((el) => Object.keys(body).includes(el.name))
|
197
|
-
.map((el) => `insert into crm.extra_data(property_id,property_key,property_entity,object_id,${el.format?.toLowerCase() === 'date' ? 'value_date' : 'value_text'})
|
198
|
-
select '${el.column_id}', '${el.name}', '${table}', '${id}', ${el.format?.toLowerCase() === 'date' ? `'${body[el.name]}'::timestamp without time zone` : `'${body[el.name]}'::text`}`)
|
199
|
-
.join(';\n') || ''}`;
|
200
|
-
return client.query(q);
|
201
|
-
});
|
202
|
-
|
203
|
-
addHook('afterInsert', async ({ table, body = {}, payload: res = {}, user = {} }) => {
|
204
|
-
const { uid } = user1 || user;
|
205
|
-
if (!uid || !table || !Object.keys(body)?.length) return null;
|
206
|
-
|
207
|
-
const loadTable = await getTemplate('table', table);
|
208
|
-
if (!client?.pk?.[loadTable?.table || table]) return null;
|
209
|
-
const pk = client?.pk?.[loadTable?.table || table];
|
210
|
-
const id = res.rows?.[0]?.[pk];
|
211
|
-
|
212
|
-
const { rows: properties = [] } = await client.query(`select column_id, name, title, format, data from admin.custom_column
|
213
|
-
where entity=$1 and uid=$2`, [table, uid]);
|
214
|
-
|
215
|
-
if (!id || !properties?.length) return null;
|
216
|
-
|
217
|
-
const q = properties
|
218
|
-
.filter((el) => Object.keys(body).includes(el.name))
|
219
|
-
.map((el) => `insert into crm.extra_data(property_id,property_key,property_entity,object_id,${el.format?.toLowerCase() === 'date' ? 'value_date' : 'value_text'})
|
220
|
-
select '${el.column_id}', '${el.name}', '${table}', '${id}', ${el.format?.toLowerCase() === 'date' ? `'${body[el.name]}'::timestamp without time zone` : `'${body[el.name]}'::text`}`)
|
221
|
-
.join(';\n');
|
222
|
-
return client.query(q);
|
223
|
-
});
|
1
|
+
import path from 'node:path';
|
2
|
+
|
3
|
+
import {
|
4
|
+
getTemplatePath, addHook, getToken, getTemplate, config, pgClients,
|
5
|
+
initPG,
|
6
|
+
} from '@opengis/fastify-table/utils.js';
|
7
|
+
|
8
|
+
import getMenu from '../routes/menu/controllers/getMenu.js';
|
9
|
+
|
10
|
+
const { client } = pgClients;
|
11
|
+
|
12
|
+
export default async function plugin(fastify) {
|
13
|
+
const user1 = config?.auth?.disable || process.env.NODE_ENV !== 'admin' ? { uid: '1' } : null;
|
14
|
+
await initPG(client);
|
15
|
+
|
16
|
+
fastify.addHook('onListen', async () => {
|
17
|
+
const json = await getMenu({ user: { uid: 1 } });
|
18
|
+
// insert interface list to db (user access management)
|
19
|
+
if (client?.pk?.['admin.routes'] && json?.length) {
|
20
|
+
const menuList = json.filter((el) => (el?.table || el?.menu?.length) && el?.ua || el?.en || el?.name);
|
21
|
+
const interfaces = menuList.reduce((acc, curr) => {
|
22
|
+
if (curr.table) { acc.push(curr.path); }
|
23
|
+
curr.menu?.forEach((el) => acc.push(el.path));
|
24
|
+
return acc;
|
25
|
+
}, []);
|
26
|
+
await client.query('update admin.routes set enabled=false where not array[route_id] <@ $1::text[]', [interfaces]);
|
27
|
+
|
28
|
+
const q = `insert into admin.menu(name, ord) values${menuList.map((el, i) => `('${(el?.ua || el?.en || el?.name).replace(/'/g, '’')}', ${i}) `).join(',')
|
29
|
+
} on conflict (name) do update set ord=excluded.ord, enabled=true returning name, menu_id`;
|
30
|
+
const { rows = [] } = menuList?.length ? await client.query(q) : {};
|
31
|
+
await client.query('update admin.menu set enabled=false where not array[menu_id] <@ $1::text[]', [rows.map((el) => el.menu_id)]);
|
32
|
+
|
33
|
+
const menus = rows.reduce((acc, curr) => Object.assign(acc, { [curr.menu_id]: menuList.find((item) => (item?.ua || item?.en || item?.name) === curr.name) }), {});
|
34
|
+
const values = Object.entries(menus).reduce((acc, curr) => {
|
35
|
+
if (curr[1]?.table) { acc.push({ ...curr[1], menuId: curr[0] }); }
|
36
|
+
curr[1]?.menu?.forEach((el) => acc.push({ ...el, menuId: curr[0] }));
|
37
|
+
return acc;
|
38
|
+
}, []);
|
39
|
+
|
40
|
+
await Promise.all(values.filter((el) => el?.table).map(async (el) => {
|
41
|
+
const loadTable = await getTemplate('table', el.table);
|
42
|
+
Object.assign(el, {
|
43
|
+
table1: loadTable?.table || el.table,
|
44
|
+
actions: loadTable?.actions,
|
45
|
+
access: loadTable?.access,
|
46
|
+
});
|
47
|
+
}));
|
48
|
+
|
49
|
+
const q1 = `insert into admin.routes(route_id, alias, title, menu_id, table_name, actions, access, query)
|
50
|
+
values ${values.filter((el) => el?.table).map((el) => `('${el.path}', '${el.table}', ${(el.title || el.ua) ? `'${(el.title || el.ua).replace(/'/g,"''")}'` : 'null'}, '${el.menuId || null}', '${el.table1}',
|
51
|
+
${el.actions?.length ? `'{ ${el.actions} }'::text[]` : 'null'}, ${el.access ? `'${el.access}'` : 'null'}, ${el.query ? `'${el.query.replace(/'/g,"''")}'` : '\'1=1\''})`).join(',')}
|
52
|
+
on conflict (route_id) do update set menu_id=excluded.menu_id, alias=excluded.alias, title=excluded.title, enabled=true, query=excluded.query,
|
53
|
+
table_name=excluded.table_name, actions=excluded.actions, access=excluded.access returning route_id, table_name`;
|
54
|
+
|
55
|
+
try {
|
56
|
+
const { rowCount } = values?.length ? await client.query(q1) : {};
|
57
|
+
console.log('interface insert ok', /* values, */ rowCount);
|
58
|
+
} catch (err) {
|
59
|
+
console.log('interface insert error', values, q1, err);
|
60
|
+
}
|
61
|
+
}
|
62
|
+
});
|
63
|
+
|
64
|
+
fastify.addHook('onListen', async () => {
|
65
|
+
const clsQuery = [];
|
66
|
+
if (!client?.pk?.['admin.cls']) return;
|
67
|
+
|
68
|
+
const selectList = await getTemplatePath('select');
|
69
|
+
const clsList = (await getTemplatePath('cls'))?.filter((el) => !(selectList?.map((el) => el?.[0]) || []).includes(el[0]));
|
70
|
+
const cls = (selectList || []).concat(clsList || [])
|
71
|
+
?.map((el) => ({ name: el[0], module: path.basename(path.dirname(path.dirname(el[1]))), type: { 'json': 'cls', 'sql': 'select' }[el[2]] }))
|
72
|
+
if (!cls?.length) return;
|
73
|
+
|
74
|
+
const dupes = cls.filter((el, idx, arr) => arr.map((item) => item.name).indexOf(el.name) !== idx);
|
75
|
+
//console.log('cls insert skip dupes', dupes.map((el) => el.name));
|
76
|
+
|
77
|
+
try {
|
78
|
+
await Promise.all(cls.filter((el, idx, arr) => arr.map((item) => item.name).indexOf(el.name) === idx).map(async (el) => {
|
79
|
+
const { name, module, type } = el;
|
80
|
+
const loadTemplate = await getTemplate(type, name);
|
81
|
+
// console.log(name, type);
|
82
|
+
if (type === 'select') {
|
83
|
+
clsQuery.push(`insert into admin.cls(name,type,data,module) values('${name}','sql','${(loadTemplate?.sql || loadTemplate)?.replace(/'/g, "''")}', '${module?.replace(/'/g, "''")}')`);
|
84
|
+
} else if (type === 'cls' && loadTemplate?.length) {
|
85
|
+
clsQuery.push(`insert into admin.cls(name,type, module) values('${name}','json', '${module?.replace(/'/g, "''")}');
|
86
|
+
insert into admin.cls(code,name,parent,icon)
|
87
|
+
select value->>'id',value->>'text','${name}',value->>'icon'
|
88
|
+
from json_array_elements('${JSON.stringify(loadTemplate).replace(/'/g, "''")}'::json)`);
|
89
|
+
} else {
|
90
|
+
console.log(name, type, 'empty');
|
91
|
+
}
|
92
|
+
}));
|
93
|
+
|
94
|
+
await client.query('truncate admin.cls');
|
95
|
+
if (clsQuery.filter((el) => el).length) {
|
96
|
+
await client.query(clsQuery.filter((el) => el).join(';'));
|
97
|
+
console.log('cls insert ok', clsQuery?.length);
|
98
|
+
}
|
99
|
+
} catch (err) {
|
100
|
+
console.error('cls insert error', err.toString());
|
101
|
+
}
|
102
|
+
});
|
103
|
+
|
104
|
+
addHook('afterTable', async ({ table, res = {}, payload: rows = [], user = {} }) => {
|
105
|
+
const loadTable = await getTemplate('table', table);
|
106
|
+
const { uid } = user1 || user;
|
107
|
+
if (!uid || !table || !client?.pk?.[table] || !rows.length || !loadTable?.table) return;
|
108
|
+
|
109
|
+
// admin.custom_column - user column data
|
110
|
+
const { rows: properties = [] } = await client.query(`select column_id, name, title, format, data from admin.custom_column
|
111
|
+
where _table and entity=$1 and uid=$2`, [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
|
+
if (!extraColumnList?.length) return;
|
114
|
+
|
115
|
+
if (res?.columns?.length) {
|
116
|
+
extraColumnList.forEach((col) => res.columns.push(col));
|
117
|
+
}
|
118
|
+
|
119
|
+
const { rows: extraData = [] } = await client.query(`select object_id, json_object_agg( property_id, coalesce(value_date::text,value_text) ) as extra from crm.extra_data
|
120
|
+
where property_entity=$1 and property_id=any($2) and object_id=any($3) group by object_id`, [table, extraColumnList?.map((el) => el.id), rows.map((el) => el.id)]);
|
121
|
+
|
122
|
+
if (!extraData?.length) {
|
123
|
+
// Object.assign(rows?.[0] || {}, { ...extraColumnList.reduce((acc, curr) => Object.assign(acc, { [curr.name]: null }), {}) });
|
124
|
+
return;
|
125
|
+
}
|
126
|
+
|
127
|
+
rows.filter((row) => extraData.map((el) => el?.object_id).includes(row.id)).forEach((row) => {
|
128
|
+
const { extra = {} } = extraData.find((el) => el.object_id === row.id);
|
129
|
+
Object.assign(row, { ...Object.fromEntries(Object.entries(extra).map((el) => [extraColumnList.find((col) => col.id === el[0]).name, el[1]])) });
|
130
|
+
});
|
131
|
+
|
132
|
+
// admin.custom_column - metaFormat
|
133
|
+
await Promise.all(extraColumnList.filter((el) => el?.data).map(async (attr) => {
|
134
|
+
const values = [...new Set(rows?.map((el) => el[attr.name]).flat())].filter((el) => el);
|
135
|
+
if (!values.length) return;
|
136
|
+
const cls = await getSelectVal({ name: attr.data, values });
|
137
|
+
if (!cls) return;
|
138
|
+
rows.forEach(el => {
|
139
|
+
const val = el[attr.name]?.map?.(c => cls[c] || c) || cls[el[attr.name]] || el[attr.name];
|
140
|
+
if (!val) return;
|
141
|
+
Object.assign(el, { [val?.color ? `${attr.name}_data` : `${attr.name}_text`]: (val.color ? val : val.text || val) });
|
142
|
+
});
|
143
|
+
}));
|
144
|
+
});
|
145
|
+
|
146
|
+
// extract table from form token for user columns - p.2 - read (refactor to global token)
|
147
|
+
addHook('preTemplate', async ({ name, type, user = {} }) => {
|
148
|
+
if (!name || !type) return;
|
149
|
+
const { uid } = user1 || user;
|
150
|
+
const tokenData = await getToken({
|
151
|
+
uid, token: name, mode: 'w', json: 1,
|
152
|
+
}) // edit?
|
153
|
+
|| await getToken({
|
154
|
+
uid, token: name, mode: 'a', json: 1,
|
155
|
+
}) || {}; // add?
|
156
|
+
return { name: tokenData?.[type] };
|
157
|
+
});
|
158
|
+
|
159
|
+
addHook('afterTemplate', async ({ name, type, payload: data = {}, user = {} }) => {
|
160
|
+
const { uid } = user1 || user;
|
161
|
+
// extract table from form token for user columns - p.1 - assign (refactor to global token)
|
162
|
+
if (!uid || !data || type !== 'form' || !name) return null;
|
163
|
+
|
164
|
+
const { form, id, table } = await getToken({
|
165
|
+
uid, token: name, mode: 'w', json: 1,
|
166
|
+
}) // edit?
|
167
|
+
|| await getToken({
|
168
|
+
uid, token: name, mode: 'a', json: 1,
|
169
|
+
}) || {}; // add?
|
170
|
+
|
171
|
+
const { rows: properties = [] } = await client.query(`select name, title, format, data from admin.custom_column
|
172
|
+
where entity=$1 and uid=$2`, [table || name, uid]);
|
173
|
+
|
174
|
+
await Promise.all(properties.map(async (el) => {
|
175
|
+
const clsData = el.data ? await getTemplate(['cls', 'select'], el.data) : undefined;
|
176
|
+
const type = clsData ? 'Select' : ({ date: 'DatePicker' }[el.format] || 'Text');
|
177
|
+
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 } });
|
178
|
+
}));
|
179
|
+
});
|
180
|
+
|
181
|
+
addHook('afterUpdate', async ({ table, body = {}, payload: res = {}, user = {} }) => {
|
182
|
+
const { uid } = user1 || user;
|
183
|
+
if (!uid || !table || !Object.keys(body)?.length) return null;
|
184
|
+
|
185
|
+
const loadTable = await getTemplate('table', table);
|
186
|
+
if (!client?.pk?.[loadTable?.table || table]) return null;
|
187
|
+
const pk = client?.pk?.[loadTable?.table || table];
|
188
|
+
const id = res[pk];
|
189
|
+
|
190
|
+
const { rows: properties = [] } = await client.query(`select column_id, name, title, format, data from admin.custom_column
|
191
|
+
where entity=$1 and uid=$2`, [table, uid]);
|
192
|
+
|
193
|
+
if (!id || !properties?.length) return null;
|
194
|
+
|
195
|
+
const q = `delete from crm.extra_data where property_entity='${table}' and object_id='${id}';${properties
|
196
|
+
.filter((el) => Object.keys(body).includes(el.name))
|
197
|
+
.map((el) => `insert into crm.extra_data(property_id,property_key,property_entity,object_id,${el.format?.toLowerCase() === 'date' ? 'value_date' : 'value_text'})
|
198
|
+
select '${el.column_id}', '${el.name}', '${table}', '${id}', ${el.format?.toLowerCase() === 'date' ? `'${body[el.name]}'::timestamp without time zone` : `'${body[el.name]}'::text`}`)
|
199
|
+
.join(';\n') || ''}`;
|
200
|
+
return client.query(q);
|
201
|
+
});
|
202
|
+
|
203
|
+
addHook('afterInsert', async ({ table, body = {}, payload: res = {}, user = {} }) => {
|
204
|
+
const { uid } = user1 || user;
|
205
|
+
if (!uid || !table || !Object.keys(body)?.length) return null;
|
206
|
+
|
207
|
+
const loadTable = await getTemplate('table', table);
|
208
|
+
if (!client?.pk?.[loadTable?.table || table]) return null;
|
209
|
+
const pk = client?.pk?.[loadTable?.table || table];
|
210
|
+
const id = res.rows?.[0]?.[pk];
|
211
|
+
|
212
|
+
const { rows: properties = [] } = await client.query(`select column_id, name, title, format, data from admin.custom_column
|
213
|
+
where entity=$1 and uid=$2`, [table, uid]);
|
214
|
+
|
215
|
+
if (!id || !properties?.length) return null;
|
216
|
+
|
217
|
+
const q = properties
|
218
|
+
.filter((el) => Object.keys(body).includes(el.name))
|
219
|
+
.map((el) => `insert into crm.extra_data(property_id,property_key,property_entity,object_id,${el.format?.toLowerCase() === 'date' ? 'value_date' : 'value_text'})
|
220
|
+
select '${el.column_id}', '${el.name}', '${table}', '${id}', ${el.format?.toLowerCase() === 'date' ? `'${body[el.name]}'::timestamp without time zone` : `'${body[el.name]}'::text`}`)
|
221
|
+
.join(';\n');
|
222
|
+
return client.query(q);
|
223
|
+
});
|
224
224
|
}
|
package/server/plugins/vite.js
CHANGED
@@ -1,71 +1,71 @@
|
|
1
|
-
import fs from 'fs';
|
2
|
-
import path, { dirname } from 'path';
|
3
|
-
import { fileURLToPath } from 'url';
|
4
|
-
|
5
|
-
const dir = dirname(fileURLToPath(import.meta.url));
|
6
|
-
const root = `${dir}/../..`;
|
7
|
-
|
8
|
-
const isProduction = process.env.NODE_ENV === 'production';
|
9
|
-
|
10
|
-
async function plugin(fastify) {
|
11
|
-
// vite server
|
12
|
-
if (!isProduction) {
|
13
|
-
const vite = await import('vite');
|
14
|
-
|
15
|
-
const viteServer = await vite.createServer({
|
16
|
-
server: {
|
17
|
-
middlewareMode: true,
|
18
|
-
},
|
19
|
-
});
|
20
|
-
// hot reload
|
21
|
-
viteServer.watcher.on('all', function (d, t) {
|
22
|
-
if (!t.includes('module')) return;
|
23
|
-
console.log(d, t);
|
24
|
-
viteServer.ws.send({ type: 'full-reload' });
|
25
|
-
});
|
26
|
-
|
27
|
-
// this is middleware for vite's dev servert
|
28
|
-
fastify.addHook('onRequest', async (req, reply) => {
|
29
|
-
const { user } = req.session?.passport || {};
|
30
|
-
if (!user) return reply.redirect('/login');
|
31
|
-
|
32
|
-
const next = () => new Promise((resolve) => {
|
33
|
-
viteServer.middlewares(req.raw, reply.raw, () => resolve());
|
34
|
-
});
|
35
|
-
await next();
|
36
|
-
});
|
37
|
-
fastify.get('*', async () => { });
|
38
|
-
return;
|
39
|
-
}
|
40
|
-
|
41
|
-
// From Build
|
42
|
-
fastify.get('*', async (req, reply) => {
|
43
|
-
const { user } = req.session?.passport || {};
|
44
|
-
|
45
|
-
if (!user) return reply.redirect('/login');
|
46
|
-
const stream = fs.createReadStream('dist/index.html');
|
47
|
-
return reply.type('text/html').send(stream);
|
48
|
-
});
|
49
|
-
|
50
|
-
const fileSize = {}
|
51
|
-
async function staticFile(req, reply) {
|
52
|
-
const assetsDir = 'dist';
|
53
|
-
const filePath = path.join(root, assetsDir, req.url);
|
54
|
-
const ext = path.extname(filePath);
|
55
|
-
|
56
|
-
if (!fs.existsSync(filePath)) return { status: 404, message: 'not found' }
|
57
|
-
fileSize[filePath] = fileSize[filePath] || fs.statSync(filePath).size;
|
58
|
-
const mime = {
|
59
|
-
'.js': 'text/javascript', '.css': 'text/css', '.woff2': 'application/font-woff', '.png': 'image/png', '.svg': 'image/svg+xml', '.jpg': 'image/jpg',
|
60
|
-
}[ext];
|
61
|
-
reply.headers({ 'Cache-control': 'max-age=3600, public', 'Content-length': fileSize[filePath], 'Content-Encoding': 'identity' });
|
62
|
-
|
63
|
-
const stream = fs.createReadStream(filePath);
|
64
|
-
return mime ? reply.type(mime).send(stream) : stream;
|
65
|
-
}
|
66
|
-
|
67
|
-
fastify.get('/assets/*', staticFile);
|
68
|
-
fastify.get('/public/*', staticFile);
|
69
|
-
}
|
70
|
-
|
71
|
-
export default plugin;
|
1
|
+
import fs from 'fs';
|
2
|
+
import path, { dirname } from 'path';
|
3
|
+
import { fileURLToPath } from 'url';
|
4
|
+
|
5
|
+
const dir = dirname(fileURLToPath(import.meta.url));
|
6
|
+
const root = `${dir}/../..`;
|
7
|
+
|
8
|
+
const isProduction = process.env.NODE_ENV === 'production';
|
9
|
+
|
10
|
+
async function plugin(fastify) {
|
11
|
+
// vite server
|
12
|
+
if (!isProduction) {
|
13
|
+
const vite = await import('vite');
|
14
|
+
|
15
|
+
const viteServer = await vite.createServer({
|
16
|
+
server: {
|
17
|
+
middlewareMode: true,
|
18
|
+
},
|
19
|
+
});
|
20
|
+
// hot reload
|
21
|
+
viteServer.watcher.on('all', function (d, t) {
|
22
|
+
if (!t.includes('module')) return;
|
23
|
+
console.log(d, t);
|
24
|
+
viteServer.ws.send({ type: 'full-reload' });
|
25
|
+
});
|
26
|
+
|
27
|
+
// this is middleware for vite's dev servert
|
28
|
+
fastify.addHook('onRequest', async (req, reply) => {
|
29
|
+
const { user } = req.session?.passport || {};
|
30
|
+
if (!user) return reply.redirect('/login');
|
31
|
+
|
32
|
+
const next = () => new Promise((resolve) => {
|
33
|
+
viteServer.middlewares(req.raw, reply.raw, () => resolve());
|
34
|
+
});
|
35
|
+
await next();
|
36
|
+
});
|
37
|
+
fastify.get('*', async () => { });
|
38
|
+
return;
|
39
|
+
}
|
40
|
+
|
41
|
+
// From Build
|
42
|
+
fastify.get('*', async (req, reply) => {
|
43
|
+
const { user } = req.session?.passport || {};
|
44
|
+
|
45
|
+
if (!user) return reply.redirect('/login');
|
46
|
+
const stream = fs.createReadStream('dist/index.html');
|
47
|
+
return reply.type('text/html').send(stream);
|
48
|
+
});
|
49
|
+
|
50
|
+
const fileSize = {}
|
51
|
+
async function staticFile(req, reply) {
|
52
|
+
const assetsDir = 'dist';
|
53
|
+
const filePath = path.join(root, assetsDir, req.url);
|
54
|
+
const ext = path.extname(filePath);
|
55
|
+
|
56
|
+
if (!fs.existsSync(filePath)) return { status: 404, message: 'not found' }
|
57
|
+
fileSize[filePath] = fileSize[filePath] || fs.statSync(filePath).size;
|
58
|
+
const mime = {
|
59
|
+
'.js': 'text/javascript', '.css': 'text/css', '.woff2': 'application/font-woff', '.png': 'image/png', '.svg': 'image/svg+xml', '.jpg': 'image/jpg',
|
60
|
+
}[ext];
|
61
|
+
reply.headers({ 'Cache-control': 'max-age=3600, public', 'Content-length': fileSize[filePath], 'Content-Encoding': 'identity' });
|
62
|
+
|
63
|
+
const stream = fs.createReadStream(filePath);
|
64
|
+
return mime ? reply.type(mime).send(stream) : stream;
|
65
|
+
}
|
66
|
+
|
67
|
+
fastify.get('/assets/*', staticFile);
|
68
|
+
fastify.get('/public/*', staticFile);
|
69
|
+
}
|
70
|
+
|
71
|
+
export default plugin;
|
@@ -0,0 +1,30 @@
|
|
1
|
+
import { pgClients } from '@opengis/fastify-table/utils.js';
|
2
|
+
|
3
|
+
import { getAdminAccess } from '../../../../utils.js';
|
4
|
+
|
5
|
+
export default async function accessGroup({
|
6
|
+
pg = pgClients.client, params = {}, session = {},
|
7
|
+
}) {
|
8
|
+
const { user = {} } = session?.passport || {};
|
9
|
+
|
10
|
+
if (!params?.id) {
|
11
|
+
return { message: 'not enough params: id', status: 400 };
|
12
|
+
}
|
13
|
+
|
14
|
+
// restrict access - admin only
|
15
|
+
const check = await getAdminAccess({
|
16
|
+
id: params.id, user,
|
17
|
+
});
|
18
|
+
if (check) return check;
|
19
|
+
|
20
|
+
const { rows: routes = [] } = await pg.query(`select a.route_id as path, coalesce(a.actions, b.actions) as actions from admin.routes a
|
21
|
+
left join admin.access b on a.route_id=b.route_id
|
22
|
+
where b.role_id=$1`, [params.id]);
|
23
|
+
|
24
|
+
const { rows: users = [] } = await pg.query(`select user_uid as id, user_name as name, access_granted,
|
25
|
+
b.cdate as user_created, b.last_activity_date as last_activity from admin.user_roles a
|
26
|
+
left join admin.users b on a.user_uid=b.uid
|
27
|
+
where a.role_id=$1`, [params.id]);
|
28
|
+
|
29
|
+
return { routes, users };
|
30
|
+
}
|