adminforth 1.4.3-next.8 → 1.5.1
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/dist/basePlugin.d.ts.map +1 -1
- package/dist/basePlugin.js +2 -3
- package/dist/basePlugin.js.map +1 -1
- package/dist/dataConnectors/postgres.d.ts.map +1 -1
- package/dist/dataConnectors/postgres.js +29 -9
- package/dist/dataConnectors/postgres.js.map +1 -1
- package/dist/dataConnectors/sqlite.d.ts.map +1 -1
- package/dist/dataConnectors/sqlite.js +12 -5
- package/dist/dataConnectors/sqlite.js.map +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +18 -5
- package/dist/index.js.map +1 -1
- package/dist/modules/codeInjector.d.ts +4 -2
- package/dist/modules/codeInjector.d.ts.map +1 -1
- package/dist/modules/codeInjector.js +98 -79
- package/dist/modules/codeInjector.js.map +1 -1
- package/dist/modules/configValidator.d.ts.map +1 -1
- package/dist/modules/configValidator.js +5 -4
- package/dist/modules/configValidator.js.map +1 -1
- package/dist/modules/operationalResource.d.ts.map +1 -1
- package/dist/modules/operationalResource.js.map +1 -1
- package/dist/modules/restApi.d.ts +8 -3
- package/dist/modules/restApi.d.ts.map +1 -1
- package/dist/modules/restApi.js +112 -39
- package/dist/modules/restApi.js.map +1 -1
- package/dist/modules/socketBroker.d.ts +16 -0
- package/dist/modules/socketBroker.d.ts.map +1 -0
- package/dist/modules/socketBroker.js +99 -0
- package/dist/modules/socketBroker.js.map +1 -0
- package/dist/modules/utils.d.ts +2 -1
- package/dist/modules/utils.d.ts.map +1 -1
- package/dist/modules/utils.js +5 -2
- package/dist/modules/utils.js.map +1 -1
- package/dist/servers/common.d.ts +21 -0
- package/dist/servers/common.d.ts.map +1 -0
- package/dist/servers/common.js +13 -0
- package/dist/servers/common.js.map +1 -0
- package/dist/servers/express.d.ts +5 -0
- package/dist/servers/express.d.ts.map +1 -1
- package/dist/servers/express.js +54 -7
- package/dist/servers/express.js.map +1 -1
- package/dist/spa/package-lock.json +59 -0
- package/dist/spa/package.json +1 -0
- package/dist/spa/src/App.vue +58 -36
- package/dist/spa/src/afcl/Button.vue +8 -1
- package/dist/spa/src/afcl/Input.vue +41 -0
- package/dist/spa/src/afcl/LinkButton.vue +23 -0
- package/dist/spa/src/afcl/Select.vue +8 -7
- package/dist/spa/src/afcl/index.ts +3 -0
- package/dist/spa/src/components/Filters.vue +0 -1
- package/dist/spa/src/components/GroupsTable.vue +4 -4
- package/dist/spa/src/components/ResourceListTable.vue +21 -21
- package/dist/spa/src/components/ValueRenderer.vue +3 -1
- package/dist/spa/src/composables/useStores.ts +4 -1
- package/dist/spa/src/renderers/CompactField.vue +4 -4
- package/dist/spa/src/renderers/CompactUUID.vue +4 -4
- package/dist/spa/src/renderers/CountryFlag.vue +3 -3
- package/dist/spa/src/renderers/HumanNumber.vue +5 -4
- package/dist/spa/src/renderers/RelativeTime.vue +5 -4
- package/dist/spa/src/router/index.ts +2 -0
- package/dist/spa/src/stores/core.ts +39 -6
- package/dist/spa/src/types/Back.ts +102 -9
- package/dist/spa/src/types/Common.ts +6 -1
- package/dist/spa/src/types/FrontendAPI.ts +7 -0
- package/dist/spa/src/views/EditView.vue +1 -0
- package/dist/spa/src/views/ListView.vue +2 -2
- package/dist/spa/src/views/LoginView.vue +7 -7
- package/dist/spa/src/views/PageNotFound.vue +20 -0
- package/dist/spa/src/views/ResourceParent.vue +0 -1
- package/dist/spa/src/views/ShowView.vue +1 -0
- package/dist/spa/src/websocket.ts +84 -0
- package/dist/spa/vite.config.ts +16 -34
- package/dist/types/Back.d.ts +70 -4
- package/dist/types/Back.d.ts.map +1 -1
- package/dist/types/Back.js.map +1 -1
- package/dist/types/Common.d.ts +4 -0
- package/dist/types/Common.d.ts.map +1 -1
- package/dist/types/FrontendAPI.d.ts +6 -0
- package/dist/types/FrontendAPI.d.ts.map +1 -1
- package/dist/types/FrontendAPI.js.map +1 -1
- package/package.json +5 -3
- package/dist/plugins/audit-log/types.d.ts +0 -35
- package/dist/plugins/audit-log/types.d.ts.map +0 -1
- package/dist/plugins/audit-log/types.js +0 -2
- package/dist/plugins/audit-log/types.js.map +0 -1
- package/dist/plugins/chat-gpt/types.d.ts +0 -82
- package/dist/plugins/chat-gpt/types.d.ts.map +0 -1
- package/dist/plugins/chat-gpt/types.js +0 -2
- package/dist/plugins/chat-gpt/types.js.map +0 -1
- package/dist/plugins/email-password-reset/types.d.ts +0 -28
- package/dist/plugins/email-password-reset/types.d.ts.map +0 -1
- package/dist/plugins/email-password-reset/types.js +0 -2
- package/dist/plugins/email-password-reset/types.js.map +0 -1
- package/dist/plugins/foreign-inline-list/types.d.ts +0 -19
- package/dist/plugins/foreign-inline-list/types.d.ts.map +0 -1
- package/dist/plugins/foreign-inline-list/types.js +0 -2
- package/dist/plugins/foreign-inline-list/types.js.map +0 -1
- package/dist/plugins/import-export/types.d.ts +0 -3
- package/dist/plugins/import-export/types.d.ts.map +0 -1
- package/dist/plugins/import-export/types.js +0 -2
- package/dist/plugins/import-export/types.js.map +0 -1
- package/dist/plugins/rich-editor/custom/async-queue.d.ts +0 -8
- package/dist/plugins/rich-editor/custom/async-queue.d.ts.map +0 -1
- package/dist/plugins/rich-editor/custom/async-queue.js +0 -29
- package/dist/plugins/rich-editor/custom/async-queue.js.map +0 -1
- package/dist/plugins/rich-editor/dist/custom/async-queue.d.ts +0 -8
- package/dist/plugins/rich-editor/dist/custom/async-queue.d.ts.map +0 -1
- package/dist/plugins/rich-editor/dist/custom/async-queue.js +0 -29
- package/dist/plugins/rich-editor/dist/custom/async-queue.js.map +0 -1
- package/dist/plugins/rich-editor/types.d.ts +0 -153
- package/dist/plugins/rich-editor/types.d.ts.map +0 -1
- package/dist/plugins/rich-editor/types.js +0 -16
- package/dist/plugins/rich-editor/types.js.map +0 -1
- package/dist/plugins/two-factors-auth/types.d.ts +0 -18
- package/dist/plugins/two-factors-auth/types.d.ts.map +0 -1
- package/dist/plugins/two-factors-auth/types.js +0 -2
- package/dist/plugins/two-factors-auth/types.js.map +0 -1
- package/dist/plugins/upload/types.d.ts +0 -132
- package/dist/plugins/upload/types.d.ts.map +0 -1
- package/dist/plugins/upload/types.js +0 -2
- package/dist/plugins/upload/types.js.map +0 -1
- package/dist/spa/src/acl/Button.vue +0 -21
- package/dist/spa/src/acl/Link.vue +0 -9
- package/dist/spa/src/components/Dropdown.vue +0 -167
- package/dist/spa/src/types/AdminForthConfig.js +0 -150
- package/dist/spa/src/types/AdminForthConfig.ts +0 -1816
- package/dist/spa/src/types/Commons.ts +0 -702
- package/dist/spa/src/types/FrontAndBack.ts +0 -698
- package/dist/types/AdminForthConfig.d.ts +0 -1665
- package/dist/types/AdminForthConfig.d.ts.map +0 -1
- package/dist/types/AdminForthConfig.js +0 -146
- package/dist/types/AdminForthConfig.js.map +0 -1
- package/dist/types/Commons.d.ts +0 -616
- package/dist/types/Commons.d.ts.map +0 -1
- package/dist/types/Commons.js +0 -65
- package/dist/types/Commons.js.map +0 -1
- package/dist/types/FrontAndBack.d.ts +0 -613
- package/dist/types/FrontAndBack.d.ts.map +0 -1
- package/dist/types/FrontAndBack.js +0 -62
- package/dist/types/FrontAndBack.js.map +0 -1
- /package/dist/spa/src/{components/AfTooltip.vue → afcl/Tooltip.vue} +0 -0
|
@@ -13,6 +13,7 @@ import { ActionCheckSource, AdminForthFilterOperators, AdminForthSortDirections,
|
|
|
13
13
|
export interface ICodeInjector {
|
|
14
14
|
srcFoldersToSync: Object;
|
|
15
15
|
allComponentNames: Object;
|
|
16
|
+
devServerPort: number;
|
|
16
17
|
|
|
17
18
|
getServeDir(): string;
|
|
18
19
|
}
|
|
@@ -72,6 +73,12 @@ export interface IExpressHttpServer extends IHttpServer {
|
|
|
72
73
|
*/
|
|
73
74
|
serve(app: Express): void;
|
|
74
75
|
|
|
76
|
+
/**
|
|
77
|
+
* Method to start listening on port.
|
|
78
|
+
*/
|
|
79
|
+
listen(port: number, callback: Function): void;
|
|
80
|
+
listen(port: number, host: string, callback: Function): void;
|
|
81
|
+
|
|
75
82
|
/**
|
|
76
83
|
* Method (middleware) to wrap express endpoints with authorization check.
|
|
77
84
|
* Adds adminUser to request object if user is authorized. Drops request with 401 status if user is not authorized.
|
|
@@ -90,6 +97,7 @@ export interface IExpressHttpServer extends IHttpServer {
|
|
|
90
97
|
authorize(callable: Function): void;
|
|
91
98
|
}
|
|
92
99
|
|
|
100
|
+
|
|
93
101
|
export interface IAdminForthFilter {
|
|
94
102
|
field: string;
|
|
95
103
|
operator: AdminForthFilterOperators;
|
|
@@ -249,15 +257,35 @@ export interface IAdminForthAuth {
|
|
|
249
257
|
removeAuthCookie(response: any): void;
|
|
250
258
|
}
|
|
251
259
|
|
|
260
|
+
export interface IAdminForthRestAPI {
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Called by AdminForth to initialize all endpoints for REST API.
|
|
264
|
+
*/
|
|
265
|
+
registerEndpoints(server: IHttpServer): void;
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Called by login endpoint to process login callbacks. Also might be called by plugins, to prevent action if user is not allowed to login.
|
|
269
|
+
* For example signup or login via google might want to check if user is allowed to login by calling this method.
|
|
270
|
+
* @param adminUser - plugin/af pases current adminUser
|
|
271
|
+
* @param toReturn - this is an object which will get status of login process. If at least one callback returns error or redirectTo, login process will be stopped (future callbacks will not be called).
|
|
272
|
+
* @param response - http response object
|
|
273
|
+
*/
|
|
274
|
+
processLoginCallbacks(adminUser: AdminUser, toReturn: { redirectTo?: string, allowedLogin: boolean, error?: string }, response: any): Promise<void>;
|
|
275
|
+
}
|
|
276
|
+
|
|
252
277
|
export interface IAdminForth {
|
|
253
278
|
config: AdminForthConfig;
|
|
254
279
|
codeInjector: ICodeInjector;
|
|
255
280
|
express: IHttpServer;
|
|
256
281
|
|
|
282
|
+
restApi: IAdminForthRestAPI;
|
|
257
283
|
activatedPlugins: Array<IAdminForthPlugin>;
|
|
258
284
|
|
|
259
285
|
baseUrlSlashed: string;
|
|
260
286
|
|
|
287
|
+
websocket: IWebSocketBroker;
|
|
288
|
+
|
|
261
289
|
statuses: {
|
|
262
290
|
dbDiscover: 'running' | 'done',
|
|
263
291
|
};
|
|
@@ -310,6 +338,7 @@ export interface IAdminForth {
|
|
|
310
338
|
* This method will be automatically called from AdminForth HTTP adapter to serve AdminForth SPA.
|
|
311
339
|
*/
|
|
312
340
|
setupEndpoints(server: IHttpServer): void;
|
|
341
|
+
|
|
313
342
|
}
|
|
314
343
|
|
|
315
344
|
|
|
@@ -424,7 +453,7 @@ export enum AdminForthMenuTypes {
|
|
|
424
453
|
/**
|
|
425
454
|
* Menu item which displayed in the left sidebar of the admin panel.
|
|
426
455
|
*/
|
|
427
|
-
export
|
|
456
|
+
export interface AdminForthConfigMenuItem {
|
|
428
457
|
type?: AdminForthMenuTypes | keyof typeof AdminForthMenuTypes,
|
|
429
458
|
|
|
430
459
|
/**
|
|
@@ -512,6 +541,11 @@ export type AdminForthConfigMenuItem = {
|
|
|
512
541
|
* Result of callback if not null will be used as a small badge near the menu item.
|
|
513
542
|
*/
|
|
514
543
|
badge?: string | ((user: AdminUser) => Promise<string>),
|
|
544
|
+
|
|
545
|
+
/**
|
|
546
|
+
* Item id will be automatically generated from hashed resourceId+Path+label
|
|
547
|
+
*/
|
|
548
|
+
_itemId?: string,
|
|
515
549
|
}
|
|
516
550
|
|
|
517
551
|
|
|
@@ -521,13 +555,17 @@ export type AdminForthConfigMenuItem = {
|
|
|
521
555
|
* Modify query to change how data is fetched from database.
|
|
522
556
|
* Return ok: false and error: string to stop execution and show error message to user. Return ok: true to continue execution.
|
|
523
557
|
*/
|
|
524
|
-
export type BeforeDataSourceRequestFunction = (params: {
|
|
558
|
+
export type BeforeDataSourceRequestFunction = (params: {
|
|
559
|
+
resource: AdminForthResource,
|
|
560
|
+
adminUser: AdminUser,
|
|
561
|
+
query: any,
|
|
525
562
|
extra: {
|
|
526
563
|
body: any,
|
|
527
564
|
query: Record<string, string>,
|
|
528
565
|
headers: Record<string, string>,
|
|
529
566
|
cookies: Record<string, string>,
|
|
530
|
-
}
|
|
567
|
+
},
|
|
568
|
+
adminforth: IAdminForth,
|
|
531
569
|
}) => Promise<{ok: boolean, error?: string}>;
|
|
532
570
|
|
|
533
571
|
/**
|
|
@@ -543,20 +581,35 @@ export type AfterDataSourceResponseFunction = (params: {
|
|
|
543
581
|
query: Record<string, string>,
|
|
544
582
|
headers: Record<string, string>,
|
|
545
583
|
cookies: { key: string, value: string }[],
|
|
546
|
-
}
|
|
584
|
+
},
|
|
585
|
+
adminforth: IAdminForth,
|
|
547
586
|
}) => Promise<{ok: boolean, error?: string}>;
|
|
548
587
|
|
|
549
588
|
/**
|
|
550
589
|
* Modify record to change how data is saved to database.
|
|
551
590
|
* Return ok: false and error: string to stop execution and show error message to user. Return ok: true to continue execution.
|
|
552
591
|
*/
|
|
553
|
-
export type BeforeSaveFunction = (params: {
|
|
592
|
+
export type BeforeSaveFunction = (params: {
|
|
593
|
+
resource: AdminForthResource,
|
|
594
|
+
recordId: any,
|
|
595
|
+
adminUser: AdminUser,
|
|
596
|
+
record: any,
|
|
597
|
+
oldRecord?: any,
|
|
598
|
+
adminforth: IAdminForth,
|
|
599
|
+
}) => Promise<{ok: boolean, error?: string}>;
|
|
554
600
|
|
|
555
601
|
/**
|
|
556
602
|
* Modify record to change how data is saved to database.
|
|
557
603
|
* Return ok: false and error: string to stop execution and show error message to user. Return ok: true to continue execution.
|
|
558
604
|
*/
|
|
559
|
-
export type AfterSaveFunction = (params: {
|
|
605
|
+
export type AfterSaveFunction = (params: {
|
|
606
|
+
resource: AdminForthResource,
|
|
607
|
+
recordId: any,
|
|
608
|
+
adminUser: AdminUser,
|
|
609
|
+
record: any,
|
|
610
|
+
oldRecord?: any,
|
|
611
|
+
adminforth: IAdminForth,
|
|
612
|
+
}) => Promise<{ok: boolean, error?: string}>;
|
|
560
613
|
|
|
561
614
|
/**
|
|
562
615
|
* Allow to get user data before login confirmation, will triger when user try to login.
|
|
@@ -564,9 +617,9 @@ export type AfterSaveFunction = (params: {resource: AdminForthResource, recordId
|
|
|
564
617
|
export type BeforeLoginConfirmationFunction = (params?: {
|
|
565
618
|
adminUser: AdminUser,
|
|
566
619
|
response: IAdminForthHttpResponse,
|
|
620
|
+
adminforth: IAdminForth,
|
|
567
621
|
}) => Promise<{
|
|
568
|
-
|
|
569
|
-
error?:string,
|
|
622
|
+
error?: string,
|
|
570
623
|
body: {
|
|
571
624
|
redirectTo?: string,
|
|
572
625
|
allowedLogin?: boolean,
|
|
@@ -678,6 +731,23 @@ export interface AdminForthConfig {
|
|
|
678
731
|
* If rememberMeDays is set, then users who check "Remember Me" will be staying logged in for this amount of days.
|
|
679
732
|
*/
|
|
680
733
|
rememberMeDays?: number,
|
|
734
|
+
|
|
735
|
+
|
|
736
|
+
/**
|
|
737
|
+
* Can be used to limit user access when subscribing from frontend to websocket topics.
|
|
738
|
+
* @param topic - topic where user is trying to subscribe
|
|
739
|
+
* @param user - user object
|
|
740
|
+
* @returns - boolean, true if user is allowed to subscribe to this topic, false otherwise
|
|
741
|
+
*/
|
|
742
|
+
websocketTopicAuth?: (topic: string, user: AdminUser) => Promise<boolean>,
|
|
743
|
+
|
|
744
|
+
/**
|
|
745
|
+
* callback which will be called after user subscribes to websocket topic
|
|
746
|
+
* @param topic - topic on which user subscribed
|
|
747
|
+
* @param user - user object
|
|
748
|
+
* @returns
|
|
749
|
+
*/
|
|
750
|
+
websocketSubscribed?: (topic: string, user: AdminUser) => void,
|
|
681
751
|
},
|
|
682
752
|
/**
|
|
683
753
|
* Array of resources which will be displayed in the admin panel.
|
|
@@ -956,7 +1026,7 @@ export interface IOperationalResource {
|
|
|
956
1026
|
* or function which returns Boolean or string with error message
|
|
957
1027
|
*
|
|
958
1028
|
*/
|
|
959
|
-
export type AllowedActionValue = boolean | (({adminUser, resource, meta, source}: {
|
|
1029
|
+
export type AllowedActionValue = boolean | (({adminUser, resource, meta, source, adminforth}: {
|
|
960
1030
|
adminUser: AdminUser,
|
|
961
1031
|
resource: AdminForthResource,
|
|
962
1032
|
|
|
@@ -969,6 +1039,11 @@ export type AllowedActionValue = boolean | (({adminUser, resource, meta, source}
|
|
|
969
1039
|
* Source of the check
|
|
970
1040
|
*/
|
|
971
1041
|
source: ActionCheckSource,
|
|
1042
|
+
|
|
1043
|
+
/**
|
|
1044
|
+
* Instance of AdminForth, can be used e.g. to call data API adminforth.resource(resourceId)...
|
|
1045
|
+
*/
|
|
1046
|
+
adminforth: IAdminForth,
|
|
972
1047
|
}) => Promise<boolean | string>);
|
|
973
1048
|
|
|
974
1049
|
|
|
@@ -1191,4 +1266,22 @@ export interface AdminForthForeignResource extends AdminForthForeignResourceComm
|
|
|
1191
1266
|
|
|
1192
1267
|
export interface AdminForthResourceColumn extends AdminForthResourceColumnCommon {
|
|
1193
1268
|
foreignResource?: AdminForthForeignResource,
|
|
1269
|
+
}
|
|
1270
|
+
|
|
1271
|
+
export interface IWebSocketClient {
|
|
1272
|
+
id: string;
|
|
1273
|
+
lastPing: number;
|
|
1274
|
+
topics: Set<string>;
|
|
1275
|
+
adminUser: AdminUser;
|
|
1276
|
+
|
|
1277
|
+
send: (message: string) => void;
|
|
1278
|
+
close: () => void;
|
|
1279
|
+
onMessage: (handler: (message: string) => void) => void;
|
|
1280
|
+
onClose: (handler: () => void) => void;
|
|
1281
|
+
}
|
|
1282
|
+
|
|
1283
|
+
export interface IWebSocketBroker {
|
|
1284
|
+
publish: (topic: string, data: any) => void;
|
|
1285
|
+
|
|
1286
|
+
registerWsClient: (client: IWebSocketClient) => void;
|
|
1194
1287
|
}
|
|
@@ -570,9 +570,14 @@ export type AdminForthResourceColumnCommon = {
|
|
|
570
570
|
*/
|
|
571
571
|
editingNote?: string | { create?: string, edit?: string },
|
|
572
572
|
|
|
573
|
+
/**
|
|
574
|
+
* Whether AdminForth will allow to edit this field in editing mode.
|
|
575
|
+
*/
|
|
576
|
+
editReadonly?: boolean,
|
|
577
|
+
|
|
573
578
|
/**
|
|
574
579
|
* On which AdminForth pages this field will be shown. By default all.
|
|
575
|
-
* Example: if you want to show field only in create and edit pages, set it to
|
|
580
|
+
* Example: if you want to show field only in create and edit pages, set it to
|
|
576
581
|
*
|
|
577
582
|
* ```ts
|
|
578
583
|
* showIn: [AdminForthResourcePages.CREATE, AdminForthResourcePages.EDIT]
|
|
@@ -98,6 +98,13 @@ export interface FrontendAPIInterface {
|
|
|
98
98
|
*/
|
|
99
99
|
clearFilters(): void;
|
|
100
100
|
}
|
|
101
|
+
|
|
102
|
+
menu: {
|
|
103
|
+
/**
|
|
104
|
+
* Refreshes the badges in the menu, by recalling the badge function for each menu item
|
|
105
|
+
*/
|
|
106
|
+
refreshMenuBadges(): void;
|
|
107
|
+
}
|
|
101
108
|
}
|
|
102
109
|
|
|
103
110
|
export type ConfirmParams = {
|
|
@@ -124,6 +124,7 @@ onMounted(async () => {
|
|
|
124
124
|
await coreStore.fetchRecord({
|
|
125
125
|
resourceId: route.params.resourceId,
|
|
126
126
|
primaryKey: route.params.primaryKey,
|
|
127
|
+
source: 'edit',
|
|
127
128
|
});
|
|
128
129
|
checkAcessByAllowedActions(coreStore.resourceOptions.allowedActions,'edit');
|
|
129
130
|
loading.value = false;
|
|
@@ -97,6 +97,7 @@
|
|
|
97
97
|
:pageSize="pageSize"
|
|
98
98
|
:totalRows="totalRows"
|
|
99
99
|
:checkboxes="checkboxes"
|
|
100
|
+
:customActionsInjection="coreStore.resourceOptions?.pageInjections?.list?.customActionIcons"
|
|
100
101
|
/>
|
|
101
102
|
|
|
102
103
|
<component
|
|
@@ -350,8 +351,7 @@ async function init() {
|
|
|
350
351
|
}
|
|
351
352
|
|
|
352
353
|
watch([page, sort, () => filtersStore.filters], async () => {
|
|
353
|
-
console.log('🔄️ page/sort/filter change fired, page:', page.value);
|
|
354
|
-
|
|
354
|
+
// console.log('🔄️ page/sort/filter change fired, page:', page.value);
|
|
355
355
|
await getList();
|
|
356
356
|
}, { deep: true });
|
|
357
357
|
|
|
@@ -39,7 +39,11 @@
|
|
|
39
39
|
<form class="space-y-4" @submit.prevent>
|
|
40
40
|
<div>
|
|
41
41
|
<label for="username" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Your {{ coreStore.config?.usernameFieldName?.toLowerCase() }}</label>
|
|
42
|
-
<input
|
|
42
|
+
<input
|
|
43
|
+
autocomplete="username"
|
|
44
|
+
type="username"
|
|
45
|
+
name="username"
|
|
46
|
+
id="username"
|
|
43
47
|
ref="usernameInput"
|
|
44
48
|
@keydown.enter="passwordInput.focus()"
|
|
45
49
|
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-600 dark:border-gray-500 dark:placeholder-gray-400 dark:text-white" placeholder="name@company.com" required />
|
|
@@ -48,6 +52,7 @@
|
|
|
48
52
|
<label for="password" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Your password</label>
|
|
49
53
|
<input
|
|
50
54
|
ref="passwordInput"
|
|
55
|
+
autocomplete="current-password"
|
|
51
56
|
@keydown.enter="login"
|
|
52
57
|
:type="!showPw ? 'password': 'text'" name="password" id="password" placeholder="••••••••" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-600 dark:border-gray-500 dark:placeholder-gray-400 dark:text-white" required />
|
|
53
58
|
<button type="button" @click="showPw = !showPw" class="absolute top-12 right-3 -translate-y-1/2 text-gray-400 dark:text-gray-300">
|
|
@@ -85,8 +90,6 @@
|
|
|
85
90
|
{{ error }}
|
|
86
91
|
</div>
|
|
87
92
|
</div>
|
|
88
|
-
|
|
89
|
-
|
|
90
93
|
|
|
91
94
|
<div v-if="coreStore.config?.loginPromptHTML"
|
|
92
95
|
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"
|
|
@@ -97,14 +100,11 @@
|
|
|
97
100
|
<span class="sr-only">Info</span>
|
|
98
101
|
<div v-html="coreStore.config?.loginPromptHTML"></div>
|
|
99
102
|
</div>
|
|
100
|
-
|
|
101
103
|
<Button @click="login" :loader="inProgress" :disabled="inProgress" class="w-full">
|
|
102
104
|
Login to your account
|
|
103
105
|
</Button>
|
|
104
106
|
</form>
|
|
105
107
|
|
|
106
|
-
|
|
107
|
-
|
|
108
108
|
</div>
|
|
109
109
|
</div>
|
|
110
110
|
</div>
|
|
@@ -123,7 +123,7 @@ import { useUserStore } from '@/stores/user';
|
|
|
123
123
|
import { IconEyeSolid, IconEyeSlashSolid } from '@iconify-prerendered/vue-flowbite';
|
|
124
124
|
import { callAdminForthApi, loadFile } from '@/utils';
|
|
125
125
|
import { useRouter } from 'vue-router';
|
|
126
|
-
import Button from '@/afcl
|
|
126
|
+
import { Button } from '@/afcl';
|
|
127
127
|
|
|
128
128
|
const passwordInput = ref(null);
|
|
129
129
|
const usernameInput = ref(null);
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<section class="bg-white dark:bg-gray-900">
|
|
3
|
+
<div class="py-8 px-4 mx-auto max-w-screen-xl lg:py-16 lg:px-6">
|
|
4
|
+
<div class="mx-auto max-w-screen-sm text-center">
|
|
5
|
+
<h1 class="mb-4 text-7xl tracking-tight font-extrabold lg:text-9xl text-lightPrimary dark:text-darkPrimary">404</h1>
|
|
6
|
+
<p class="mb-4 text-3xl tracking-tight font-bold text-gray-900 md:text-4xl dark:text-white">Something's missing.</p>
|
|
7
|
+
<p class="mb-4 text-lg font-light text-gray-500 dark:text-gray-400">Sorry, we can't find that page. You'll find lots to explore on the home page. </p>
|
|
8
|
+
<div class="flex justify-center">
|
|
9
|
+
<LinkButton to="/">Go back home</LinkButton>
|
|
10
|
+
</div>
|
|
11
|
+
</div>
|
|
12
|
+
</div>
|
|
13
|
+
</section>
|
|
14
|
+
</template>
|
|
15
|
+
|
|
16
|
+
<script setup lang="ts">
|
|
17
|
+
|
|
18
|
+
import { LinkButton } from '@/afcl';
|
|
19
|
+
|
|
20
|
+
</script>
|
|
@@ -32,7 +32,6 @@ const limitHeightToPage = computed(() => {
|
|
|
32
32
|
return true;
|
|
33
33
|
}
|
|
34
34
|
const listPageInjects = coreStore.resource.options.pageInjections.list;
|
|
35
|
-
console.log('asdcoreStore.resource', JSON.stringify(listPageInjects, null, 2))
|
|
36
35
|
|
|
37
36
|
for (const pi of [listPageInjects.beforeBreadcrumbs, listPageInjects.afterBreadcrumbs, listPageInjects.bottom]) {
|
|
38
37
|
if (pi) {
|
|
@@ -158,6 +158,7 @@ onMounted(async () => {
|
|
|
158
158
|
await coreStore.fetchRecord({
|
|
159
159
|
resourceId: route.params.resourceId,
|
|
160
160
|
primaryKey: route.params.primaryKey,
|
|
161
|
+
source: 'show',
|
|
161
162
|
});
|
|
162
163
|
checkAcessByAllowedActions(coreStore.resourceOptions.allowedActions,'show');
|
|
163
164
|
loading.value = false;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
|
|
2
|
+
const subscriptions: { [topic: string]: ((data: any) => void)[] } = {};
|
|
3
|
+
const state: {
|
|
4
|
+
status: 'connecting' | 'connected' | 'disconnected';
|
|
5
|
+
ws: WebSocket | null;
|
|
6
|
+
} = {
|
|
7
|
+
status: 'connecting',
|
|
8
|
+
ws: null
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
function doPhysicalSubscribe(topic: string) {
|
|
12
|
+
state.ws!.send(JSON.stringify({ type: 'subscribe', topic }));
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function doPhysicalUnsubscribe(topic: string) {
|
|
16
|
+
state.ws!.send(JSON.stringify({ type: 'unsubscribe', topic }));
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
async function init() {
|
|
21
|
+
state.ws = new WebSocket(`${
|
|
22
|
+
window.location.protocol === 'http:' ? 'ws' : 'wss'
|
|
23
|
+
}://${window.location.host}/afws`);
|
|
24
|
+
state.status = 'connecting';
|
|
25
|
+
state.ws.addEventListener('open', () => {
|
|
26
|
+
console.log('connected');
|
|
27
|
+
state.status = 'connected';
|
|
28
|
+
Object.keys(subscriptions).forEach((topic) => {
|
|
29
|
+
doPhysicalSubscribe(topic);
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
state.ws.addEventListener('message', (event) => {
|
|
33
|
+
const data = event.data.toString();
|
|
34
|
+
if (data === 'pong') {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
const message = JSON.parse(data);
|
|
38
|
+
if (message.type === 'message') {
|
|
39
|
+
const topic = message.topic;
|
|
40
|
+
const data = message.data;
|
|
41
|
+
if (subscriptions[topic]) {
|
|
42
|
+
for (const callback of subscriptions[topic]) {
|
|
43
|
+
callback(data);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
state.ws.addEventListener('close', () => {
|
|
49
|
+
console.log('disconnected');
|
|
50
|
+
state.status = 'disconnected';
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
setInterval(() => {
|
|
55
|
+
if (state.status === 'connected') {
|
|
56
|
+
state.ws!.send('ping');
|
|
57
|
+
}
|
|
58
|
+
}, 10_000);
|
|
59
|
+
|
|
60
|
+
try {
|
|
61
|
+
init();
|
|
62
|
+
} catch (e) {
|
|
63
|
+
console.error('Failed to initialize websocket', e);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export default {
|
|
67
|
+
subscribe(topic: string, callback: (data: any) => void): void {
|
|
68
|
+
if (!subscriptions[topic]) {
|
|
69
|
+
subscriptions[topic] = [];
|
|
70
|
+
}
|
|
71
|
+
subscriptions[topic].push(callback);
|
|
72
|
+
if (state.status === 'connected') {
|
|
73
|
+
doPhysicalSubscribe(topic);
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
|
|
77
|
+
unsubscribe(topic: string): void {
|
|
78
|
+
delete subscriptions[topic];
|
|
79
|
+
if (state.status === 'connected') {
|
|
80
|
+
doPhysicalUnsubscribe(topic);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
}
|
package/dist/spa/vite.config.ts
CHANGED
|
@@ -2,48 +2,30 @@ import { fileURLToPath, URL } from 'node:url'
|
|
|
2
2
|
|
|
3
3
|
import { defineConfig } from 'vite'
|
|
4
4
|
import vue from '@vitejs/plugin-vue'
|
|
5
|
-
import
|
|
5
|
+
import portfinder from 'portfinder';
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
},
|
|
15
|
-
warnOnce(msg: string) {
|
|
16
|
-
console.warn('warn once', msg);
|
|
17
|
-
if (!this.hasWarned) {
|
|
18
|
-
this.hasWarned = true;
|
|
19
|
-
}
|
|
20
|
-
},
|
|
21
|
-
warn(msg: string) {
|
|
22
|
-
console.warn(msg);
|
|
23
|
-
},
|
|
24
|
-
error(msg: string) {
|
|
25
|
-
console.error(msg);
|
|
26
|
-
},
|
|
27
|
-
clear() {
|
|
28
|
-
console.clear();
|
|
29
|
-
},
|
|
30
|
-
clearScreen() {
|
|
31
|
-
console.clear();
|
|
32
|
-
},
|
|
33
|
-
hasWarned: false,
|
|
34
|
-
hasErrorLogged: (error: any): boolean => {
|
|
35
|
-
return false;
|
|
36
|
-
}
|
|
7
|
+
/**
|
|
8
|
+
* Find the next available port after a specified port.
|
|
9
|
+
* @param {number} startPort - The starting port to check.
|
|
10
|
+
* @returns {Promise<number>} - A promise that resolves with the next available port.
|
|
11
|
+
*/
|
|
12
|
+
async function getNextAvailablePort(startPort) {
|
|
13
|
+
return await portfinder.getPortPromise({ port: startPort });
|
|
37
14
|
};
|
|
38
15
|
|
|
16
|
+
const appPort = await getNextAvailablePort(5173);
|
|
17
|
+
const hmrPort = await getNextAvailablePort(5273);
|
|
18
|
+
console.log(`SPA port: ${appPort}. HMR port: ${hmrPort}`);
|
|
39
19
|
// https://vitejs.dev/config/
|
|
40
20
|
export default defineConfig({
|
|
41
21
|
base: process.env.VITE_ADMINFORTH_PUBLIC_PATH || '/',
|
|
42
22
|
server: {
|
|
43
|
-
port:
|
|
44
|
-
|
|
23
|
+
port: appPort,
|
|
24
|
+
hmr: {
|
|
25
|
+
path: '/adminforth-dev-server-ws', // Set your custom WebSocket path here
|
|
26
|
+
port: hmrPort,
|
|
27
|
+
},
|
|
45
28
|
},
|
|
46
|
-
customLogger,
|
|
47
29
|
plugins: [
|
|
48
30
|
vue(),
|
|
49
31
|
],
|