@opengis/admin 0.2.7 → 0.2.8

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.
Files changed (164) hide show
  1. package/README.md +29 -29
  2. package/config.js +4 -4
  3. package/dist/{IconChevronDown-B3Ry6l2F.js → IconChevronDown-BCpDxWU9.js} +1 -1
  4. package/dist/{add-page-Dv6JSPJD.js → add-page-CKy5L_78.js} +1 -1
  5. package/dist/{admin-interface-BWZcS6r_.js → admin-interface-BX613rWQ.js} +259 -260
  6. package/dist/{admin-view-xT8rj54J.js → admin-view-DJBkad_B.js} +3 -3
  7. package/dist/admin.js +1 -1
  8. package/dist/admin.umd.cjs +54 -54
  9. package/dist/assets/logo.svg +41 -41
  10. package/dist/{card-view-DbHBF1Gz.js → card-view-CdCsaogK.js} +1 -1
  11. package/dist/{edit-page-TnYoAVr7.js → edit-page-C76NxZRq.js} +1 -1
  12. package/dist/{import-file-1QgPYOHQ.js → import-file-CknmeGNO.js} +6116 -6159
  13. package/dist/style.css +1 -1
  14. package/module/settings/card/admin.accounts.table/index.yml +7 -7
  15. package/module/settings/card/admin.accounts.table/rules.hbs +18 -18
  16. package/module/settings/card/admin.accounts.table/users.hbs +13 -13
  17. package/module/settings/card/admin.roles.table/access.hbs +3 -3
  18. package/module/settings/card/admin.roles.table/general_info.hbs +1 -1
  19. package/module/settings/card/admin.roles.table/index.yml +21 -21
  20. package/module/settings/card/admin.roles.table/users.hbs +6 -6
  21. package/module/settings/card/admin.routes.table/general_info.hbs +13 -13
  22. package/module/settings/card/admin.routes.table/groups.hbs +11 -11
  23. package/module/settings/card/admin.routes.table/index.yml +11 -11
  24. package/module/settings/card/admin.routes.table/users.hbs +16 -16
  25. package/module/settings/card/admin.users.table/context.hbs +14 -14
  26. package/module/settings/card/admin.users.table/general_info.hbs +12 -12
  27. package/module/settings/card/admin.users.table/index.yml +22 -22
  28. package/module/settings/card/admin.users.table/last_login.hbs +9 -9
  29. package/module/settings/card/admin.users.table/logs.hbs +10 -10
  30. package/module/settings/card/admin.users.table/routes.hbs +7 -7
  31. package/module/settings/card/admin.users.table/user_roles.hbs +12 -12
  32. package/module/settings/cls/core.actions.json +17 -17
  33. package/module/settings/cls/core.scope.json +13 -13
  34. package/module/settings/cls/properties.site_status.json +13 -13
  35. package/module/settings/cls/properties.widget_status.json +13 -13
  36. package/module/settings/cls/users.user_type.json +13 -13
  37. package/module/settings/form/admin.accounts.form.json +13 -13
  38. package/module/settings/form/admin.custom_column.form.json +71 -71
  39. package/module/settings/form/admin.properties.form.json +15 -15
  40. package/module/settings/form/admin.roles.form.json +21 -21
  41. package/module/settings/form/admin.routes.form.json +25 -25
  42. package/module/settings/form/admin.rules.form.json +30 -30
  43. package/module/settings/form/admin.user_properties.form.json +15 -15
  44. package/module/settings/form/admin.user_roles.form.json +13 -13
  45. package/module/settings/form/admin.user_roles_card.form.json +13 -13
  46. package/module/settings/form/admin.users.form.json +153 -153
  47. package/module/settings/form/context.account_grants.form.json +23 -23
  48. package/module/settings/form/context.account_users.form.json +12 -12
  49. package/module/settings/form/user.user_roles.form.json +13 -13
  50. package/module/settings/interface/admin.properties.json +4 -4
  51. package/module/settings/interface/admin.roles.json +4 -4
  52. package/module/settings/interface/admin.routes.json +4 -4
  53. package/module/settings/interface/admin.users.json +4 -4
  54. package/module/settings/menu.json +84 -84
  55. package/module/settings/select/core.routes.sql +1 -1
  56. package/module/settings/select/core.user_mentioned.sql +1 -1
  57. package/module/settings/select/core.user_uid.sql +1 -1
  58. package/module/settings/table/admin.accounts.table.json +42 -42
  59. package/module/settings/table/admin.custom_column.table.json +99 -99
  60. package/module/settings/table/admin.properties.table.json +39 -39
  61. package/module/settings/table/admin.roles.table.json +64 -64
  62. package/module/settings/table/admin.routes.table.json +73 -73
  63. package/module/settings/table/admin.rules.table.json +76 -76
  64. package/module/settings/table/admin.user_properties.table.json +34 -34
  65. package/module/settings/table/admin.user_roles.table.json +72 -72
  66. package/module/settings/table/admin.users.table.json +132 -132
  67. package/module/settings/table/context.account_grants.table.json +67 -67
  68. package/module/settings/table/context.account_users.table.json +37 -37
  69. package/package.json +83 -83
  70. package/plugin.js +29 -29
  71. package/server/helpers/core/badge.js +16 -16
  72. package/server/helpers/core/buttonHelper.js +21 -21
  73. package/server/helpers/core/select.js +48 -48
  74. package/server/helpers/core/token.js +18 -18
  75. package/server/helpers/index.js +29 -28
  76. package/server/helpers/list/buttonHelper.js +21 -21
  77. package/server/helpers/list/descriptionList.js +43 -43
  78. package/server/helpers/list/tableList.js +81 -81
  79. package/server/helpers/list/utils/button.js +5 -5
  80. package/server/helpers/temp/contentList.js +58 -58
  81. package/server/helpers/temp/ifCond.js +101 -101
  82. package/server/helpers/utils/button.js +5 -5
  83. package/server/helpers/utils/buttonAdd.js +5 -5
  84. package/server/helpers/utils/buttonDel.js +5 -5
  85. package/server/helpers/utils/buttonEdit.js +5 -5
  86. package/server/plugins/access/funcs/getAdminAccess.js +12 -12
  87. package/server/plugins/access/index.mjs +6 -6
  88. package/server/plugins/adminHook.js +81 -81
  89. package/server/plugins/cron.js +10 -10
  90. package/server/plugins/docs.js +28 -28
  91. package/server/plugins/hook.js +236 -230
  92. package/server/plugins/vite.js +71 -71
  93. package/server/routes/access/controllers/access.group.js +29 -29
  94. package/server/routes/access/controllers/access.group.post.js +49 -49
  95. package/server/routes/access/index.mjs +8 -8
  96. package/server/routes/access/schema.mjs +57 -57
  97. package/server/routes/calendar/controllers/calendar.data.js +87 -87
  98. package/server/routes/calendar/index.mjs +7 -7
  99. package/server/routes/calendar/schema.js +21 -21
  100. package/server/routes/data/controllers/cardData.js +105 -105
  101. package/server/routes/data/controllers/cardTabData.js +49 -49
  102. package/server/routes/data/controllers/funcs/getFilterSQL/index.js +92 -92
  103. package/server/routes/data/controllers/funcs/getFilterSQL/util/formatValue.js +170 -170
  104. package/server/routes/data/controllers/funcs/getFilterSQL/util/getCustomQuery.js +13 -13
  105. package/server/routes/data/controllers/funcs/getFilterSQL/util/getFilterQuery.js +64 -64
  106. package/server/routes/data/controllers/funcs/getFilterSQL/util/getOptimizedQuery.js +12 -12
  107. package/server/routes/data/controllers/funcs/getFilterSQL/util/getTableSql.js +34 -34
  108. package/server/routes/data/controllers/tableData.js +29 -29
  109. package/server/routes/data/controllers/tableDataId.js +27 -27
  110. package/server/routes/data/controllers/tableFilter.js +67 -67
  111. package/server/routes/data/controllers/tokenInfo.js +9 -9
  112. package/server/routes/data/controllers/utils/assignTokens.js +30 -30
  113. package/server/routes/data/controllers/utils/conditions.js +20 -20
  114. package/server/routes/data/controllers/utils/getColumns.js +8 -8
  115. package/server/routes/data/index.mjs +17 -17
  116. package/server/routes/data/schema.js +54 -54
  117. package/server/routes/menu/controllers/getMenu.js +58 -58
  118. package/server/routes/menu/index.mjs +5 -5
  119. package/server/routes/notifications/controllers/readNotifications.js +27 -27
  120. package/server/routes/notifications/controllers/testEmail.js +35 -35
  121. package/server/routes/notifications/controllers/userNotifications.js +53 -53
  122. package/server/routes/notifications/funcs/addNotification.js +21 -21
  123. package/server/routes/notifications/funcs/sendNotification.js +92 -92
  124. package/server/routes/notifications/hook/onWidgetSet.js +57 -57
  125. package/server/routes/notifications/index.mjs +27 -27
  126. package/server/routes/notifications/schema.js +16 -16
  127. package/server/routes/properties/controllers/admin.properties.get.js +29 -29
  128. package/server/routes/properties/controllers/user.properties.get.js +30 -30
  129. package/server/routes/properties/controllers/user.properties.post.js +30 -30
  130. package/server/routes/properties/funcs/getSettings.js +56 -56
  131. package/server/routes/properties/funcs/setSettings.js +44 -44
  132. package/server/routes/properties/funcs/utils/dataInsert.js +26 -26
  133. package/server/routes/properties/index.mjs +14 -14
  134. package/server/routes/properties/schema.js +10 -10
  135. package/server/routes/root.mjs +3 -3
  136. package/server/routes/templates/controllers/getTemplate.js +43 -41
  137. package/server/routes/templates/index.mjs +16 -16
  138. package/server/routes/templates/schema.js +8 -8
  139. package/server/routes/user/controllers/user.cls.id.js +14 -14
  140. package/server/routes/user/controllers/user.cls.js +71 -71
  141. package/server/routes/user/controllers/user.cls.post.js +52 -52
  142. package/server/routes/user/controllers/user.info.js +17 -17
  143. package/server/routes/user/schema.js +14 -14
  144. package/server/routes/widget/controllers/utils/historyFormat.js +75 -75
  145. package/server/routes/widget/controllers/utils/obj2db.js +13 -13
  146. package/server/routes/widget/controllers/widget.del.js +41 -41
  147. package/server/routes/widget/controllers/widget.get.js +96 -96
  148. package/server/routes/widget/controllers/widget.set.js +76 -76
  149. package/server/routes/widget/index.mjs +11 -11
  150. package/server/routes/widget/schema.js +12 -12
  151. package/server/templates/cls/itree.recrzone_category.json +73 -73
  152. package/server/templates/cls/test.json +9 -9
  153. package/server/templates/form/admin.user_cls.data.form.json +49 -49
  154. package/server/templates/form/admin.user_group_rel.form.json +21 -21
  155. package/server/templates/form/cp_building.form.json +32 -32
  156. package/server/templates/form/form-user-pass.json +10 -10
  157. package/server/templates/form/form-user_group.json +39 -39
  158. package/server/templates/form/form-users.json +156 -156
  159. package/server/templates/form/user_group_access.form.json +22 -22
  160. package/server/templates/select/account_id.json +2 -2
  161. package/server/templates/table/gis.dataset.table.json +43 -43
  162. package/server/templates/table/management.user_group.table.json +112 -112
  163. package/server/templates/table/management.users.table.json +126 -126
  164. package/utils.js +29 -29
