adminforth 1.3.54-next.9 → 1.3.55-next.0

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 (250) hide show
  1. package/dist/auth.d.ts +31 -0
  2. package/dist/auth.d.ts.map +1 -0
  3. package/dist/auth.js +42 -56
  4. package/dist/auth.js.map +1 -0
  5. package/dist/basePlugin.d.ts +18 -0
  6. package/dist/basePlugin.d.ts.map +1 -0
  7. package/dist/basePlugin.js +1 -0
  8. package/dist/basePlugin.js.map +1 -0
  9. package/dist/dataConnectors/baseConnector.d.ts +94 -0
  10. package/dist/dataConnectors/baseConnector.d.ts.map +1 -0
  11. package/dist/dataConnectors/baseConnector.js +108 -122
  12. package/dist/dataConnectors/baseConnector.js.map +1 -0
  13. package/dist/dataConnectors/clickhouse.d.ts +92 -0
  14. package/dist/dataConnectors/clickhouse.d.ts.map +1 -0
  15. package/dist/dataConnectors/clickhouse.js +132 -149
  16. package/dist/dataConnectors/clickhouse.js.map +1 -0
  17. package/dist/dataConnectors/mongo.d.ts +93 -0
  18. package/dist/dataConnectors/mongo.d.ts.map +1 -0
  19. package/dist/dataConnectors/mongo.js +75 -101
  20. package/dist/dataConnectors/mongo.js.map +1 -0
  21. package/dist/dataConnectors/postgres.d.ts +71 -0
  22. package/dist/dataConnectors/postgres.d.ts.map +1 -0
  23. package/dist/dataConnectors/postgres.js +124 -143
  24. package/dist/dataConnectors/postgres.js.map +1 -0
  25. package/dist/dataConnectors/sqlite.d.ts +67 -0
  26. package/dist/dataConnectors/sqlite.d.ts.map +1 -0
  27. package/dist/dataConnectors/sqlite.js +113 -130
  28. package/dist/dataConnectors/sqlite.js.map +1 -0
  29. package/dist/index.d.ts +92 -0
  30. package/dist/index.d.ts.map +1 -0
  31. package/dist/index.js +197 -217
  32. package/dist/index.js.map +1 -0
  33. package/dist/modules/codeInjector.d.ts +35 -0
  34. package/dist/modules/codeInjector.d.ts.map +1 -0
  35. package/dist/modules/codeInjector.js +480 -486
  36. package/dist/modules/codeInjector.js.map +1 -0
  37. package/dist/modules/configValidator.d.ts +12 -0
  38. package/dist/modules/configValidator.d.ts.map +1 -0
  39. package/dist/modules/configValidator.js +31 -22
  40. package/dist/modules/configValidator.js.map +1 -0
  41. package/dist/modules/operationalResource.d.ts +17 -0
  42. package/dist/modules/operationalResource.d.ts.map +1 -0
  43. package/dist/modules/operationalResource.js +50 -70
  44. package/dist/modules/operationalResource.js.map +1 -0
  45. package/dist/modules/restApi.d.ts +10 -0
  46. package/dist/modules/restApi.d.ts.map +1 -0
  47. package/dist/modules/restApi.js +104 -116
  48. package/dist/modules/restApi.js.map +1 -0
  49. package/dist/modules/styleGenerator.d.ts +9 -0
  50. package/dist/modules/styleGenerator.d.ts.map +1 -0
  51. package/dist/modules/styleGenerator.js +1 -0
  52. package/dist/modules/styleGenerator.js.map +1 -0
  53. package/dist/modules/styles.d.ts +96 -0
  54. package/dist/modules/styles.d.ts.map +1 -0
  55. package/dist/modules/styles.js +1 -0
  56. package/dist/modules/styles.js.map +1 -0
  57. package/dist/modules/utils.d.ts +39 -0
  58. package/dist/modules/utils.d.ts.map +1 -0
  59. package/dist/modules/utils.js +1 -0
  60. package/dist/modules/utils.js.map +1 -0
  61. package/dist/plugins/audit-log/types.d.ts +35 -0
  62. package/dist/plugins/audit-log/types.d.ts.map +1 -0
  63. package/dist/plugins/audit-log/types.js +2 -0
  64. package/dist/plugins/audit-log/types.js.map +1 -0
  65. package/dist/plugins/chat-gpt/types.d.ts +82 -0
  66. package/dist/plugins/chat-gpt/types.d.ts.map +1 -0
  67. package/dist/plugins/chat-gpt/types.js +2 -0
  68. package/dist/plugins/chat-gpt/types.js.map +1 -0
  69. package/dist/plugins/email-password-reset/types.d.ts +28 -0
  70. package/dist/plugins/email-password-reset/types.d.ts.map +1 -0
  71. package/dist/plugins/email-password-reset/types.js +2 -0
  72. package/dist/plugins/email-password-reset/types.js.map +1 -0
  73. package/dist/plugins/foreign-inline-list/types.d.ts +19 -0
  74. package/dist/plugins/foreign-inline-list/types.d.ts.map +1 -0
  75. package/dist/plugins/foreign-inline-list/types.js +2 -0
  76. package/dist/plugins/foreign-inline-list/types.js.map +1 -0
  77. package/dist/plugins/import-export/types.d.ts +3 -0
  78. package/dist/plugins/import-export/types.d.ts.map +1 -0
  79. package/dist/plugins/import-export/types.js +2 -0
  80. package/dist/plugins/import-export/types.js.map +1 -0
  81. package/dist/plugins/rich-editor/custom/async-queue.d.ts +8 -0
  82. package/dist/plugins/rich-editor/custom/async-queue.d.ts.map +1 -0
  83. package/dist/plugins/rich-editor/custom/async-queue.js +29 -0
  84. package/dist/plugins/rich-editor/custom/async-queue.js.map +1 -0
  85. package/dist/plugins/rich-editor/dist/custom/async-queue.d.ts +8 -0
  86. package/dist/plugins/rich-editor/dist/custom/async-queue.d.ts.map +1 -0
  87. package/dist/plugins/rich-editor/dist/custom/async-queue.js +29 -0
  88. package/dist/plugins/rich-editor/dist/custom/async-queue.js.map +1 -0
  89. package/dist/plugins/rich-editor/types.d.ts +153 -0
  90. package/dist/plugins/rich-editor/types.d.ts.map +1 -0
  91. package/dist/plugins/rich-editor/types.js +16 -0
  92. package/dist/plugins/rich-editor/types.js.map +1 -0
  93. package/dist/plugins/two-factors-auth/types.d.ts +18 -0
  94. package/dist/plugins/two-factors-auth/types.d.ts.map +1 -0
  95. package/dist/plugins/two-factors-auth/types.js +2 -0
  96. package/dist/plugins/two-factors-auth/types.js.map +1 -0
  97. package/dist/plugins/upload/types.d.ts +132 -0
  98. package/dist/plugins/upload/types.d.ts.map +1 -0
  99. package/dist/plugins/upload/types.js +2 -0
  100. package/dist/plugins/upload/types.js.map +1 -0
  101. package/dist/servers/express.d.ts +18 -0
  102. package/dist/servers/express.d.ts.map +1 -0
  103. package/dist/servers/express.js +30 -42
  104. package/dist/servers/express.js.map +1 -0
  105. package/dist/types/AdminForthConfig.d.ts +1619 -0
  106. package/dist/types/AdminForthConfig.d.ts.map +1 -0
  107. package/dist/types/AdminForthConfig.js +1 -0
  108. package/dist/types/AdminForthConfig.js.map +1 -0
  109. package/{types/FrontendAPI.ts → dist/types/FrontendAPI.d.ts} +27 -52
  110. package/dist/types/FrontendAPI.d.ts.map +1 -0
  111. package/dist/types/FrontendAPI.js +1 -0
  112. package/dist/types/FrontendAPI.js.map +1 -0
  113. package/package.json +15 -5
  114. package/auth.ts +0 -140
  115. package/basePlugin.ts +0 -70
  116. package/dataConnectors/baseConnector.ts +0 -216
  117. package/dataConnectors/clickhouse.ts +0 -338
  118. package/dataConnectors/mongo.ts +0 -202
  119. package/dataConnectors/postgres.ts +0 -306
  120. package/dataConnectors/sqlite.ts +0 -254
  121. package/dist/spa/.eslintrc.cjs +0 -14
  122. package/dist/spa/README.md +0 -39
  123. package/dist/spa/env.d.ts +0 -1
  124. package/dist/spa/index.html +0 -23
  125. package/dist/spa/package-lock.json +0 -4573
  126. package/dist/spa/package.json +0 -49
  127. package/dist/spa/postcss.config.js +0 -6
  128. package/dist/spa/public/assets/favicon.png +0 -0
  129. package/dist/spa/src/App.vue +0 -314
  130. package/dist/spa/src/assets/base.css +0 -2
  131. package/dist/spa/src/assets/logo.svg +0 -19
  132. package/dist/spa/src/components/AcceptModal.vue +0 -45
  133. package/dist/spa/src/components/Breadcrumbs.vue +0 -41
  134. package/dist/spa/src/components/BreadcrumbsWithButtons.vue +0 -26
  135. package/dist/spa/src/components/CustomDatePicker.vue +0 -176
  136. package/dist/spa/src/components/CustomDateRangePicker.vue +0 -218
  137. package/dist/spa/src/components/CustomRangePicker.vue +0 -152
  138. package/dist/spa/src/components/Dropdown.vue +0 -168
  139. package/dist/spa/src/components/Filters.vue +0 -222
  140. package/dist/spa/src/components/HelloWorld.vue +0 -17
  141. package/dist/spa/src/components/MenuLink.vue +0 -24
  142. package/dist/spa/src/components/ResourceForm.vue +0 -294
  143. package/dist/spa/src/components/ResourceListTable.vue +0 -394
  144. package/dist/spa/src/components/SingleSkeletLoader.vue +0 -13
  145. package/dist/spa/src/components/SkeleteLoader.vue +0 -23
  146. package/dist/spa/src/components/Toast.vue +0 -77
  147. package/dist/spa/src/components/ValueRenderer.vue +0 -66
  148. package/dist/spa/src/components/icons/IconCalendar.vue +0 -5
  149. package/dist/spa/src/components/icons/IconCommunity.vue +0 -7
  150. package/dist/spa/src/components/icons/IconDocumentation.vue +0 -7
  151. package/dist/spa/src/components/icons/IconEcosystem.vue +0 -7
  152. package/dist/spa/src/components/icons/IconSupport.vue +0 -7
  153. package/dist/spa/src/components/icons/IconTime.vue +0 -5
  154. package/dist/spa/src/components/icons/IconTooling.vue +0 -19
  155. package/dist/spa/src/composables/useFrontendApi.ts +0 -26
  156. package/dist/spa/src/composables/useStores.ts +0 -127
  157. package/dist/spa/src/index.scss +0 -27
  158. package/dist/spa/src/main.ts +0 -18
  159. package/dist/spa/src/router/index.ts +0 -63
  160. package/dist/spa/src/spa_types/core.ts +0 -51
  161. package/dist/spa/src/stores/core.ts +0 -144
  162. package/dist/spa/src/stores/filters.ts +0 -22
  163. package/dist/spa/src/stores/modal.ts +0 -48
  164. package/dist/spa/src/stores/toast.ts +0 -15
  165. package/dist/spa/src/stores/user.ts +0 -72
  166. package/dist/spa/src/types/AdminForthConfig.ts +0 -1477
  167. package/dist/spa/src/types/FrontendAPI.ts +0 -121
  168. package/dist/spa/src/utils.ts +0 -103
  169. package/dist/spa/src/views/CreateView.vue +0 -156
  170. package/dist/spa/src/views/EditView.vue +0 -157
  171. package/dist/spa/src/views/ListView.vue +0 -258
  172. package/dist/spa/src/views/LoginView.vue +0 -160
  173. package/dist/spa/src/views/ResourceParent.vue +0 -17
  174. package/dist/spa/src/views/ShowView.vue +0 -184
  175. package/dist/spa/tailwind.config.js +0 -17
  176. package/dist/spa/tsconfig.app.json +0 -14
  177. package/dist/spa/tsconfig.json +0 -11
  178. package/dist/spa/tsconfig.node.json +0 -19
  179. package/dist/spa/vite.config.ts +0 -56
  180. package/index.ts +0 -428
  181. package/modules/codeInjector.ts +0 -736
  182. package/modules/configValidator.ts +0 -571
  183. package/modules/operationalResource.ts +0 -98
  184. package/modules/restApi.ts +0 -718
  185. package/modules/styleGenerator.ts +0 -55
  186. package/modules/styles.ts +0 -126
  187. package/modules/utils.ts +0 -472
  188. package/servers/express.ts +0 -259
  189. package/spa/.eslintrc.cjs +0 -14
  190. package/spa/README.md +0 -39
  191. package/spa/env.d.ts +0 -1
  192. package/spa/index.html +0 -23
  193. package/spa/package-lock.json +0 -4602
  194. package/spa/package.json +0 -51
  195. package/spa/postcss.config.js +0 -6
  196. package/spa/public/assets/favicon.png +0 -0
  197. package/spa/src/App.vue +0 -418
  198. package/spa/src/assets/base.css +0 -2
  199. package/spa/src/assets/logo.svg +0 -19
  200. package/spa/src/components/AcceptModal.vue +0 -45
  201. package/spa/src/components/Breadcrumbs.vue +0 -41
  202. package/spa/src/components/BreadcrumbsWithButtons.vue +0 -26
  203. package/spa/src/components/CustomDatePicker.vue +0 -176
  204. package/spa/src/components/CustomDateRangePicker.vue +0 -218
  205. package/spa/src/components/CustomRangePicker.vue +0 -156
  206. package/spa/src/components/Dropdown.vue +0 -168
  207. package/spa/src/components/Filters.vue +0 -222
  208. package/spa/src/components/HelloWorld.vue +0 -17
  209. package/spa/src/components/MenuLink.vue +0 -27
  210. package/spa/src/components/ResourceForm.vue +0 -290
  211. package/spa/src/components/ResourceListTable.vue +0 -466
  212. package/spa/src/components/SingleSkeletLoader.vue +0 -13
  213. package/spa/src/components/SkeleteLoader.vue +0 -23
  214. package/spa/src/components/ThreeDotsMenu.vue +0 -43
  215. package/spa/src/components/Toast.vue +0 -78
  216. package/spa/src/components/ValueRenderer.vue +0 -114
  217. package/spa/src/components/icons/IconCalendar.vue +0 -5
  218. package/spa/src/components/icons/IconCommunity.vue +0 -7
  219. package/spa/src/components/icons/IconDocumentation.vue +0 -7
  220. package/spa/src/components/icons/IconEcosystem.vue +0 -7
  221. package/spa/src/components/icons/IconSupport.vue +0 -7
  222. package/spa/src/components/icons/IconTime.vue +0 -5
  223. package/spa/src/components/icons/IconTooling.vue +0 -19
  224. package/spa/src/composables/useFrontendApi.ts +0 -26
  225. package/spa/src/composables/useStores.ts +0 -131
  226. package/spa/src/index.scss +0 -31
  227. package/spa/src/main.ts +0 -18
  228. package/spa/src/renderers/CompactUUID.vue +0 -48
  229. package/spa/src/renderers/CountryFlag.vue +0 -69
  230. package/spa/src/router/index.ts +0 -59
  231. package/spa/src/spa_types/core.ts +0 -53
  232. package/spa/src/stores/core.ts +0 -148
  233. package/spa/src/stores/filters.ts +0 -27
  234. package/spa/src/stores/modal.ts +0 -48
  235. package/spa/src/stores/toast.ts +0 -31
  236. package/spa/src/stores/user.ts +0 -72
  237. package/spa/src/utils.ts +0 -160
  238. package/spa/src/views/CreateView.vue +0 -167
  239. package/spa/src/views/EditView.vue +0 -170
  240. package/spa/src/views/ListView.vue +0 -352
  241. package/spa/src/views/LoginView.vue +0 -192
  242. package/spa/src/views/ResourceParent.vue +0 -17
  243. package/spa/src/views/ShowView.vue +0 -186
  244. package/spa/tailwind.config.js +0 -17
  245. package/spa/tsconfig.app.json +0 -14
  246. package/spa/tsconfig.json +0 -11
  247. package/spa/tsconfig.node.json +0 -19
  248. package/spa/vite.config.ts +0 -56
  249. package/tsconfig.json +0 -112
  250. package/types/AdminForthConfig.ts +0 -1762
