adminforth 1.2.99 → 1.3.2

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.
@@ -67,73 +67,64 @@ export default class AdminForthRestAPI {
67
67
  let adminUser: AdminUser;
68
68
  let toReturn: { ok: boolean, redirectTo?: string, allowedLogin:boolean } = { ok: true, allowedLogin:true};
69
69
 
70
- let token;
71
- if (this.adminforth.config.rootUser
72
- && username === this.adminforth.config.rootUser.username
73
- && password === this.adminforth.config.rootUser.password
74
- ) {
75
- this.adminforth.auth.setAuthCookie({ response, username, pk: null });
76
- adminUser = { isRoot: true, dbUser: null, pk: null, username: this.adminforth.config.rootUser.username};
77
- } else {
78
- // get resource from db
79
- if (!this.adminforth.config.auth) {
80
- throw new Error('No config.auth defined');
81
- }
82
- const userResource = this.adminforth.config.resources.find((res) => res.resourceId === this.adminforth.config.auth.resourceId);
83
- // if there is no passwordHashField, in columns, add it, with backendOnly and showIn: []
84
- if (!userResource.dataSourceColumns.find((col) => col.name === this.adminforth.config.auth.passwordHashField)) {
85
- userResource.dataSourceColumns.push({
86
- name: this.adminforth.config.auth.passwordHashField,
87
- backendOnly: true,
88
- showIn: [],
89
- type: AdminForthDataTypes.STRING,
90
- });
91
- console.log('Adding passwordHashField to userResource', userResource)
92
- }
93
-
94
- const userRecord = (
95
- await this.adminforth.connectors[userResource.dataSource].getData({
96
- resource: userResource,
97
- filters: [
98
- { field: this.adminforth.config.auth.usernameField, operator: AdminForthFilterOperators.EQ, value: username },
99
- ],
100
- limit: 1,
101
- offset: 0,
102
- sort: [],
103
- })
104
- ).data?.[0];
105
-
106
- if (!userRecord) {
107
- return { error: 'User not found' };
108
- }
109
-
110
- const passwordHash = userRecord[this.adminforth.config.auth.passwordHashField];
111
- const valid = await AdminForthAuth.verifyPassword(password, passwordHash);
112
- if (valid) {
113
- adminUser = {
114
- isRoot: false, dbUser: userRecord,
115
- pk: userRecord[userResource.columns.find((col) => col.primaryKey).name],
116
- username,
117
- };
118
- const beforeLoginConfirmation = this.adminforth.config.auth.beforeLoginConfirmation as (BeforeLoginConfirmationFunction[] | undefined);
119
- if (beforeLoginConfirmation?.length){
120
- for (const hook of beforeLoginConfirmation) {
121
- const resp = await hook({ adminUser, response });
122
-
123
- if (resp?.body?.redirectTo) {
124
- toReturn = {ok:resp.ok, redirectTo:resp?.body?.redirectTo, allowedLogin:resp?.body?.allowedLogin};
125
- break;
126
- }
70
+ // get resource from db
71
+ if (!this.adminforth.config.auth) {
72
+ throw new Error('No config.auth defined we need it to find user, please follow the docs');
73
+ }
74
+ const userResource = this.adminforth.config.resources.find((res) => res.resourceId === this.adminforth.config.auth.usersResourceId);
75
+ // if there is no passwordHashField, in columns, add it, with backendOnly and showIn: []
76
+ if (!userResource.dataSourceColumns.find((col) => col.name === this.adminforth.config.auth.passwordHashField)) {
77
+ userResource.dataSourceColumns.push({
78
+ name: this.adminforth.config.auth.passwordHashField,
79
+ backendOnly: true,
80
+ showIn: [],
81
+ type: AdminForthDataTypes.STRING,
82
+ });
83
+ console.log('Adding passwordHashField to userResource', userResource)
84
+ }
85
+
86
+ const userRecord = (
87
+ await this.adminforth.connectors[userResource.dataSource].getData({
88
+ resource: userResource,
89
+ filters: [
90
+ { field: this.adminforth.config.auth.usernameField, operator: AdminForthFilterOperators.EQ, value: username },
91
+ ],
92
+ limit: 1,
93
+ offset: 0,
94
+ sort: [],
95
+ })
96
+ ).data?.[0];
97
+
98
+ if (!userRecord) {
99
+ return { error: 'User not found' };
100
+ }
101
+
102
+ const passwordHash = userRecord[this.adminforth.config.auth.passwordHashField];
103
+ const valid = await AdminForthAuth.verifyPassword(password, passwordHash);
104
+ if (valid) {
105
+ adminUser = {
106
+ dbUser: userRecord,
107
+ pk: userRecord[userResource.columns.find((col) => col.primaryKey).name],
108
+ username,
109
+ };
110
+ const beforeLoginConfirmation = this.adminforth.config.auth.beforeLoginConfirmation as (BeforeLoginConfirmationFunction[] | undefined);
111
+ if (beforeLoginConfirmation?.length){
112
+ for (const hook of beforeLoginConfirmation) {
113
+ const resp = await hook({ adminUser, response });
114
+
115
+ if (resp?.body?.redirectTo) {
116
+ toReturn = {ok:resp.ok, redirectTo:resp?.body?.redirectTo, allowedLogin:resp?.body?.allowedLogin};
117
+ break;
127
118
  }
128
119
  }
129
- if (toReturn.allowedLogin){
130
- this.adminforth.auth.setAuthCookie({ response, username, pk: userRecord[userResource.columns.find((col) => col.primaryKey).name] });
131
- }
132
- } else {
133
- return { error: INVALID_MESSAGE };
134
120
  }
135
-
121
+ if (toReturn.allowedLogin){
122
+ this.adminforth.auth.setAuthCookie({ response, username, pk: userRecord[userResource.columns.find((col) => col.primaryKey).name] });
123
+ }
124
+ } else {
125
+ return { error: INVALID_MESSAGE };
136
126
  }
127
+
137
128
 
138
129
  return toReturn;
139
130
  }
@@ -168,7 +159,7 @@ export default class AdminForthRestAPI {
168
159
  throw new Error('No config.auth defined');
169
160
  }
170
161
  const usernameField = this.adminforth.config.auth.usernameField;
171
- const resource = this.adminforth.config.resources.find((res) => res.resourceId === this.adminforth.config.auth.resourceId);
162
+ const resource = this.adminforth.config.resources.find((res) => res.resourceId === this.adminforth.config.auth.usersResourceId);
172
163
  const usernameColumn = resource.columns.find((col) => col.name === usernameField);
173
164
 
174
165
  return {
@@ -188,14 +179,10 @@ export default class AdminForthRestAPI {
188
179
  handler: async ({input, adminUser, cookies}) => {
189
180
  let username = ''
190
181
  let userFullName = ''
191
- if (adminUser.isRoot) {
192
- // isRoot can be in JWT token still when rootUser deleted, so we check for rootUser?"
193
- username = this.adminforth.config.rootUser?.username || 'RootUser';
194
- } else {
195
- const dbUser = adminUser.dbUser;
196
- username = dbUser[this.adminforth.config.auth.usernameField];
197
- userFullName =dbUser[this.adminforth.config.auth.userFullNameField];
198
- }
182
+
183
+ const dbUser = adminUser.dbUser;
184
+ username = dbUser[this.adminforth.config.auth.usernameField];
185
+ userFullName =dbUser[this.adminforth.config.auth.userFullNameField];
199
186
 
200
187
  const userData = {
201
188
  [this.adminforth.config.auth.usernameField]: username,
package/modules/utils.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import path from 'path';
2
2
  import { fileURLToPath } from 'url';
3
3
  import fs from 'fs';
4
+ import Fuse from 'fuse.js';
4
5
  // @ts-ignore-next-line
5
6
 
6
7
 
@@ -335,3 +336,22 @@ export function inverseRGBA(rgba) {
335
336
  return brightness > 128 ? 'rgba(0,0,0,1)' : 'rgba(255,255,255,1)';
336
337
  }
337
338
 
339
+
340
+ export function suggestIfTypo(names: string[], name: string): string {
341
+ if (!name) {
342
+ return null;
343
+ }
344
+ const options = {
345
+ includeScore: true, // Includes score in the results to see how close matches are
346
+ threshold: 0.3, // Defines the fuzziness (lower values mean stricter matches)
347
+ };
348
+
349
+ const fuse = new Fuse(names.filter(
350
+ (n) => !!n
351
+ ), options);
352
+ // Search for a resource
353
+ const result = fuse.search(name);
354
+ if (result.length > 0) {
355
+ return result[0].item;
356
+ }
357
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "adminforth",
3
- "version": "1.2.99",
3
+ "version": "1.3.2",
4
4
  "description": "OpenSource Vue3 powered forth-generation admin panel",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -23,6 +23,7 @@
23
23
  "express": "^4.19.2",
24
24
  "filewatcher": "^3.0.1",
25
25
  "fs-extra": "^11.2.0",
26
+ "fuse.js": "^7.0.0",
26
27
  "jsonwebtoken": "^9.0.2",
27
28
  "mongodb": "6.6",
28
29
  "node-fetch": "^3.3.2",
@@ -51,16 +51,18 @@ export interface IHttpServer {
51
51
 
52
52
  export type AdminUser = {
53
53
  /**
54
- * primaryKey field value of user in table which is defined by {@link AdminForthConfig.auth.resourceId}
55
- * or null if it is user logged in as {@link AdminForthConfig.rootUser}
54
+ * primaryKey field value of user in table which is defined by {@link AdminForthConfig.auth.usersResourceId}
56
55
  */
57
56
  pk: string | null,
58
57
 
59
58
  /**
60
- * Username which takend from {@link AdminForthConfig.auth.usernameField} field in user resource {@link AdminForthConfig.auth.resourceId}
59
+ * Username which taken from {@link AdminForthConfig.auth.usernameField} field in user resource {@link AdminForthConfig.auth.usersResourceId}
61
60
  */
62
61
  username: string,
63
- isRoot: boolean,
62
+
63
+ /**
64
+ * User record fetched from database, from resource defined in {@link AdminForthConfig.auth.usersResourceId}
65
+ */
64
66
  dbUser: any,
65
67
  }
66
68
 
@@ -220,7 +222,11 @@ export interface IAdminForthDataSourceConnectorBase extends IAdminForthDataSourc
220
222
 
221
223
  getRecordByPrimaryKey(resource: AdminForthResource, recordId: string): Promise<any>;
222
224
 
223
- createRecord({ resource, record }: { resource: AdminForthResource, record: any }): Promise<void>;
225
+ createRecord({ resource, record, adminUser }: {
226
+ resource: AdminForthResource,
227
+ record: any
228
+ adminUser: AdminUser
229
+ }): Promise<void>;
224
230
 
225
231
  getMinMaxForColumns({ resource, columns }: { resource: AdminForthResource, columns: AdminForthResourceColumn[] }): Promise<{ [key: string]: { min: any, max: any } }>;
226
232
  }
@@ -744,7 +750,7 @@ export type AdminForthBulkAction = {
744
750
  *
745
751
  * ```ts
746
752
  * allowed: async ({ resource, adminUser, selectedIds }) => {
747
- * if (adminUser.isRoot || adminUser.dbUser.role !== 'superadmin') {
753
+ * if (adminUser.dbUser.role !== 'superadmin') {
748
754
  * return false;
749
755
  * }
750
756
  * return true;
@@ -786,7 +792,7 @@ export type AdminForthBulkAction = {
786
792
  * ],
787
793
  * allowedActions: \{
788
794
  * edit: (\{ resource, adminUser, recordIds \}) => \{
789
- * return adminUser.isRoot || adminUser.dbUser.role === 'superadmin';
795
+ * return adminUser.dbUser.role === 'superadmin';
790
796
  * \}
791
797
  * \}
792
798
  * \}
@@ -979,8 +985,8 @@ export type AdminForthResource = {
979
985
  * ```ts
980
986
  * allowedActions: {
981
987
  * create: ({ resource, adminUser }) => {
982
- * // Allow only superadmin or root user to create records
983
- * return adminUser.isRoot || adminUser.dbUser.role === 'superadmin';
988
+ * // Allow only superadmin to create records
989
+ * return adminUser.dbUser.role === 'superadmin';
984
990
  * },
985
991
  * delete: false, // disable delete action for all users
986
992
  * }
@@ -1085,33 +1091,21 @@ export type AdminForthDataSource = {
1085
1091
  */
1086
1092
  export type AdminForthConfig = {
1087
1093
 
1088
- /**
1089
- * Root user should be used to login to the admin panel first time.
1090
- * Then you should create User for yourself using AdminForth (so it will be persisted in DB),
1091
- * and then disable this option as it is less secure
1092
- */
1093
- rootUser?: {
1094
- /**
1095
- * Username for root user
1096
- */
1097
- username: string,
1098
-
1099
- /**
1100
- * Password for root user
1101
- */
1102
- password: string,
1103
- },
1104
-
1105
1094
  /**
1106
1095
  * Authorization module configuration
1107
1096
  */
1108
1097
  auth?: {
1109
1098
  /**
1110
- * Resource ID for user resource.
1099
+ * Resource ID for resource which stores user table.
1111
1100
  * Resource is a table in database where users will be stored and fetched from. Resources and their ids are defined in resources section of the config.
1112
1101
  * In other words this setting is a reference to a table in database where users will be fetched from on login.
1113
1102
  */
1114
- resourceId: string,
1103
+ usersResourceId?: string,
1104
+
1105
+ /**
1106
+ * Legacy field left for backward compatibility. Use usersResourceId instead.
1107
+ */
1108
+ resourceId?: string,
1115
1109
 
1116
1110
  /**
1117
1111
  * Field name (column name) in user resource which will be used as username for searching user in database during login.
@@ -1322,6 +1316,10 @@ export type AdminForthConfig = {
1322
1316
  baseUrl?: string,
1323
1317
 
1324
1318
 
1319
+ /**
1320
+ * Whether to show delete confirmation dialog before deleting record.
1321
+ * By default it is true.
1322
+ */
1325
1323
  deleteConfirmation?: boolean,
1326
1324
 
1327
1325
 
@@ -1375,7 +1373,7 @@ export class Sorts {
1375
1373
  }
1376
1374
 
1377
1375
  export interface IOperationalResource {
1378
- get: (filter: IAdminForthFilter | IAdminForthFilter[]) => Promise<any[]>;
1376
+ get: (filter: IAdminForthFilter | IAdminForthFilter[]) => Promise<any | null>;
1379
1377
 
1380
1378
  list: (filter: IAdminForthFilter | IAdminForthFilter[], limit: number, offset: number, sort: IAdminForthSort | IAdminForthSort[]) => Promise<any[]>;
1381
1379