@@ -1,231 +1,237 @@
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?.component || el?.menu?.length) && el?.ua || el?.en || el?.name);
21
-
22
- // skip dupes
23
- const q = `insert into admin.menu(name, ord) values${menuList.filter((el, idx, arr) => arr.map((el) => el?.ua || el?.en || el?.name).indexOf(el?.ua || el?.en || el?.name) === idx).map((el, i) => `('${(el?.ua || el?.en || el?.name).replace(/'/g, '’')}', ${i}) `).join(',')
24
- } on conflict (name) do update set ord=excluded.ord, enabled=true returning name, menu_id`;
25
-
26
- const { rows = [] } = menuList?.length ? await client.query(q) : {};
27
- const menus = rows.reduce((acc, curr) => Object.assign(acc, { [curr.menu_id]: menuList.find((item) => (item?.ua || item?.en || item?.name) === curr.name) }), {});
28
- const values = Object.entries(menus).reduce((acc, curr) => {
29
- if (curr[1]?.table || curr[1]?.component) { acc.push({ ...curr[1], menuId: curr[0] }); }
30
- curr[1]?.menu?.forEach((el) => acc.push({ ...el, menuId: curr[0] }));
31
- return acc;
32
- }, []);
33
-
34
- await Promise.all(values.filter((el) => el?.table).map(async (el) => {
35
- const loadTable = await getTemplate('table', el.table);
36
- Object.assign(el, {
37
- table1: loadTable?.table || el.table,
38
- actions: loadTable?.actions,
39
- access: loadTable?.access,
40
- });
41
- }));
42
-
43
- const q1 = `insert into admin.routes(route_id, alias, title, menu_id, table_name, actions, access, query)
44
- values ${values.filter((el) => el?.path).map((el) => `('${el.path}', ${el.table ? `'${el.table}'` : null},
45
- ${(el.title || el.ua) ? `'${(el.title || el.ua).replace(/'/g, "''")}'` : null},
46
- ${el.menuId ? `'${el.menuId}'` : null}, ${el.table1 ? `'${el.table1}'` : null},
47
- ${el.actions?.length ? `'{ ${el.actions} }'::text[]` : null}, ${el.access ? `'${el.access}'` : null},
48
- ${el.query ? `'${el.query.replace(/'/g, "''")}'` : '\'1=1\''})`).join(',')}
49
- on conflict (route_id) do update set menu_id=excluded.menu_id, alias=excluded.alias, title=excluded.title, enabled=true, query=excluded.query,
50
- table_name=excluded.table_name, actions=excluded.actions, access=excluded.access returning route_id, table_name`;
51
-
52
- try {
53
- console.log('admin/hook routes sql start');
54
-
55
- const { rowCount: menuCount } = await client.query(`delete from admin.menu
56
- where not array[menu_id] <@ $1::text[] and menu_id not in (select menu_id from admin.routes)`, [rows.map((el) => el.menu_id)]);
57
- console.log('delete deprecated menus ok', menuCount);
58
-
59
- const { rowCount: interfaceCount } = await client.query(`delete from admin.routes
60
- where not array[route_id] <@ $1::text[] and route_id not in (select route_id from admin.role_access)`, [values.filter((el) => el?.path)]);
61
- console.log('delete deprecated interfaces ok', interfaceCount);
62
-
63
- const { rowCount } = values?.length ? await client.query(q1) : {};
64
- console.log('insert interfaces ok', rowCount);
65
- } catch (err) {
66
- console.log('admin/hook routes sql error', values, q1, err);
67
- }
68
- }
69
- });
70
-
71
- fastify.addHook('onListen', async () => {
72
- const clsQuery = [];
73
- if (!client?.pk?.['admin.cls']) return;
74
-
75
- const selectList = await getTemplatePath('select');
76
- const clsList = (await getTemplatePath('cls'))?.filter((el) => !(selectList?.map((el) => el?.[0]) || []).includes(el[0]));
77
- const cls = (selectList || []).concat(clsList || [])
78
- ?.map((el) => ({ name: el[0], module: path.basename(path.dirname(path.dirname(el[1]))), type: { 'json': 'cls', 'sql': 'select' }[el[2]] }))
79
- if (!cls?.length) return;
80
-
81
- const dupes = cls.filter((el, idx, arr) => arr.map((item) => item.name).indexOf(el.name) !== idx);
82
- //console.log('cls insert skip dupes', dupes.map((el) => el.name));
83
-
84
- try {
85
- await Promise.all(cls.filter((el, idx, arr) => arr.map((item) => item.name).indexOf(el.name) === idx).map(async (el) => {
86
- const { name, module, type } = el;
87
- const loadTemplate = await getTemplate(type, name);
88
- // console.log(name, type);
89
- if (type === 'select') {
90
- clsQuery.push(`insert into admin.cls(name,type,data,module) values('${name}','sql','${(loadTemplate?.sql || loadTemplate)?.replace(/'/g, "''")}', '${module?.replace(/'/g, "''")}')`);
91
- } else if (type === 'cls' && loadTemplate?.length) {
92
- clsQuery.push(`insert into admin.cls(name,type, module) values('${name}','json', '${module?.replace(/'/g, "''")}');
93
- insert into admin.cls(code,name,parent,icon,data)
94
- select value->>'id',value->>'text','${name}',value->>'icon',value->>'data'
95
- from json_array_elements('${JSON.stringify(loadTemplate).replace(/'/g, "''")}'::json)`);
96
- } else {
97
- console.log(name, type, 'empty');
98
- }
99
- }));
100
-
101
- await client.query('truncate admin.cls');
102
- if (clsQuery.filter((el) => el).length) {
103
- await client.query(clsQuery.filter((el) => el).join(';'));
104
- console.log('admin/hook cls sql start', clsQuery?.length);
105
- }
106
- } catch (err) {
107
- console.error('admin/hook cls sql error', err.toString());
108
- }
109
- });
110
-
111
- addHook('afterTable', async ({ table, res = {}, payload: rows = [], user = {} }) => {
112
- const loadTable = await getTemplate('table', table);
113
- const { uid } = user1 || user;
114
- if (!uid || !table || !client?.pk?.[table] || !rows.length || !loadTable?.table) return;
115
-
116
- // admin.custom_column - user column data
117
- const { rows: properties = [] } = await client.query(`select column_id, name, title, format, data from admin.custom_column
118
- where _table and entity=$1 and uid=$2`, [table, uid]);
119
- const extraColumnList = properties.map((row) => ({ id: row.column_id, name: row.name, title: row.title, format: row.format, data: row.data }));
120
- if (!extraColumnList?.length) return;
121
-
122
- if (res?.columns?.length) {
123
- extraColumnList.forEach((col) => res.columns.push(col));
124
- }
125
-
126
- 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
127
- 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)]);
128
-
129
- if (!extraData?.length) {
130
- // Object.assign(rows?.[0] || {}, { ...extraColumnList.reduce((acc, curr) => Object.assign(acc, { [curr.name]: null }), {}) });
131
- return;
132
- }
133
-
134
- rows.filter((row) => extraData.map((el) => el?.object_id).includes(row.id)).forEach((row) => {
135
- const { extra = {} } = extraData.find((el) => el.object_id === row.id);
136
- Object.assign(row, { ...Object.fromEntries(Object.entries(extra).map((el) => [extraColumnList.find((col) => col.id === el[0]).name, el[1]])) });
137
- });
138
-
139
- // admin.custom_column - metaFormat
140
- await Promise.all(extraColumnList.filter((el) => el?.data).map(async (attr) => {
141
- const values = [...new Set(rows?.map((el) => el[attr.name]).flat())].filter((el) => el);
142
- if (!values.length) return;
143
- const cls = await getSelectVal({ name: attr.data, values });
144
- if (!cls) return;
145
- rows.forEach(el => {
146
- const val = el[attr.name]?.map?.(c => cls[c] || c) || cls[el[attr.name]] || el[attr.name];
147
- if (!val) return;
148
- Object.assign(el, { [val?.color ? `${attr.name}_data` : `${attr.name}_text`]: (val.color ? val : val.text || val) });
149
- });
150
- }));
151
- });
152
-
153
- // extract table from form token for user columns - p.2 - read (refactor to global token)
154
- addHook('preTemplate', async ({ name, type, user = {} }) => {
155
- if (!name || !type) return;
156
- const { uid } = user1 || user;
157
- const tokenData = await getToken({
158
- uid, token: name, mode: 'w', json: 1,
159
- }) // edit?
160
- || await getToken({
161
- uid, token: name, mode: 'a', json: 1,
162
- }) || {}; // add?
163
- return { name: tokenData?.[type] };
164
- });
165
-
166
- addHook('afterTemplate', async ({ name, type, payload: data = {}, user = {} }) => {
167
- const { uid } = user1 || user;
168
- // extract table from form token for user columns - p.1 - assign (refactor to global token)
169
- if (!uid || !data || type !== 'form' || !name) return null;
170
-
171
- const { form, id, table } = await getToken({
172
- uid, token: name, mode: 'w', json: 1,
173
- }) // edit?
174
- || await getToken({
175
- uid, token: name, mode: 'a', json: 1,
176
- }) || {}; // add?
177
-
178
- const { rows: properties = [] } = await client.query(`select name, title, format, data from admin.custom_column
179
- where entity=$1 and uid=$2`, [table || name, uid]);
180
-
181
- await Promise.all(properties.map(async (el) => {
182
- const clsData = el.data ? await getTemplate(['cls', 'select'], el.data) : undefined;
183
- const type = clsData ? 'Select' : ({ date: 'DatePicker' }[el.format] || 'Text');
184
- 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 } });
185
- }));
186
- });
187
-
188
- addHook('afterUpdate', async ({ table, body = {}, payload: res = {}, user = {} }) => {
189
- const { uid } = user1 || user;
190
- if (!uid || !table || !Object.keys(body)?.length) return null;
191
-
192
- const loadTable = await getTemplate('table', table);
193
- if (!client?.pk?.[loadTable?.table || table]) return null;
194
- const pk = client?.pk?.[loadTable?.table || table];
195
- const id = res[pk];
196
-
197
- const { rows: properties = [] } = await client.query(`select column_id, name, title, format, data from admin.custom_column
198
- where entity=$1 and uid=$2`, [table, uid]);
199
-
200
- if (!id || !properties?.length) return null;
201
-
202
- const q = `delete from crm.extra_data where property_entity='${table}' and object_id='${id}';${properties
203
- .filter((el) => Object.keys(body).includes(el.name))
204
- .map((el) => `insert into crm.extra_data(property_id,property_key,property_entity,object_id,${el.format?.toLowerCase() === 'date' ? 'value_date' : 'value_text'})
205
- select '${el.column_id}', '${el.name}', '${table}', '${id}', ${el.format?.toLowerCase() === 'date' ? `'${body[el.name]}'::timestamp without time zone` : `'${body[el.name]}'::text`}`)
206
- .join(';\n') || ''}`;
207
- return client.query(q);
208
- });
209
-
210
- addHook('afterInsert', async ({ table, body = {}, payload: res = {}, user = {} }) => {
211
- const { uid } = user1 || user;
212
- if (!uid || !table || !Object.keys(body)?.length) return null;
213
-
214
- const loadTable = await getTemplate('table', table);
215
- if (!client?.pk?.[loadTable?.table || table]) return null;
216
- const pk = client?.pk?.[loadTable?.table || table];
217
- const id = res.rows?.[0]?.[pk];
218
-
219
- const { rows: properties = [] } = await client.query(`select column_id, name, title, format, data from admin.custom_column
220
- where entity=$1 and uid=$2`, [table, uid]);
221
-
222
- if (!id || !properties?.length) return null;
223
-
224
- const q = properties
225
- .filter((el) => Object.keys(body).includes(el.name))
226
- .map((el) => `insert into crm.extra_data(property_id,property_key,property_entity,object_id,${el.format?.toLowerCase() === 'date' ? 'value_date' : 'value_text'})
227
- select '${el.column_id}', '${el.name}', '${table}', '${id}', ${el.format?.toLowerCase() === 'date' ? `'${body[el.name]}'::timestamp without time zone` : `'${body[el.name]}'::text`}`)
228
- .join(';\n');
229
- return client.query(q);
230
- });
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
+ addHook('preForm', async ({ form, user }) => {
17
+ if (!user?.uid) return null;
18
+ const opt = await getToken({ mode: 'w', token: form, uid: user.uid, json: 1 });
19
+ return opt;
20
+ });
21
+
22
+ fastify.addHook('onListen', async () => {
23
+ const json = await getMenu({ user: { uid: 1 } });
24
+ // insert interface list to db (user access management)
25
+ if (client?.pk?.['admin.routes'] && json?.length) {
26
+ const menuList = json.filter((el) => (el?.table || el?.component || el?.menu?.length) && el?.ua || el?.en || el?.name);
27
+
28
+ // skip dupes
29
+ const q = `insert into admin.menu(name, ord) values${menuList.filter((el, idx, arr) => arr.map((el) => el?.ua || el?.en || el?.name).indexOf(el?.ua || el?.en || el?.name) === idx).map((el, i) => `('${(el?.ua || el?.en || el?.name).replace(/'/g, '’')}', ${i}) `).join(',')
30
+ } on conflict (name) do update set ord=excluded.ord, enabled=true returning name, menu_id`;
31
+
32
+ const { rows = [] } = menuList?.length ? await client.query(q) : {};
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 || curr[1]?.component) { 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?.path).map((el) => `('${el.path}', ${el.table ? `'${el.table}'` : null},
51
+ ${(el.title || el.ua) ? `'${(el.title || el.ua).replace(/'/g, "''")}'` : null},
52
+ ${el.menuId ? `'${el.menuId}'` : null}, ${el.table1 ? `'${el.table1}'` : null},
53
+ ${el.actions?.length ? `'{ ${el.actions} }'::text[]` : null}, ${el.access ? `'${el.access}'` : null},
54
+ ${el.query ? `'${el.query.replace(/'/g, "''")}'` : '\'1=1\''})`).join(',')}
55
+ on conflict (route_id) do update set menu_id=excluded.menu_id, alias=excluded.alias, title=excluded.title, enabled=true, query=excluded.query,
56
+ table_name=excluded.table_name, actions=excluded.actions, access=excluded.access returning route_id, table_name`;
57
+
58
+ try {
59
+ console.log('admin/hook routes sql start');
60
+
61
+ const { rowCount: menuCount } = await client.query(`delete from admin.menu
62
+ where not array[menu_id] <@ $1::text[] and menu_id not in (select menu_id from admin.routes)`, [rows.map((el) => el.menu_id)]);
63
+ console.log('delete deprecated menus ok', menuCount);
64
+
65
+ const { rowCount: interfaceCount } = await client.query(`delete from admin.routes
66
+ where not array[route_id] <@ $1::text[] and route_id not in (select route_id from admin.role_access)`, [values.filter((el) => el?.path)]);
67
+ console.log('delete deprecated interfaces ok', interfaceCount);
68
+
69
+ const { rowCount } = values?.length ? await client.query(q1) : {};
70
+ console.log('insert interfaces ok', rowCount);
71
+ } catch (err) {
72
+ console.log('admin/hook routes sql error', values, q1, err);
73
+ }
74
+ }
75
+ });
76
+
77
+ fastify.addHook('onListen', async () => {
78
+ const clsQuery = [];
79
+ if (!client?.pk?.['admin.cls']) return;
80
+
81
+ const selectList = await getTemplatePath('select');
82
+ const clsList = (await getTemplatePath('cls'))?.filter((el) => !(selectList?.map((el) => el?.[0]) || []).includes(el[0]));
83
+ const cls = (selectList || []).concat(clsList || [])
84
+ ?.map((el) => ({ name: el[0], module: path.basename(path.dirname(path.dirname(el[1]))), type: { 'json': 'cls', 'sql': 'select' }[el[2]] }))
85
+ if (!cls?.length) return;
86
+
87
+ const dupes = cls.filter((el, idx, arr) => arr.map((item) => item.name).indexOf(el.name) !== idx);
88
+ //console.log('cls insert skip dupes', dupes.map((el) => el.name));
89
+
90
+ try {
91
+ await Promise.all(cls.filter((el, idx, arr) => arr.map((item) => item.name).indexOf(el.name) === idx).map(async (el) => {
92
+ const { name, module, type } = el;
93
+ const loadTemplate = await getTemplate(type, name);
94
+ // console.log(name, type);
95
+ if (type === 'select') {
96
+ clsQuery.push(`insert into admin.cls(name,type,data,module) values('${name}','sql','${(loadTemplate?.sql || loadTemplate)?.replace(/'/g, "''")}', '${module?.replace(/'/g, "''")}')`);
97
+ } else if (type === 'cls' && loadTemplate?.length) {
98
+ clsQuery.push(`insert into admin.cls(name,type, module) values('${name}','json', '${module?.replace(/'/g, "''")}');
99
+ insert into admin.cls(code,name,parent,icon,data)
100
+ select value->>'id',value->>'text','${name}',value->>'icon',value->>'data'
101
+ from json_array_elements('${JSON.stringify(loadTemplate).replace(/'/g, "''")}'::json)`);
102
+ } else {
103
+ console.log(name, type, 'empty');
104
+ }
105
+ }));
106
+
107
+ await client.query('truncate admin.cls');
108
+ if (clsQuery.filter((el) => el).length) {
109
+ await client.query(clsQuery.filter((el) => el).join(';'));
110
+ console.log('admin/hook cls sql start', clsQuery?.length);
111
+ }
112
+ } catch (err) {
113
+ console.error('admin/hook cls sql error', err.toString());
114
+ }
115
+ });
116
+
117
+ addHook('afterTable', async ({ table, res = {}, payload: rows = [], user = {} }) => {
118
+ const loadTable = await getTemplate('table', table);
119
+ const { uid } = user1 || user;
120
+ if (!uid || !table || !client?.pk?.[table] || !rows.length || !loadTable?.table) return;
121
+
122
+ // admin.custom_column - user column data
123
+ const { rows: properties = [] } = await client.query(`select column_id, name, title, format, data from admin.custom_column
124
+ where _table and entity=$1 and uid=$2`, [table, uid]);
125
+ const extraColumnList = properties.map((row) => ({ id: row.column_id, name: row.name, title: row.title, format: row.format, data: row.data }));
126
+ if (!extraColumnList?.length) return;
127
+
128
+ if (res?.columns?.length) {
129
+ extraColumnList.forEach((col) => res.columns.push(col));
130
+ }
131
+
132
+ 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
133
+ 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)]);
134
+
135
+ if (!extraData?.length) {
136
+ // Object.assign(rows?.[0] || {}, { ...extraColumnList.reduce((acc, curr) => Object.assign(acc, { [curr.name]: null }), {}) });
137
+ return;
138
+ }
139
+
140
+ rows.filter((row) => extraData.map((el) => el?.object_id).includes(row.id)).forEach((row) => {
141
+ const { extra = {} } = extraData.find((el) => el.object_id === row.id);
142
+ Object.assign(row, { ...Object.fromEntries(Object.entries(extra).map((el) => [extraColumnList.find((col) => col.id === el[0]).name, el[1]])) });
143
+ });
144
+
145
+ // admin.custom_column - metaFormat
146
+ await Promise.all(extraColumnList.filter((el) => el?.data).map(async (attr) => {
147
+ const values = [...new Set(rows?.map((el) => el[attr.name]).flat())].filter((el) => el);
148
+ if (!values.length) return;
149
+ const cls = await getSelectVal({ name: attr.data, values });
150
+ if (!cls) return;
151
+ rows.forEach(el => {
152
+ const val = el[attr.name]?.map?.(c => cls[c] || c) || cls[el[attr.name]] || el[attr.name];
153
+ if (!val) return;
154
+ Object.assign(el, { [val?.color ? `${attr.name}_data` : `${attr.name}_text`]: (val.color ? val : val.text || val) });
155
+ });
156
+ }));
157
+ });
158
+
159
+ // extract table from form token for user columns - p.2 - read (refactor to global token)
160
+ addHook('preTemplate', async ({ name, type, user = {} }) => {
161
+ if (!name || !type) return;
162
+ const { uid } = user1 || user;
163
+ const tokenData = await getToken({
164
+ uid, token: name, mode: 'w', json: 1,
165
+ }) // edit?
166
+ || await getToken({
167
+ uid, token: name, mode: 'a', json: 1,
168
+ }) || {}; // add?
169
+ return { name: tokenData?.[type] };
170
+ });
171
+
172
+ addHook('afterTemplate', async ({ name, type, payload: data = {}, user = {} }) => {
173
+ const { uid } = user1 || user;
174
+ // extract table from form token for user columns - p.1 - assign (refactor to global token)
175
+ if (!uid || !data || type !== 'form' || !name) return null;
176
+
177
+ const { form, id, table } = await getToken({
178
+ uid, token: name, mode: 'w', json: 1,
179
+ }) // edit?
180
+ || await getToken({
181
+ uid, token: name, mode: 'a', json: 1,
182
+ }) || {}; // add?
183
+
184
+ const { rows: properties = [] } = await client.query(`select name, title, format, data from admin.custom_column
185
+ where entity=$1 and uid=$2`, [table || name, uid]);
186
+
187
+ await Promise.all(properties.map(async (el) => {
188
+ const clsData = el.data ? await getTemplate(['cls', 'select'], el.data) : undefined;
189
+ const type = clsData ? 'Select' : ({ date: 'DatePicker' }[el.format] || 'Text');
190
+ 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 } });
191
+ }));
192
+ });
193
+
194
+ addHook('afterUpdate', async ({ table, body = {}, payload: res = {}, user = {} }) => {
195
+ const { uid } = user1 || user;
196
+ if (!uid || !table || !Object.keys(body)?.length) return null;
197
+
198
+ const loadTable = await getTemplate('table', table);
199
+ if (!client?.pk?.[loadTable?.table || table]) return null;
200
+ const pk = client?.pk?.[loadTable?.table || table];
201
+ const id = res[pk];
202
+
203
+ const { rows: properties = [] } = await client.query(`select column_id, name, title, format, data from admin.custom_column
204
+ where entity=$1 and uid=$2`, [table, uid]);
205
+
206
+ if (!id || !properties?.length) return null;
207
+
208
+ const q = `delete from crm.extra_data where property_entity='${table}' and object_id='${id}';${properties
209
+ .filter((el) => Object.keys(body).includes(el.name))
210
+ .map((el) => `insert into crm.extra_data(property_id,property_key,property_entity,object_id,${el.format?.toLowerCase() === 'date' ? 'value_date' : 'value_text'})
211
+ select '${el.column_id}', '${el.name}', '${table}', '${id}', ${el.format?.toLowerCase() === 'date' ? `'${body[el.name]}'::timestamp without time zone` : `'${body[el.name]}'::text`}`)
212
+ .join(';\n') || ''}`;
213
+ return client.query(q);
214
+ });
215
+
216
+ addHook('afterInsert', async ({ table, body = {}, payload: res = {}, user = {} }) => {
217
+ const { uid } = user1 || user;
218
+ if (!uid || !table || !Object.keys(body)?.length) return null;
219
+
220
+ const loadTable = await getTemplate('table', table);
221
+ if (!client?.pk?.[loadTable?.table || table]) return null;
222
+ const pk = client?.pk?.[loadTable?.table || table];
223
+ const id = res.rows?.[0]?.[pk];
224
+
225
+ const { rows: properties = [] } = await client.query(`select column_id, name, title, format, data from admin.custom_column
226
+ where entity=$1 and uid=$2`, [table, uid]);
227
+
228
+ if (!id || !properties?.length) return null;
229
+
230
+ const q = properties
231
+ .filter((el) => Object.keys(body).includes(el.name))
232
+ .map((el) => `insert into crm.extra_data(property_id,property_key,property_entity,object_id,${el.format?.toLowerCase() === 'date' ? 'value_date' : 'value_text'})
233
+ select '${el.column_id}', '${el.name}', '${table}', '${id}', ${el.format?.toLowerCase() === 'date' ? `'${body[el.name]}'::timestamp without time zone` : `'${body[el.name]}'::text`}`)
234
+ .join(';\n');
235
+ return client.query(q);
236
+ });
231
237
  }