@@ -1,718 +0,0 @@
1
- import {
2
- type IAdminForth,
3
- type IHttpServer, type AdminUser,
4
- AdminForthFilterOperators,
5
- AdminForthDataTypes,
6
- BeforeLoginConfirmationFunction,
7
- AdminForthResource,
8
- AllowedActionValue,
9
- AllowedActionsEnum,
10
- AllowedActions,
11
- ActionCheckSource,
12
- BeforeSaveFunction,
13
- AfterDataSourceResponseFunction,
14
- BeforeDataSourceRequestFunction,
15
- AfterSaveFunction,
16
- AllowedActionsResolved,
17
- AdminForthResourcePages
18
-
19
- } from "../types/AdminForthConfig.js";
20
-
21
- import { ADMINFORTH_VERSION, listify } from './utils.js';
22
-
23
- import AdminForthAuth from "../auth.js";
24
-
25
-
26
- export async function interpretResource(adminUser: AdminUser, resource: AdminForthResource, meta: any, source: ActionCheckSource): Promise<{allowedActions: AllowedActionsResolved}> {
27
- // if (process.env.HEAVY_DEBUG) {
28
- // console.log('🪲Interpreting resource', resource.resourceId, source, 'adminUser', adminUser);
29
- // }
30
- const allowedActions = {};
31
-
32
- await Promise.all(
33
- Object.entries(resource.options?.allowedActions || {}).map(
34
- async ([key, value]: [string, AllowedActionValue]) => {
35
- if (process.env.HEAVY_DEBUG) {
36
- console.log(`🪲🚥check allowed ${key}, ${value}`)
37
- }
38
-
39
- // if callable then call
40
- if (typeof value === 'function') {
41
- allowedActions[key] = await value({ adminUser, resource, meta, source });
42
- } else {
43
- allowedActions[key] = value;
44
- }
45
- })
46
- );
47
-
48
- return { allowedActions };
49
- }
50
-
51
- export default class AdminForthRestAPI {
52
-
53
- adminforth: IAdminForth;
54
-
55
- constructor(adminforth: IAdminForth) {
56
- this.adminforth = adminforth;
57
- }
58
-
59
- registerEndpoints(server: IHttpServer) {
60
- server.endpoint({
61
- noAuth: true,
62
- method: 'POST',
63
- path: '/login',
64
- handler: async ({ body, response }) => {
65
-
66
- const INVALID_MESSAGE = 'Invalid Username or Password';
67
- const { username, password, rememberMe } = body;
68
- let adminUser: AdminUser;
69
- let toReturn: { ok: boolean, redirectTo?: string, allowedLogin:boolean } = { ok: true, allowedLogin:true};
70
-
71
- // get resource from db
72
- if (!this.adminforth.config.auth) {
73
- throw new Error('No config.auth defined we need it to find user, please follow the docs');
74
- }
75
- const userResource = this.adminforth.config.resources.find((res) => res.resourceId === this.adminforth.config.auth.usersResourceId);
76
- // if there is no passwordHashField, in columns, add it, with backendOnly and showIn: []
77
- if (!userResource.dataSourceColumns.find((col) => col.name === this.adminforth.config.auth.passwordHashField)) {
78
- userResource.dataSourceColumns.push({
79
- name: this.adminforth.config.auth.passwordHashField,
80
- backendOnly: true,
81
- showIn: [],
82
- type: AdminForthDataTypes.STRING,
83
- });
84
- console.log('Adding passwordHashField to userResource', userResource)
85
- }
86
-
87
- const userRecord = (
88
- await this.adminforth.connectors[userResource.dataSource].getData({
89
- resource: userResource,
90
- filters: [
91
- { field: this.adminforth.config.auth.usernameField, operator: AdminForthFilterOperators.EQ, value: username },
92
- ],
93
- limit: 1,
94
- offset: 0,
95
- sort: [],
96
- })
97
- ).data?.[0];
98
-
99
-
100
- if (!userRecord) {
101
- return { error: INVALID_MESSAGE };
102
- }
103
-
104
- const passwordHash = userRecord[this.adminforth.config.auth.passwordHashField];
105
- const valid = await AdminForthAuth.verifyPassword(password, passwordHash);
106
- if (valid) {
107
- adminUser = {
108
- dbUser: userRecord,
109
- pk: userRecord[userResource.columns.find((col) => col.primaryKey).name],
110
- username,
111
- };
112
- const beforeLoginConfirmation = this.adminforth.config.auth.beforeLoginConfirmation as (BeforeLoginConfirmationFunction[] | undefined);
113
- if (beforeLoginConfirmation?.length){
114
- for (const hook of beforeLoginConfirmation) {
115
- const resp = await hook({ adminUser, response });
116
-
117
- if (resp?.body?.redirectTo) {
118
- toReturn = {ok:resp.ok, redirectTo:resp?.body?.redirectTo, allowedLogin:resp?.body?.allowedLogin};
119
- break;
120
- }
121
- }
122
- }
123
- if (toReturn.allowedLogin){
124
- const expireInDays = rememberMe && this.adminforth.config.auth.rememberMeDays;
125
- this.adminforth.auth.setAuthCookie({
126
- expireInDays,
127
- response,
128
- username,
129
- pk: userRecord[userResource.columns.find((col) => col.primaryKey).name]
130
- });
131
- }
132
- } else {
133
- return { error: INVALID_MESSAGE };
134
- }
135
-
136
-
137
- return toReturn;
138
- }
139
- });
140
-
141
- server.endpoint({
142
- method: 'POST',
143
- path: '/check_auth',
144
- handler: async ({ adminUser }) => {
145
- return { ok: true };
146
- },
147
- });
148
-
149
- server.endpoint({
150
- noAuth: true,
151
- method: 'POST',
152
- path: '/logout',
153
- handler: async ({ response }) => {
154
- this.adminforth.auth.removeAuthCookie( response );
155
- return { ok: true };
156
- },
157
- })
158
-
159
- server.endpoint({
160
- noAuth: true,
161
- method: 'GET',
162
- path: '/get_public_config',
163
- handler: async ({ body }) => {
164
-
165
- // find resource
166
- if (!this.adminforth.config.auth) {
167
- throw new Error('No config.auth defined');
168
- }
169
- const usernameField = this.adminforth.config.auth.usernameField;
170
- const resource = this.adminforth.config.resources.find((res) => res.resourceId === this.adminforth.config.auth.usersResourceId);
171
- const usernameColumn = resource.columns.find((col) => col.name === usernameField);
172
-
173
- return {
174
- brandName: this.adminforth.config.customization.brandName,
175
- usernameFieldName: usernameColumn.label,
176
- loginBackgroundImage: this.adminforth.config.auth.loginBackgroundImage,
177
- loginBackgroundPosition: this.adminforth.config.auth.loginBackgroundPosition,
178
- title: this.adminforth.config.customization?.title,
179
- demoCredentials: this.adminforth.config.auth.demoCredentials,
180
- loginPromptHTML: this.adminforth.config.auth.loginPromptHTML,
181
- loginPageInjections: this.adminforth.config.customization.loginPageInjections,
182
- rememberMeDays: this.adminforth.config.auth.rememberMeDays,
183
- };
184
- },
185
- });
186
-
187
- server.endpoint({
188
- method: 'GET',
189
- path: '/get_base_config',
190
- handler: async ({input, adminUser, cookies}) => {
191
- let username = ''
192
- let userFullName = ''
193
-
194
- const dbUser = adminUser.dbUser;
195
- username = dbUser[this.adminforth.config.auth.usernameField];
196
- userFullName = dbUser[this.adminforth.config.auth.userFullNameField];
197
- const userResource = this.adminforth.config.resources.find((res) => res.resourceId === this.adminforth.config.auth.usersResourceId);
198
-
199
- const userPk = dbUser[userResource.columns.find((col) => col.primaryKey).name];
200
-
201
- const userData = {
202
- [this.adminforth.config.auth.usernameField]: username,
203
- [this.adminforth.config.auth.userFullNameField]: userFullName,
204
- pk: userPk,
205
- };
206
- const checkIsMenuItemVisible = (menuItem) => {
207
- if (typeof menuItem.visible === 'function') {
208
- const toReturn = menuItem.visible( adminUser );
209
- if (typeof toReturn !== 'boolean') {
210
- throw new Error(`'visible' function of ${menuItem.label || menuItem.type } must return boolean value`);
211
- }
212
- return toReturn;
213
- }
214
-
215
-
216
- }
217
-
218
- async function processMenuItem(menuItem) {
219
- if (menuItem.badge) {
220
- if (typeof menuItem.badge === 'function') {
221
- menuItem.badge = await menuItem.badge(adminUser);
222
- }
223
- }
224
- }
225
- let newMenu = []
226
- for (let menuItem of this.adminforth.config.menu) {
227
- let newMenuItem = {...menuItem,}
228
- if (menuItem.visible){
229
- if (!checkIsMenuItemVisible(menuItem)){
230
- continue
231
- }
232
- }
233
- if (menuItem.children){
234
- let newChildren = []
235
- for (let child of menuItem.children){
236
- let newChild = {...child,}
237
- if (child.visible){
238
- if (!checkIsMenuItemVisible(child)){
239
- continue
240
- }
241
- }
242
- await processMenuItem(newChild)
243
- newChildren.push(newChild)
244
- }
245
- newMenuItem = {...newMenuItem, children: newChildren}
246
- }
247
- await processMenuItem(newMenuItem)
248
- newMenu.push(newMenuItem)
249
- }
250
-
251
- const announcementBadge = this.adminforth.config.customization.announcementBadge?.(adminUser);
252
-
253
- return {
254
- user: userData,
255
- resources: this.adminforth.config.resources.map((res) => ({
256
- resourceId: res.resourceId,
257
- label: res.label,
258
- })),
259
- menu: newMenu,
260
- config: {
261
- brandName: this.adminforth.config.customization.brandName,
262
- showBrandNameInSidebar: this.adminforth.config.customization.showBrandNameInSidebar,
263
- brandLogo: this.adminforth.config.customization.brandLogo,
264
- datesFormat: this.adminforth.config.customization.datesFormat,
265
- timeFormat: this.adminforth.config.customization.timeFormat,
266
- deleteConfirmation: this.adminforth.config.deleteConfirmation,
267
- auth: this.adminforth.config.auth,
268
- usernameField: this.adminforth.config.auth.usernameField,
269
- title: this.adminforth.config.customization?.title,
270
- emptyFieldPlaceholder: this.adminforth.config.customization?.emptyFieldPlaceholder,
271
- announcementBadge,
272
- globalInjections: this.adminforth.config.customization?.globalInjections,
273
- },
274
- adminUser,
275
- version: ADMINFORTH_VERSION,
276
- };
277
- },
278
- });
279
-
280
- function checkAccess(action: AllowedActionsEnum, allowedActions: AllowedActions): { allowed: boolean, error?: string } {
281
- const allowed = (allowedActions[action] as boolean | string | undefined);
282
- if (allowed !== true) {
283
- return { error: typeof allowed === 'string' ? allowed : 'Action is not allowed', allowed: false };
284
- }
285
- return { allowed: true };
286
- }
287
-
288
- server.endpoint({
289
- method: 'POST',
290
- path: '/get_resource',
291
- handler: async ({ body, adminUser }) => {
292
- const { resourceId } = body;
293
- if (!this.adminforth.statuses.dbDiscover) {
294
- return { error: 'Database discovery not started' };
295
- }
296
- if (this.adminforth.statuses.dbDiscover !== 'done') {
297
- return { error : 'Database discovery is still in progress, please try later' };
298
- }
299
- const resource = this.adminforth.config.resources.find((res) => res.resourceId == resourceId);
300
- if (!resource) {
301
- return { error: `Resource ${resourceId} not found` };
302
- }
303
-
304
- const { allowedActions } = await interpretResource(adminUser, resource, {}, ActionCheckSource.DisplayButtons);
305
-
306
-
307
- const allowedBulkActions = [];
308
- await Promise.all(
309
- resource.options.bulkActions.map(async (action) => {
310
- if (action.allowed) {
311
- const res = await action.allowed({ adminUser, resource, allowedActions });
312
- if (res) {
313
- allowedBulkActions.push(action);
314
- }
315
- } else {
316
- allowedBulkActions.push(action);
317
- }
318
- })
319
- );
320
-
321
- // exclude "plugins" key
322
- return {
323
- resource: {
324
- ...resource,
325
- plugins: undefined,
326
- options: {
327
- ...resource.options,
328
- bulkActions: allowedBulkActions,
329
- allowedActions,
330
- }
331
- }
332
- };
333
- },
334
- });
335
- server.endpoint({
336
- method: 'POST',
337
- path: '/get_resource_data',
338
- handler: async ({ body, adminUser }) => {
339
-
340
- const { resourceId, source } = body;
341
- if (['show', 'list'].includes(source) === false) {
342
- return { error: 'Invalid source, should be list or show' };
343
- }
344
- if (!this.adminforth.statuses.dbDiscover) {
345
- return { error: 'Database discovery not started' };
346
- }
347
- if (this.adminforth.statuses.dbDiscover !== 'done') {
348
- return { error : 'Database discovery is still in progress, please try later' };
349
- }
350
- const resource = this.adminforth.config.resources.find((res) => res.resourceId == resourceId);
351
- if (!resource) {
352
- return { error: `Resource ${resourceId} not found` };
353
- }
354
-
355
- const { allowedActions } = await interpretResource(adminUser, resource, { requestBody: body }, ActionCheckSource.DisplayButtons);
356
-
357
- const { allowed, error } = checkAccess(source as AllowedActionsEnum, allowedActions);
358
- if (!allowed) {
359
- return { error };
360
- }
361
-
362
- for (const hook of listify(resource.hooks?.[source]?.beforeDatasourceRequest)) {
363
- const resp = await hook({ resource, query: body, adminUser });
364
- if (!resp || (!resp.ok && !resp.error)) {
365
- throw new Error(`Hook must return object with {ok: true} or { error: 'Error' } `);
366
- }
367
-
368
- if (resp.error) {
369
- return { error: resp.error };
370
- }
371
- }
372
- const { limit, offset, filters, sort } = body;
373
-
374
- for (const filter of (filters || [])) {
375
- if (!Object.values(AdminForthFilterOperators).includes(filter.operator)) {
376
- throw new Error(`Operator '${filter.operator}' is not allowed`);
377
- }
378
-
379
- if (!resource.columns.some((col) => col.name === filter.field)) {
380
- throw new Error(`Field '${filter.field}' is not in resource '${resource.resourceId}'. Available fields: ${resource.columns.map((col) => col.name).join(', ')}`);
381
- }
382
-
383
- if (filter.operator === AdminForthFilterOperators.IN || filter.operator === AdminForthFilterOperators.NIN) {
384
- if (!Array.isArray(filter.value)) {
385
- throw new Error(`Value for operator '${filter.operator}' should be an array`);
386
- }
387
- }
388
-
389
- if (filter.operator === AdminForthFilterOperators.IN && filter.value.length === 0) {
390
- // nonsense
391
- return { data: [], total: 0 };
392
- }
393
- }
394
-
395
- const data = await this.adminforth.connectors[resource.dataSource].getData({
396
- resource,
397
- limit,
398
- offset,
399
- filters,
400
- sort,
401
- getTotals: true,
402
- });
403
- // for foreign keys, add references
404
- await Promise.all(
405
- resource.columns.filter((col) => col.foreignResource).map(async (col) => {
406
- const targetResource = this.adminforth.config.resources.find((res) => res.resourceId == col.foreignResource.resourceId);
407
- const targetConnector = this.adminforth.connectors[targetResource.dataSource];
408
- const targetResourcePkField = targetResource.columns.find((col) => col.primaryKey).name;
409
- const pksUnique = [...new Set(data.data.map((item) => item[col.name]))];
410
- if (pksUnique.length === 0) {
411
- return;
412
- }
413
- const targetData = await targetConnector.getData({
414
- resource: targetResource,
415
- limit: limit,
416
- offset: 0,
417
- filters: [
418
- {
419
- field: targetResourcePkField,
420
- operator: AdminForthFilterOperators.IN,
421
- value: pksUnique,
422
- }
423
- ],
424
- sort: [],
425
- });
426
- const targetDataMap = targetData.data.reduce((acc, item) => {
427
- acc[item[targetResourcePkField]] = {
428
- label: targetResource.recordLabel(item),
429
- pk: item[targetResourcePkField],
430
- }
431
- return acc;
432
- }, {});
433
- data.data.forEach((item) => {
434
- item[col.name] = targetDataMap[item[col.name]];
435
- });
436
- })
437
- );
438
-
439
- // remove all columns which are not defined in resources, or defined but backendOnly
440
- data.data.forEach((item) => {
441
- Object.keys(item).forEach((key) => {
442
- if (!resource.columns.find((col) => col.name === key) || resource.columns.find((col) => col.name === key && col.backendOnly)) {
443
- delete item[key];
444
- }
445
- })
446
- item._label = resource.recordLabel(item);
447
- });
448
- if (resource.options.listTableClickUrl) {
449
- await Promise.all(
450
- data.data.map(async (item) => {
451
- item._clickUrl = await resource.options.listTableClickUrl(item, adminUser);
452
- })
453
- );
454
- }
455
-
456
- // only after adminforth made all post processing, give user ability to edit it
457
- for (const hook of listify(resource.hooks?.[source]?.afterDatasourceResponse)) {
458
- const resp = await hook({ resource, response: data.data, adminUser });
459
- if (!resp || (!resp.ok && !resp.error)) {
460
- throw new Error(`Hook must return object with {ok: true} or { error: 'Error' } `);
461
- }
462
-
463
- if (resp.error) {
464
- return { error: resp.error };
465
- }
466
- }
467
-
468
- return {
469
- ...data,
470
- options: resource?.options,
471
- };
472
- },
473
- });
474
- server.endpoint({
475
- method: 'POST',
476
- path: '/get_resource_foreign_data',
477
- handler: async ({ body, adminUser }) => {
478
- const { resourceId, column } = body;
479
- if (!this.adminforth.statuses.dbDiscover) {
480
- return { error: 'Database discovery not started' };
481
- }
482
- if (this.adminforth.statuses.dbDiscover !== 'done') {
483
- return { error : 'Database discovery is still in progress, please try later' };
484
- }
485
- const resource = this.adminforth.config.resources.find((res) => res.resourceId == resourceId);
486
- if (!resource) {
487
- return { error: `Resource '${resourceId}' not found` };
488
- }
489
- const columnConfig = resource.columns.find((col) => col.name == column);
490
- if (!columnConfig) {
491
- return { error: `Column "${column}' not found in resource with resourceId '${resourceId}'` };
492
- }
493
- if (!columnConfig.foreignResource) {
494
- return { error: `Column '${column}' in resource '${resourceId}' is not a foreign key` };
495
- }
496
- const targetResourceId = columnConfig.foreignResource.resourceId;
497
- const targetResource = this.adminforth.config.resources.find((res) => res.resourceId == targetResourceId);
498
-
499
- for (const hook of listify(columnConfig.foreignResource.hooks?.dropdownList?.beforeDatasourceRequest as BeforeDataSourceRequestFunction[])) {
500
- const resp = await hook({ query: body, adminUser, resource: targetResource });
501
- if (!resp || (!resp.ok && !resp.error)) {
502
- throw new Error(`Hook must return object with {ok: true} or { error: 'Error' } `);
503
- }
504
-
505
- if (resp.error) {
506
- return { error: resp.error };
507
- }
508
- }
509
- const { limit, offset, filters, sort } = body;
510
- const dbDataItems = await this.adminforth.connectors[targetResource.dataSource].getData({
511
- resource: targetResource,
512
- limit,
513
- offset,
514
- filters: filters || [],
515
- sort: sort || [],
516
- });
517
- const items = dbDataItems.data.map((item) => {
518
- const pk = item[targetResource.columns.find((col) => col.primaryKey).name];
519
- const labler = targetResource.recordLabel;
520
- return {
521
- value: pk,
522
- label: labler(item),
523
- _item: item, // user might need it in hook to form new label
524
- }
525
- });
526
- const response = {
527
- items
528
- };
529
-
530
- for (const hook of listify(columnConfig.foreignResource.hooks?.dropdownList?.afterDatasourceResponse as AfterDataSourceResponseFunction[])) {
531
- const resp = await hook({ response, adminUser, resource: targetResource });
532
- if (!resp || (!resp.ok && !resp.error)) {
533
- throw new Error(`Hook must return object with {ok: true} or { error: 'Error' } `);
534
- }
535
-
536
- if (resp.error) {
537
- return { error: resp.error };
538
- }
539
- }
540
-
541
- return response;
542
- },
543
- });
544
-
545
- server.endpoint({
546
- method: 'POST',
547
- path: '/get_min_max_for_columns',
548
- handler: async ({ body }) => {
549
- const { resourceId } = body;
550
- if (!this.adminforth.statuses.dbDiscover) {
551
- return { error: 'Database discovery not started' };
552
- }
553
- if (this.adminforth.statuses.dbDiscover !== 'done') {
554
- return { error : 'Database discovery is still in progress, please try later' };
555
- }
556
- const resource = this.adminforth.config.resources.find((res) => res.resourceId == resourceId);
557
- if (!resource) {
558
- return { error: `Resource '${resourceId}' not found` };
559
- }
560
- const item = await this.adminforth.connectors[resource.dataSource].getMinMaxForColumns({
561
- resource,
562
- columns: resource.columns.filter((col) => [
563
- AdminForthDataTypes.INTEGER,
564
- AdminForthDataTypes.FLOAT,
565
- AdminForthDataTypes.DATE,
566
- AdminForthDataTypes.DATETIME,
567
- AdminForthDataTypes.TIME,
568
- AdminForthDataTypes.DECIMAL,
569
- ].includes(col.type) && col.allowMinMaxQuery === true),
570
- });
571
- return item;
572
- },
573
- });
574
- server.endpoint({
575
- method: 'POST',
576
- path: '/create_record',
577
- handler: async ({ body, adminUser }) => {
578
- const resource = this.adminforth.config.resources.find((res) => res.resourceId == body['resourceId']);
579
- if (!resource) {
580
- return { error: `Resource '${body['resourceId']}' not found` };
581
- }
582
- const { allowedActions } = await interpretResource(adminUser, resource, { requestBody: body}, ActionCheckSource.CreateRequest);
583
-
584
- const { allowed, error } = checkAccess(AllowedActionsEnum.create, allowedActions);
585
- if (!allowed) {
586
- return { error };
587
- }
588
-
589
- const { record } = body;
590
-
591
- for (const column of resource.columns) {
592
- if (
593
- (column.required as {create?: boolean, edit?: boolean})?.create &&
594
- record[column.name] === undefined &&
595
- column.showIn.includes(AdminForthResourcePages.create)
596
- ) {
597
- return { error: `Column '${column.name}' is required`, ok: false };
598
- }
599
- }
600
-
601
- const response = await this.adminforth.createResourceRecord({ resource, record, adminUser });
602
- if (response.error) {
603
- return { error: response.error, ok: false };
604
- }
605
- const connector = this.adminforth.connectors[resource.dataSource];
606
-
607
- return {
608
- newRecordId: response.createdRecord[connector.getPrimaryKey(resource)],
609
- ok: true
610
- }
611
- }
612
- });
613
- server.endpoint({
614
- method: 'POST',
615
- path: '/update_record',
616
- handler: async ({ body, adminUser }) => {
617
- const resource = this.adminforth.config.resources.find((res) => res.resourceId == body['resourceId']);
618
- if (!resource) {
619
- return { error: `Resource '${body['resourceId']}' not found` };
620
- }
621
-
622
- const recordId = body['recordId'];
623
- const connector = this.adminforth.connectors[resource.dataSource];
624
- const oldRecord = await connector.getRecordByPrimaryKey(resource, recordId)
625
- if (!oldRecord) {
626
- const primaryKeyColumn = resource.columns.find((col) => col.primaryKey);
627
- return { error: `Record with ${primaryKeyColumn.name} ${recordId} not found` };
628
- }
629
- const record = body['record'];
630
-
631
- const { allowedActions } = await interpretResource(adminUser, resource, { requestBody: body, newRecord: record, oldRecord}, ActionCheckSource.EditRequest);
632
-
633
- const { allowed, error: allowedError } = checkAccess(AllowedActionsEnum.edit, allowedActions);
634
- if (!allowed) {
635
- return { allowedError };
636
- }
637
-
638
- const { error } = await this.adminforth.updateResourceRecord({ resource, record, adminUser, oldRecord, recordId });
639
- if (error) {
640
- return { error };
641
- }
642
- return {
643
- ok: true
644
- }
645
- }
646
- });
647
- server.endpoint({
648
- method: 'POST',
649
- path: '/delete_record',
650
- handler: async ({ body, adminUser }) => {
651
- const resource = this.adminforth.config.resources.find((res) => res.resourceId == body['resourceId']);
652
- const record = await this.adminforth.connectors[resource.dataSource].getRecordByPrimaryKey(resource, body['primaryKey']);
653
- if (!resource) {
654
- return { error: `Resource '${body['resourceId']}' not found` };
655
- }
656
- if (!record){
657
- return { error: `Record with ${body['primaryKey']} not found` };
658
- }
659
- if (resource.options.allowedActions.delete === false) {
660
- return { error: `Resource '${resource.resourceId}' does not allow delete action` };
661
- }
662
-
663
- const { allowedActions } = await interpretResource(adminUser, resource, { requestBody: body }, ActionCheckSource.DeleteRequest);
664
-
665
- const { allowed, error } = checkAccess(AllowedActionsEnum.delete, allowedActions);
666
- if (!allowed) {
667
- return { error };
668
- }
669
-
670
- const { error: deleteError } = await this.adminforth.deleteResourceRecord({ resource, record, adminUser, recordId: body['primaryKey'] });
671
- if (deleteError) {
672
- return { error: deleteError };
673
- }
674
- return {
675
- ok: true,
676
- recordId: body['primaryKey']
677
- }
678
- }
679
- });
680
- server.endpoint({
681
- method: 'POST',
682
- path: '/start_bulk_action',
683
- handler: async ({ body, adminUser }) => {
684
- const { resourceId, actionId, recordIds } = body;
685
- const resource = this.adminforth.config.resources.find((res) => res.resourceId == resourceId);
686
- if (!resource) {
687
- return { error: `Resource '${resourceId}' not found` };
688
- }
689
- const { allowedActions } = await interpretResource(adminUser, resource, { requestBody: body }, ActionCheckSource.BulkActionRequest);
690
-
691
- const action = resource.options.bulkActions.find((act) => act.id == actionId);
692
- if (!action) {
693
- return { error: `Action '${actionId}' not found` };
694
- }
695
-
696
- if (action.allowed) {
697
- const execAllowed = await action.allowed({ adminUser, resource, selectedIds: recordIds, allowedActions });
698
- if (!execAllowed) {
699
- return { error: `Action '${actionId}' is not allowed` };
700
- }
701
- }
702
- const response = await action.action({selectedIds: recordIds, adminUser, resource});
703
-
704
- return {
705
- actionId,
706
- recordIds,
707
- resourceId,
708
- ...response
709
- }
710
- }
711
- })
712
-
713
- // setup endpoints for all plugins
714
- this.adminforth.activatedPlugins.forEach((plugin) => {
715
- plugin.setupEndpoints(server);
716
- });
717
- }
718
- }