adminforth 1.3.20 → 1.3.21

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/auth.ts CHANGED
@@ -63,7 +63,7 @@ class AdminForthAuth {
63
63
  return jwt.sign({...payload, t: type}, secret, { expiresIn });
64
64
  }
65
65
 
66
- async verify(jwtToken: string, mustHaveType: string): Promise<Object> {
66
+ async verify(jwtToken: string, mustHaveType: string, decodeUser: boolean | undefined = true): Promise<Object> {
67
67
  // read ADMINFORH_SECRET from environment if not drop error
68
68
  const secret = process.env.ADMINFORTH_SECRET;
69
69
  if (!secret) {
@@ -88,13 +88,16 @@ class AdminForthAuth {
88
88
  console.error(`Invalid token type during verification: ${t}, must be ${mustHaveType}`);
89
89
  return null;
90
90
  }
91
- const dbUser = await this.adminforth.getUserByPk(pk);
92
- if (!dbUser) {
93
- console.error(`User with pk ${pk} not found in database`);
94
- // will logout user which was deleted
95
- return null;
91
+ if (decodeUser !== false) {
92
+ const dbUser = await this.adminforth.getUserByPk(pk);
93
+ if (!dbUser) {
94
+ console.error(`User with pk ${pk} not found in database`);
95
+ // will logout user which was deleted
96
+ return null;
97
+ }
98
+ decoded.dbUser = dbUser;
96
99
  }
97
- decoded.dbUser = dbUser;
100
+
98
101
  return decoded;
99
102
  }
100
103
 
package/dist/auth.js CHANGED
@@ -51,8 +51,8 @@ class AdminForthAuth {
51
51
  const expiresIn = process.env.ADMINFORTH_AUTH_EXPIRESIN || '24h';
52
52
  return jwt.sign(Object.assign(Object.assign({}, payload), { t: type }), secret, { expiresIn });
53
53
  }
54
- verify(jwtToken, mustHaveType) {
55
- return __awaiter(this, void 0, void 0, function* () {
54
+ verify(jwtToken_1, mustHaveType_1) {
55
+ return __awaiter(this, arguments, void 0, function* (jwtToken, mustHaveType, decodeUser = true) {
56
56
  // read ADMINFORH_SECRET from environment if not drop error
57
57
  const secret = process.env.ADMINFORTH_SECRET;
58
58
  if (!secret) {
@@ -80,13 +80,15 @@ class AdminForthAuth {
80
80
  console.error(`Invalid token type during verification: ${t}, must be ${mustHaveType}`);
81
81
  return null;
82
82
  }
83
- const dbUser = yield this.adminforth.getUserByPk(pk);
84
- if (!dbUser) {
85
- console.error(`User with pk ${pk} not found in database`);
86
- // will logout user which was deleted
87
- return null;
83
+ if (decodeUser !== false) {
84
+ const dbUser = yield this.adminforth.getUserByPk(pk);
85
+ if (!dbUser) {
86
+ console.error(`User with pk ${pk} not found in database`);
87
+ // will logout user which was deleted
88
+ return null;
89
+ }
90
+ decoded.dbUser = dbUser;
88
91
  }
89
- decoded.dbUser = dbUser;
90
92
  return decoded;
91
93
  });
92
94
  }
@@ -195,12 +195,12 @@ class CodeInjector {
195
195
  const registerCustomPages = (config) => {
196
196
  if (config.customization.customPages) {
197
197
  config.customization.customPages.forEach((page) => {
198
- var _a, _b, _c, _d;
198
+ var _a, _b, _c;
199
199
  routes += `{
200
200
  path: '${page.path}',
201
201
  name: '${page.path}',
202
202
  component: () => import('${((_a = page === null || page === void 0 ? void 0 : page.component) === null || _a === void 0 ? void 0 : _a.file) || page.component}'),
203
- meta: { title: '${((_b = page.meta) === null || _b === void 0 ? void 0 : _b.title) || page.path.replace('/', '')}',customLayout:${(_d = (_c = page === null || page === void 0 ? void 0 : page.component) === null || _c === void 0 ? void 0 : _c.meta) === null || _d === void 0 ? void 0 : _d.customLayout}}
203
+ meta: ${JSON.stringify(Object.assign(Object.assign({}, (((_b = page === null || page === void 0 ? void 0 : page.component) === null || _b === void 0 ? void 0 : _b.meta) || {})), { title: ((_c = page.meta) === null || _c === void 0 ? void 0 : _c.title) || page.path.replace('/', '') }))}
204
204
  },`;
205
205
  });
206
206
  }
@@ -119,6 +119,12 @@ export default class ConfigValidator {
119
119
  if (((_a = this.config) === null || _a === void 0 ? void 0 : _a.customization.brandName) === undefined) {
120
120
  this.config.customization.brandName = 'AdminForth';
121
121
  }
122
+ if (this.config.customization.loginPageInjections === undefined) {
123
+ this.config.customization.loginPageInjections = {};
124
+ }
125
+ if (this.config.customization.loginPageInjections.underInputs === undefined) {
126
+ this.config.customization.loginPageInjections.underInputs = [];
127
+ }
122
128
  if (this.config.customization.brandLogo) {
123
129
  errors.push(...this.checkCustomFileExists(this.config.customization.brandLogo));
124
130
  }
@@ -71,6 +71,7 @@ export default class AdminForthRestAPI {
71
71
  offset: 0,
72
72
  sort: [],
73
73
  })).data) === null || _b === void 0 ? void 0 : _b[0];
74
+ console.log('userRecord', userRecord, this.adminforth.config.auth.usernameField, username);
74
75
  if (!userRecord) {
75
76
  return { error: 'User not found' };
76
77
  }
@@ -139,6 +140,7 @@ export default class AdminForthRestAPI {
139
140
  title: (_j = this.adminforth.config.customization) === null || _j === void 0 ? void 0 : _j.title,
140
141
  demoCredentials: this.adminforth.config.auth.demoCredentials,
141
142
  loginPromptHTML: this.adminforth.config.auth.loginPromptHTML,
143
+ loginPageInjections: this.adminforth.config.customization.loginPageInjections,
142
144
  };
143
145
  }),
144
146
  });
package/index.ts CHANGED
@@ -264,7 +264,7 @@ class AdminForth implements IAdminForth {
264
264
  return { ok, error, createdRecord };
265
265
  }
266
266
 
267
- resource(resourceId: string) {
267
+ resource(resourceId: string): IOperationalResource {
268
268
  if (this.statuses.dbDiscover !== 'done') {
269
269
  if (this.statuses.dbDiscover === 'running') {
270
270
  throw new Error('Database discovery is running. You can\'t use data API while database discovery is not finished.\n'+
@@ -10,6 +10,7 @@ import AdminForth from '../index.js';
10
10
  import { ADMIN_FORTH_ABSOLUTE_PATH, getComponentNameFromPath, transformObject, deepMerge } from './utils.js';
11
11
  import { ICodeInjector } from '../types/AdminForthConfig.js';
12
12
  import { StylesGenerator } from './styleGenerator.js';
13
+ import { title } from 'process';
13
14
 
14
15
 
15
16
  let TMP_DIR;
@@ -211,7 +212,12 @@ class CodeInjector implements ICodeInjector {
211
212
  path: '${page.path}',
212
213
  name: '${page.path}',
213
214
  component: () => import('${page?.component?.file || page.component}'),
214
- meta: { title: '${page.meta?.title || page.path.replace('/', '')}',customLayout:${page?.component?.meta?.customLayout }}
215
+ meta: ${
216
+ JSON.stringify({
217
+ ...(page?.component?.meta || {}),
218
+ title: page.meta?.title || page.path.replace('/', '')
219
+ })
220
+ }
215
221
  },`})
216
222
 
217
223
  }}
@@ -127,6 +127,13 @@ export default class ConfigValidator implements IConfigValidator {
127
127
  if (this.config?.customization.brandName === undefined) {
128
128
  this.config.customization.brandName = 'AdminForth';
129
129
  }
130
+ if (this.config.customization.loginPageInjections === undefined) {
131
+ this.config.customization.loginPageInjections = {};
132
+ }
133
+
134
+ if (this.config.customization.loginPageInjections.underInputs === undefined) {
135
+ this.config.customization.loginPageInjections.underInputs = [];
136
+ }
130
137
  if (this.config.customization.brandLogo) {
131
138
  errors.push(...this.checkCustomFileExists(this.config.customization.brandLogo));
132
139
  }
@@ -95,6 +95,8 @@ export default class AdminForthRestAPI {
95
95
  })
96
96
  ).data?.[0];
97
97
 
98
+ console.log('userRecord', userRecord, this.adminforth.config.auth.usernameField, username);
99
+
98
100
  if (!userRecord) {
99
101
  return { error: 'User not found' };
100
102
  }
@@ -170,6 +172,7 @@ export default class AdminForthRestAPI {
170
172
  title: this.adminforth.config.customization?.title,
171
173
  demoCredentials: this.adminforth.config.auth.demoCredentials,
172
174
  loginPromptHTML: this.adminforth.config.auth.loginPromptHTML,
175
+ loginPageInjections: this.adminforth.config.customization.loginPageInjections,
173
176
  };
174
177
  },
175
178
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "adminforth",
3
- "version": "1.3.20",
3
+ "version": "1.3.21",
4
4
  "description": "OpenSource Vue3 powered forth-generation admin panel",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
package/spa/src/App.vue CHANGED
@@ -297,7 +297,11 @@ async function initRouter() {
297
297
  }
298
298
 
299
299
  async function loadMenu() {
300
- await coreStore.fetchMenuAndResource();
300
+ await initRouter();
301
+ if (!route.meta.customLayout) {
302
+ // for custom layouts we don't need to fetch menu
303
+ await coreStore.fetchMenuAndResource();
304
+ }
301
305
  loginRedirectCheckIsReady.value = true;
302
306
  }
303
307
 
@@ -6,16 +6,14 @@
6
6
  <form autocomplete="off" @submit.prevent>
7
7
  <table class="w-full text-sm text-left rtl:text-right text-gray-500 dark:text-gray-400 ">
8
8
  <thead class="text-xs text-gray-700 uppercase bg-lightFormHeading dark:bg-gray-700 dark:text-gray-400 block md:table-row-group">
9
- <tr>
10
- <th scope="col" class="px-6 py-3 hidden md:table-cell">
11
- Field
12
- </th>
13
- <th scope="col" class="px-6 py-3 w-5/6 hidden md:table-cell">
14
- Value
15
- </th>
16
-
17
-
18
- </tr>
9
+ <tr>
10
+ <th scope="col" class="px-6 py-3 hidden md:table-cell">
11
+ Field
12
+ </th>
13
+ <th scope="col" class="px-6 py-3 w-5/6 hidden md:table-cell">
14
+ Value
15
+ </th>
16
+ </tr>
19
17
  </thead>
20
18
  <tbody>
21
19
  <tr v-for="column, i in editableColumns" :key="column.name"
@@ -147,7 +145,6 @@
147
145
 
148
146
  import CustomDatePicker from "@/components/CustomDatePicker.vue";
149
147
  import Dropdown from '@/components/Dropdown.vue';
150
- import { useCoreStore } from '@/stores/core';
151
148
  import { callAdminForthApi, getCustomComponent } from '@/utils';
152
149
  import { IconExclamationCircleSolid, IconEyeSlashSolid, IconEyeSolid } from '@iconify-prerendered/vue-flowbite';
153
150
  import { computedAsync } from '@vueuse/core';
@@ -56,8 +56,18 @@ const props = defineProps({
56
56
 
57
57
  function protectAgainstXSS(value) {
58
58
  return sanitizeHtml(value, {
59
+ allowedTags: [
60
+ "address", "article", "aside", "footer", "header", "h1", "h2", "h3", "h4",
61
+ "h5", "h6", "hgroup", "main", "nav", "section", "blockquote", "dd", "div",
62
+ "dl", "dt", "figcaption", "figure", "hr", "li", "main", "ol", "p", "pre",
63
+ "ul", "a", "abbr", "b", "bdi", "bdo", "br", "cite", "code", "data", "dfn",
64
+ "em", "i", "kbd", "mark", "q", "rb", "rp", "rt", "rtc", "ruby", "s", "samp",
65
+ "small", "span", "strong", "sub", "sup", "time", "u", "var", "wbr", "caption",
66
+ "col", "colgroup", "table", "tbody", "td", "tfoot", "th", "thead", "tr", 'img'
67
+ ],
59
68
  allowedAttributes: {
60
- 'li': [ 'data-list' ],
69
+ 'li': [ 'data-list' ],
70
+ 'img': [ 'src', 'srcset', 'alt', 'title', 'width', 'height', 'loading' ]
61
71
  }
62
72
  });
63
73
  }
@@ -110,7 +110,6 @@
110
110
  import BreadcrumbsWithButtons from '@/components/BreadcrumbsWithButtons.vue';
111
111
  import ResourceListTable from '@/components/ResourceListTable.vue';
112
112
  import { useCoreStore } from '@/stores/core';
113
- import { useModalStore } from '@/stores/modal';
114
113
  import { useFiltersStore } from '@/stores/filters';
115
114
  import { callAdminForthApi, getIcon } from '@/utils';
116
115
  import { initFlowbite } from 'flowbite';
@@ -56,6 +56,19 @@
56
56
  </button>
57
57
  </div>
58
58
 
59
+ <div class="flex items-start mb-5 hidden">
60
+ <div class="flex items-center h-5">
61
+ <input id="remember" type="checkbox" value="" class="w-4 h-4 border border-gray-300 rounded bg-gray-50 focus:ring-3 focus:ring-blue-300 dark:bg-gray-700 dark:border-gray-600 dark:focus:ring-blue-600 dark:ring-offset-gray-800 dark:focus:ring-offset-gray-800" required />
62
+ </div>
63
+ <label for="remember" class="ms-2 text-sm font-medium text-gray-900 dark:text-gray-300">Remember me</label>
64
+ </div>
65
+
66
+ <component
67
+ v-for="c in coreStore?.config?.loginPageInjections?.underInputs || []"
68
+ :is="getCustomComponent(c)"
69
+ :meta="c.meta"
70
+ />
71
+
59
72
  <div v-if="error" class="flex items-center p-4 mb-4 text-sm text-red-800 rounded-lg bg-red-50 dark:bg-gray-800 dark:text-red-400" role="alert">
60
73
  <svg class="flex-shrink-0 inline w-4 h-4 me-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20">
61
74
  <path d="M10 .5a9.5 9.5 0 1 0 9.5 9.5A9.51 9.51 0 0 0 10 .5ZM9.5 4a1.5 1.5 0 1 1 0 3 1.5 1.5 0 0 1 0-3ZM12 15H8a1 1 0 0 1 0-2h1v-3H8a1 1 0 0 1 0-2h2a1 1 0 0 1 1 1v4h1a1 1 0 0 1 0 2Z"/>
@@ -66,6 +79,7 @@
66
79
  </div>
67
80
  </div>
68
81
 
82
+
69
83
 
70
84
  <div v-if="coreStore.config?.loginPromptHTML"
71
85
  class="flex items-center p-4 mb-4 text-sm text-gray-800 rounded-lg bg-gray-50 dark:bg-gray-800 dark:text-gray-400" role="alert"
@@ -101,6 +115,7 @@
101
115
 
102
116
  <script setup>
103
117
 
118
+ import { getCustomComponent } from '@/utils';
104
119
  import { onMounted, ref, computed } from 'vue';
105
120
  import { useCoreStore } from '@/stores/core';
106
121
  import { useUserStore } from '@/stores/user';
@@ -1,4 +1,5 @@
1
1
  import type { Express } from 'express';
2
+ import OperationalResource from '../modules/operationalResource.js';
2
3
 
3
4
  export interface ICodeInjector {
4
5
  srcFoldersToSync: Object;
@@ -237,7 +238,7 @@ export interface IAdminForthDataSourceConnectorConstructor {
237
238
  }
238
239
 
239
240
  export interface IAdminForthAuth {
240
- verify(jwt : string, mustHaveType: string): Promise<any>;
241
+ verify(jwt : string, mustHaveType: string, decodeUser?: boolean): Promise<any>;
241
242
  issueJWT(payload: Object, type: string): string;
242
243
 
243
244
  removeCustomCookie({response, name}: {response: any, name: string}): void;
@@ -291,6 +292,11 @@ export interface IAdminForth {
291
292
  */
292
293
  bundleNow({ hotReload, verbose }: { hotReload: boolean, verbose: boolean }): Promise<void>;
293
294
 
295
+ /**
296
+ * Resource to get access to operational resources for data api fetching and manipulation.
297
+ */
298
+ resource(resourceId: string): IOperationalResource;
299
+
294
300
  /**
295
301
  * This method will be automatically called from AdminForth HTTP adapter to serve AdminForth SPA.
296
302
  */
@@ -1326,6 +1332,15 @@ export type AdminForthConfig = {
1326
1332
  * Execution is done on admin app load.
1327
1333
  */
1328
1334
  announcementBadge?: (user: AdminUser) => { text?: string, html?: string, closable?: boolean, title?: string } | null,
1335
+
1336
+ /**
1337
+ * Custom panel components or array of components which will be displayed in the login form
1338
+ * right after the inputs. Use it to add custom authorization methods like social login or other custom fields e.g. 'reset'
1339
+ * password link.
1340
+ */
1341
+ loginPageInjections?: {
1342
+ underInputs?: AdminForthComponentDeclaration | Array<AdminForthComponentDeclaration>,
1343
+ }
1329
1344
  }
1330
1345
 
1331
1346
  /**