oip-common 0.0.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/README.md +25 -0
- package/ng-package.json +8 -0
- package/package.json +16 -0
- package/src/api/FolderModule.ts +124 -0
- package/src/api/Menu.ts +134 -0
- package/src/api/Module.ts +92 -0
- package/src/api/Security.ts +40 -0
- package/src/api/Service.ts +57 -0
- package/src/api/data-contracts.ts +186 -0
- package/src/api/http-client.ts +276 -0
- package/src/assets/demo/code.scss +17 -0
- package/src/assets/demo/data/products-small.json +124 -0
- package/src/assets/demo/demo.scss +2 -0
- package/src/assets/demo/flags/flags.scss +740 -0
- package/src/assets/demo/flags/flags_responsive.png +0 -0
- package/src/assets/demo/images/access/asset-access.svg +46 -0
- package/src/assets/demo/images/blocks/hero/hero-1.png +0 -0
- package/src/assets/demo/images/blocks/logos/hyper.svg +3 -0
- package/src/assets/demo/images/error/asset-error.svg +74 -0
- package/src/assets/demo/images/flag/flag_placeholder.png +0 -0
- package/src/assets/demo/images/product/bamboo-watch.jpg +0 -0
- package/src/assets/demo/images/product/black-watch.jpg +0 -0
- package/src/assets/demo/images/product/blue-band.jpg +0 -0
- package/src/assets/demo/images/product/blue-t-shirt.jpg +0 -0
- package/src/assets/demo/images/product/bracelet.jpg +0 -0
- package/src/assets/demo/images/product/brown-purse.jpg +0 -0
- package/src/assets/demo/images/product/chakra-bracelet.jpg +0 -0
- package/src/assets/demo/images/product/galaxy-earrings.jpg +0 -0
- package/src/assets/demo/images/product/game-controller.jpg +0 -0
- package/src/assets/demo/images/product/gaming-set.jpg +0 -0
- package/src/assets/demo/images/product/gold-phone-case.jpg +0 -0
- package/src/assets/demo/images/product/green-earbuds.jpg +0 -0
- package/src/assets/demo/images/product/green-t-shirt.jpg +0 -0
- package/src/assets/demo/images/product/grey-t-shirt.jpg +0 -0
- package/src/assets/demo/images/product/headphones.jpg +0 -0
- package/src/assets/demo/images/product/light-green-t-shirt.jpg +0 -0
- package/src/assets/demo/images/product/lime-band.jpg +0 -0
- package/src/assets/demo/images/product/mini-speakers.jpg +0 -0
- package/src/assets/demo/images/product/painted-phone-case.jpg +0 -0
- package/src/assets/demo/images/product/pink-band.jpg +0 -0
- package/src/assets/demo/images/product/pink-purse.jpg +0 -0
- package/src/assets/demo/images/product/product-placeholder.svg +10 -0
- package/src/assets/demo/images/product/purple-band.jpg +0 -0
- package/src/assets/demo/images/product/purple-gemstone-necklace.jpg +0 -0
- package/src/assets/demo/images/product/purple-t-shirt.jpg +0 -0
- package/src/assets/demo/images/product/shoes.jpg +0 -0
- package/src/assets/demo/images/product/sneakers.jpg +0 -0
- package/src/assets/demo/images/product/teal-t-shirt.jpg +0 -0
- package/src/assets/demo/images/product/yellow-earbuds.jpg +0 -0
- package/src/assets/demo/images/product/yoga-mat.jpg +0 -0
- package/src/assets/demo/images/product/yoga-set.jpg +0 -0
- package/src/assets/favicon.svg +14 -0
- package/src/assets/i18n/app-modules.en.json +23 -0
- package/src/assets/i18n/app-modules.ru.json +23 -0
- package/src/assets/i18n/config.en.json +14 -0
- package/src/assets/i18n/config.ru.json +14 -0
- package/src/assets/layout/_core.scss +24 -0
- package/src/assets/layout/_footer.scss +8 -0
- package/src/assets/layout/_logo.scss +7 -0
- package/src/assets/layout/_main.scss +12 -0
- package/src/assets/layout/_menu.scss +159 -0
- package/src/assets/layout/_mixins.scss +15 -0
- package/src/assets/layout/_preloading.scss +49 -0
- package/src/assets/layout/_responsive.scss +108 -0
- package/src/assets/layout/_topbar.scss +168 -0
- package/src/assets/layout/_typography.scss +68 -0
- package/src/assets/layout/_utils.scss +25 -0
- package/src/assets/layout/layout.scss +14 -0
- package/src/assets/layout/variables/_common.scss +20 -0
- package/src/assets/layout/variables/_dark.scss +5 -0
- package/src/assets/layout/variables/_light.scss +5 -0
- package/src/assets/oip-common.scss +5 -0
- package/src/assets/tailwind.css +3 -0
- package/src/components/app-configurator.component.ts +491 -0
- package/src/components/app-floating-configurator.component.ts +47 -0
- package/src/components/app-modules.component.ts +144 -0
- package/src/components/app.layout.component.ts +130 -0
- package/src/components/auth/access/access.component.ts +42 -0
- package/src/components/auth/error/error.component.ts +42 -0
- package/src/components/auth/login/login.component.ts +120 -0
- package/src/components/auth/unauthorized/unauthorized.component.ts +51 -0
- package/src/components/base-module.component.ts +258 -0
- package/src/components/config.component.ts +131 -0
- package/src/components/db-migration/db-migration.component.ts +162 -0
- package/src/components/db-migration.component.ts +154 -0
- package/src/components/footer.component.ts +17 -0
- package/src/components/logo.component.ts +34 -0
- package/src/components/menu/menu-item-create-dialog.component.ts +119 -0
- package/src/components/menu/menu-item-edit-dialog.component.ts +123 -0
- package/src/components/menu/menu-item.component.ts +295 -0
- package/src/components/menu/menu.component.ts +85 -0
- package/src/components/notfound.component.ts +31 -0
- package/src/components/profile.component.ts +43 -0
- package/src/components/security.component.ts +102 -0
- package/src/components/sidebar.component.ts +12 -0
- package/src/components/top-bar.component.ts +147 -0
- package/src/dtos/context-menu-item.dto.ts +23 -0
- package/src/dtos/edit-module-instance.dto.ts +8 -0
- package/src/dtos/no-settings.dto.ts +4 -0
- package/src/dtos/put-security.dto.ts +6 -0
- package/src/dtos/security.dto.ts +6 -0
- package/src/dtos/top-bar.dto.ts +13 -0
- package/src/events/menu-change.event.ts +23 -0
- package/src/helpers/date.helper.ts +94 -0
- package/src/intercepts/i18n-intercept.service.ts +13 -0
- package/src/modules/http-loader.factory.ts +40 -0
- package/src/modules/secure.pipe.ts +19 -0
- package/src/public-api.ts +42 -0
- package/src/services/app-title.service.ts +22 -0
- package/src/services/app.layout.service.ts +236 -0
- package/src/services/app.menu.service.ts +64 -0
- package/src/services/auth.service.ts +58 -0
- package/src/services/base-data.service.ts +74 -0
- package/src/services/l10n.service.ts +71 -0
- package/src/services/msg.service.ts +76 -0
- package/src/services/security-data.service.ts +19 -0
- package/src/services/security-storage.service.ts +21 -0
- package/src/services/security.service.ts +116 -0
- package/src/services/top-bar.service.ts +44 -0
- package/src/services/user.service.ts +77 -0
- package/src/test.ts +11 -0
- package/src/user-api/UserProfile.ts +85 -0
- package/src/user-api/data-contracts.ts +42 -0
- package/src/user-api/http-client.ts +251 -0
- package/tsconfig.lib.json +12 -0
- package/tsconfig.lib.prod.json +10 -0
- package/tsconfig.spec.json +9 -0
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { Component, inject } from '@angular/core';
|
|
2
|
+
import { MenuItem } from 'primeng/api';
|
|
3
|
+
import { RouterModule } from '@angular/router';
|
|
4
|
+
import { CommonModule } from '@angular/common';
|
|
5
|
+
import { StyleClassModule } from 'primeng/styleclass';
|
|
6
|
+
import { LayoutService } from '../services/app.layout.service';
|
|
7
|
+
import { AppConfiguratorComponent } from './app-configurator.component';
|
|
8
|
+
import { LogoComponent } from './logo.component';
|
|
9
|
+
import { SecurityService } from '../services/security.service';
|
|
10
|
+
import { TopBarService } from '../services/top-bar.service';
|
|
11
|
+
import { Tab, TabList, Tabs } from 'primeng/tabs';
|
|
12
|
+
import { AvatarModule } from 'primeng/avatar';
|
|
13
|
+
import { UserService } from '../services/user.service';
|
|
14
|
+
import { ButtonModule } from 'primeng/button';
|
|
15
|
+
|
|
16
|
+
@Component({
|
|
17
|
+
selector: 'app-topbar',
|
|
18
|
+
standalone: true,
|
|
19
|
+
imports: [
|
|
20
|
+
RouterModule,
|
|
21
|
+
CommonModule,
|
|
22
|
+
StyleClassModule,
|
|
23
|
+
AppConfiguratorComponent,
|
|
24
|
+
LogoComponent,
|
|
25
|
+
Tabs,
|
|
26
|
+
TabList,
|
|
27
|
+
Tab,
|
|
28
|
+
AvatarModule,
|
|
29
|
+
ButtonModule
|
|
30
|
+
],
|
|
31
|
+
template: ` <div class="layout-topbar">
|
|
32
|
+
<div class="layout-topbar-logo-container">
|
|
33
|
+
<button class="layout-menu-button layout-topbar-action" (click)="layoutService.onMenuToggle()">
|
|
34
|
+
<i class="pi pi-bars"></i>
|
|
35
|
+
</button>
|
|
36
|
+
<a class="layout-topbar-logo" id="oip-app-topbar-logo-link" routerLink="">
|
|
37
|
+
<logo [height]="36" [width]="36"></logo>
|
|
38
|
+
<span>OIP</span>
|
|
39
|
+
</a>
|
|
40
|
+
</div>
|
|
41
|
+
|
|
42
|
+
@if (securityService.isAdmin && topBarService.topBarItems.length > 0) {
|
|
43
|
+
<p-tabs class="layout-topbar-tabs ml-2" [(value)]="topBarService.activeId">
|
|
44
|
+
<p-tablist>
|
|
45
|
+
@for (tab of topBarService.availableTopBarItems; track tab.id) {
|
|
46
|
+
<p-tab id="oip-app-topbar-tab-{{ tab.id }}" [value]="tab.id">
|
|
47
|
+
<i class="pi {{ tab.icon }}"></i>
|
|
48
|
+
<span class="ml-2">{{ tab.caption }}</span>
|
|
49
|
+
</p-tab>
|
|
50
|
+
}
|
|
51
|
+
</p-tablist>
|
|
52
|
+
</p-tabs>
|
|
53
|
+
}
|
|
54
|
+
<div class="layout-topbar-actions">
|
|
55
|
+
<div class="layout-config-menu">
|
|
56
|
+
<p-button
|
|
57
|
+
class="layout-topbar-action"
|
|
58
|
+
id="oip-app-topbar-theme-button"
|
|
59
|
+
severity="secondary"
|
|
60
|
+
type="button"
|
|
61
|
+
[rounded]="true"
|
|
62
|
+
[text]="true"
|
|
63
|
+
(click)="toggleDarkMode()">
|
|
64
|
+
<i
|
|
65
|
+
class="pi"
|
|
66
|
+
[ngClass]="{
|
|
67
|
+
'pi-moon': layoutService.isDarkTheme(),
|
|
68
|
+
'pi-sun': !layoutService.isDarkTheme()
|
|
69
|
+
}"></i>
|
|
70
|
+
</p-button>
|
|
71
|
+
<div class="relative">
|
|
72
|
+
<p-button
|
|
73
|
+
class="layout-topbar-action layout-topbar-action-highlight"
|
|
74
|
+
enterActiveClass="animate-scalein"
|
|
75
|
+
enterFromClass="hidden"
|
|
76
|
+
id="oip-app-topbar-palette-button"
|
|
77
|
+
leaveActiveClass="animate-fadeout"
|
|
78
|
+
leaveToClass="hidden"
|
|
79
|
+
pStyleClass="@next"
|
|
80
|
+
[hideOnOutsideClick]="true"
|
|
81
|
+
[rounded]="true">
|
|
82
|
+
<i class="pi pi-palette"></i>
|
|
83
|
+
</p-button>
|
|
84
|
+
<app-configurator />
|
|
85
|
+
</div>
|
|
86
|
+
</div>
|
|
87
|
+
|
|
88
|
+
<button
|
|
89
|
+
class="layout-topbar-menu-button layout-topbar-action"
|
|
90
|
+
enterActiveClass="animate-scalein"
|
|
91
|
+
enterFromClass="hidden"
|
|
92
|
+
id="oip-app-topbar-menu-expand-button"
|
|
93
|
+
leaveActiveClass="animate-fadeout"
|
|
94
|
+
leaveToClass="hidden"
|
|
95
|
+
pStyleClass="@next"
|
|
96
|
+
[hideOnOutsideClick]="true">
|
|
97
|
+
<i class="pi pi-ellipsis-v"></i>
|
|
98
|
+
</button>
|
|
99
|
+
|
|
100
|
+
<div class="layout-topbar-menu hidden lg:block">
|
|
101
|
+
<div class="layout-topbar-menu-content">
|
|
102
|
+
<button
|
|
103
|
+
class="layout-topbar-action"
|
|
104
|
+
id="oip-app-topbar-logout-button"
|
|
105
|
+
type="button"
|
|
106
|
+
(click)="securityService.logout()"
|
|
107
|
+
(keydown)="logoutKeyDown($event)">
|
|
108
|
+
<i class="pi pi-sign-out"></i>
|
|
109
|
+
<span>Logout</span>
|
|
110
|
+
</button>
|
|
111
|
+
<button class="layout-topbar-action" routerLink="config">
|
|
112
|
+
<p-avatar
|
|
113
|
+
class="p-link flex align-items-center"
|
|
114
|
+
id="oip-app-topbar-user-avatar"
|
|
115
|
+
shape="circle"
|
|
116
|
+
size="normal"
|
|
117
|
+
[image]="userService.photoLoaded ? userService.photo : null"
|
|
118
|
+
>{{ !userService.photoLoaded ? userService.shortLabel : null }}
|
|
119
|
+
</p-avatar>
|
|
120
|
+
<span class="ml-2">Profile</span>
|
|
121
|
+
</button>
|
|
122
|
+
</div>
|
|
123
|
+
</div>
|
|
124
|
+
</div>
|
|
125
|
+
</div>`
|
|
126
|
+
})
|
|
127
|
+
export class AppTopbar {
|
|
128
|
+
items!: MenuItem[];
|
|
129
|
+
securityService = inject(SecurityService);
|
|
130
|
+
topBarService = inject(TopBarService);
|
|
131
|
+
userService = inject(UserService);
|
|
132
|
+
|
|
133
|
+
constructor(public layoutService: LayoutService) {}
|
|
134
|
+
|
|
135
|
+
toggleDarkMode() {
|
|
136
|
+
this.layoutService.layoutConfig.update((state) => ({
|
|
137
|
+
...state,
|
|
138
|
+
darkTheme: !state.darkTheme
|
|
139
|
+
}));
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
logoutKeyDown($event: KeyboardEvent) {
|
|
143
|
+
if ($event.key === 'Enter') {
|
|
144
|
+
this.securityService.logout();
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { IsActiveMatchOptions, Params, QueryParamsHandling } from '@angular/router';
|
|
2
|
+
|
|
3
|
+
export interface ContextMenuItemDto {
|
|
4
|
+
url: any;
|
|
5
|
+
class: string | string[] | Set<string> | { [p: string]: any } | null | undefined;
|
|
6
|
+
command: any;
|
|
7
|
+
disabled: boolean;
|
|
8
|
+
fragment: string | undefined;
|
|
9
|
+
icon: string | string[] | Set<string> | { [p: string]: any } | null | undefined;
|
|
10
|
+
items: ContextMenuItemDto[];
|
|
11
|
+
label: string;
|
|
12
|
+
preserveFragment: unknown;
|
|
13
|
+
queryParamsHandling: QueryParamsHandling | null | undefined;
|
|
14
|
+
routerLink: string;
|
|
15
|
+
routerLinkActiveOptions: { exact: boolean } | IsActiveMatchOptions;
|
|
16
|
+
visible: boolean;
|
|
17
|
+
skipLocationChange: unknown;
|
|
18
|
+
queryParams: Params | null | undefined;
|
|
19
|
+
target: string;
|
|
20
|
+
state: { [p: string]: any } | undefined;
|
|
21
|
+
replaceUrl: unknown;
|
|
22
|
+
badgeClass: string;
|
|
23
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// TopBarDto interface
|
|
2
|
+
export interface TopBarDto {
|
|
3
|
+
// Identifier of item
|
|
4
|
+
id: 'content' | 'settings' | 'security';
|
|
5
|
+
// Icon from prime icon
|
|
6
|
+
icon: string;
|
|
7
|
+
// Caption
|
|
8
|
+
caption: string;
|
|
9
|
+
// Claims from SSO
|
|
10
|
+
claims?: string[];
|
|
11
|
+
// Method from module call when index toolbar change
|
|
12
|
+
click?(): void;
|
|
13
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { ContextMenuItemDto } from '../dtos/context-menu-item.dto';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Represents an event triggered when a menu item is clicked.
|
|
5
|
+
* @interface MenuChangeEvent
|
|
6
|
+
* @class
|
|
7
|
+
*/
|
|
8
|
+
export interface MenuChangeEvent {
|
|
9
|
+
/**
|
|
10
|
+
* A string representing a key, used for identification or access.
|
|
11
|
+
*/
|
|
12
|
+
key: string;
|
|
13
|
+
/**
|
|
14
|
+
* Indicates whether a route event has occurred.
|
|
15
|
+
* @type {boolean}
|
|
16
|
+
*/
|
|
17
|
+
routeEvent?: boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Represents a single item within a context menu. Contains data necessary to display the item
|
|
20
|
+
* and execute its associated action.
|
|
21
|
+
*/
|
|
22
|
+
item: ContextMenuItemDto;
|
|
23
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Convert Date to string in format 2022-02-22
|
|
3
|
+
* @param date
|
|
4
|
+
*/
|
|
5
|
+
export function dateToString(date: Date): string {
|
|
6
|
+
const year = date.getFullYear();
|
|
7
|
+
const month = (date.getMonth() + 1).toString().padStart(2, '0');
|
|
8
|
+
const day = date.getDate().toString().padStart(2, '0');
|
|
9
|
+
|
|
10
|
+
return `${year}-${month}-${day}`;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Convert string in format 2022-02-22 to Date
|
|
15
|
+
* @param date
|
|
16
|
+
*/
|
|
17
|
+
export function stringToDate(date: string): Date {
|
|
18
|
+
const dateParts = date.split('-');
|
|
19
|
+
const day = parseInt(dateParts[2], 10);
|
|
20
|
+
const month = parseInt(dateParts[1], 10) - 1;
|
|
21
|
+
const year = parseInt(dateParts[0], 10);
|
|
22
|
+
|
|
23
|
+
return new Date(year, month, day);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Add days to date
|
|
28
|
+
* @param date
|
|
29
|
+
* @param days
|
|
30
|
+
*/
|
|
31
|
+
export function addDays(date: Date, days: number): Date {
|
|
32
|
+
const clonedDate = structuredClone(date);
|
|
33
|
+
clonedDate.setDate(clonedDate.getDate() + days);
|
|
34
|
+
return clonedDate;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Get today's date
|
|
39
|
+
* @param date
|
|
40
|
+
*/
|
|
41
|
+
export function getTodayDate(): Date {
|
|
42
|
+
const date = new Date();
|
|
43
|
+
return new Date(date.getFullYear(), date.getMonth(), date.getDate());
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/* Convert dateformat from DatePipe to PrimeNG*/
|
|
47
|
+
export function convertToPrimeNgDateFormat(dateformat: string): string {
|
|
48
|
+
switch (dateformat) {
|
|
49
|
+
case 'dd.MM.yyyy':
|
|
50
|
+
return 'dd.mm.yy';
|
|
51
|
+
case 'dd.MM.yy':
|
|
52
|
+
return 'dd.mm.y';
|
|
53
|
+
case 'yyyy-MM-dd':
|
|
54
|
+
return 'yy-mm-dd';
|
|
55
|
+
case 'dd.MMM.yyyy':
|
|
56
|
+
return 'dd.M.yy';
|
|
57
|
+
default:
|
|
58
|
+
console.error(`Failed to convert format: ${dateformat}`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Преобразовать все свойства объекта со строковыми датами в даты
|
|
64
|
+
* @param obj
|
|
65
|
+
*/
|
|
66
|
+
export function restoreDates(obj: any) {
|
|
67
|
+
if (obj === null || typeof obj !== 'object') return obj;
|
|
68
|
+
|
|
69
|
+
if (Array.isArray(obj)) {
|
|
70
|
+
return obj.map((item) => restoreDates(item));
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const result = {};
|
|
74
|
+
for (const key in obj) {
|
|
75
|
+
const value = obj[key];
|
|
76
|
+
|
|
77
|
+
if (typeof value === 'string' && isIsoDate(value)) {
|
|
78
|
+
result[key] = new Date(value);
|
|
79
|
+
} else if (typeof value === 'object' && value !== null && !(value instanceof Date)) {
|
|
80
|
+
result[key] = restoreDates(value);
|
|
81
|
+
} else {
|
|
82
|
+
result[key] = value;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return result;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Проверить, является ли строка датой в ISO формате
|
|
90
|
+
* @param str
|
|
91
|
+
*/
|
|
92
|
+
export function isIsoDate(str: string) {
|
|
93
|
+
return /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?Z$/.test(str);
|
|
94
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { inject } from '@angular/core';
|
|
2
|
+
import { HttpInterceptorFn } from '@angular/common/http';
|
|
3
|
+
import { LayoutService } from '../services/app.layout.service';
|
|
4
|
+
|
|
5
|
+
export const langIntercept: HttpInterceptorFn = (req, next) => {
|
|
6
|
+
const layoutService = inject(LayoutService);
|
|
7
|
+
const lang = layoutService.language() ? layoutService.language() : 'en';
|
|
8
|
+
const httpHeaders = req.headers.set('Accept-language', lang);
|
|
9
|
+
const authReq = req.clone({
|
|
10
|
+
headers: httpHeaders
|
|
11
|
+
});
|
|
12
|
+
return next(authReq);
|
|
13
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { HttpClient } from '@angular/common/http';
|
|
2
|
+
import { map, Observable } from 'rxjs';
|
|
3
|
+
import { OpenIdConfiguration, StsConfigHttpLoader } from 'angular-auth-oidc-client';
|
|
4
|
+
import { GetKeycloakClientSettingsResponse } from '../api/data-contracts';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Load keycloak settings from backend and save to sessionStorage
|
|
8
|
+
* @param httpClient
|
|
9
|
+
* @returns StsConfigHttpLoader
|
|
10
|
+
*/
|
|
11
|
+
export const httpLoaderAuthFactory = (httpClient: HttpClient) => {
|
|
12
|
+
const KEYCLOAK_SETTINGS_KEY = 'keycloak-client-settings';
|
|
13
|
+
const settingsStings = sessionStorage.getItem(KEYCLOAK_SETTINGS_KEY);
|
|
14
|
+
if (settingsStings) {
|
|
15
|
+
const config$ = new Observable<GetKeycloakClientSettingsResponse>((subscribe) => {
|
|
16
|
+
subscribe.next(JSON.parse(settingsStings));
|
|
17
|
+
});
|
|
18
|
+
return new StsConfigHttpLoader(config$);
|
|
19
|
+
} else {
|
|
20
|
+
const config$ = httpClient.get<GetKeycloakClientSettingsResponse>(`api/security/get-keycloak-client-settings`).pipe(
|
|
21
|
+
map((config: GetKeycloakClientSettingsResponse) => {
|
|
22
|
+
const authConfig: OpenIdConfiguration = {
|
|
23
|
+
authority: config.authority,
|
|
24
|
+
redirectUrl: window.location.origin,
|
|
25
|
+
postLogoutRedirectUri: window.location.origin,
|
|
26
|
+
clientId: config.clientId,
|
|
27
|
+
scope: config.scope,
|
|
28
|
+
responseType: config.responseType,
|
|
29
|
+
useRefreshToken: config.useRefreshToken,
|
|
30
|
+
silentRenew: config.silentRenew,
|
|
31
|
+
logLevel: config.logLevel,
|
|
32
|
+
secureRoutes: config.secureRoutes
|
|
33
|
+
};
|
|
34
|
+
sessionStorage.setItem(KEYCLOAK_SETTINGS_KEY, JSON.stringify(authConfig));
|
|
35
|
+
return authConfig;
|
|
36
|
+
})
|
|
37
|
+
);
|
|
38
|
+
return new StsConfigHttpLoader(config$);
|
|
39
|
+
}
|
|
40
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { inject, Pipe, PipeTransform } from '@angular/core';
|
|
2
|
+
import { HttpClient } from '@angular/common/http';
|
|
3
|
+
import { DomSanitizer, SafeUrl } from '@angular/platform-browser';
|
|
4
|
+
import { map, Observable } from 'rxjs';
|
|
5
|
+
|
|
6
|
+
@Pipe({
|
|
7
|
+
standalone: true,
|
|
8
|
+
name: 'secure'
|
|
9
|
+
})
|
|
10
|
+
export class SecurePipe implements PipeTransform {
|
|
11
|
+
private readonly http = inject(HttpClient);
|
|
12
|
+
private readonly sanitizer = inject(DomSanitizer);
|
|
13
|
+
|
|
14
|
+
transform(url: any): Observable<SafeUrl> {
|
|
15
|
+
return this.http
|
|
16
|
+
.get(url, { responseType: 'blob' })
|
|
17
|
+
.pipe(map((val) => this.sanitizer.bypassSecurityTrustUrl(URL.createObjectURL(val))));
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// Components
|
|
2
|
+
export * from './components/base-module.component';
|
|
3
|
+
export * from './components/security.component';
|
|
4
|
+
export * from './components/top-bar.component';
|
|
5
|
+
export * from './components/footer.component';
|
|
6
|
+
export * from './components/menu/menu.component';
|
|
7
|
+
export * from './components/app.layout.component';
|
|
8
|
+
export * from './components/sidebar.component';
|
|
9
|
+
export * from './components/logo.component';
|
|
10
|
+
export * from './components/notfound.component';
|
|
11
|
+
export * from './components/auth/unauthorized/unauthorized.component';
|
|
12
|
+
export * from './components/auth/error/error.component';
|
|
13
|
+
export * from './components/profile.component';
|
|
14
|
+
export { ConfigComponent } from './components/config.component';
|
|
15
|
+
export * from './components/db-migration.component';
|
|
16
|
+
export { AppModulesComponent } from './components/app-modules.component';
|
|
17
|
+
|
|
18
|
+
// Dtos
|
|
19
|
+
export * from './dtos/top-bar.dto';
|
|
20
|
+
export * from './dtos/security.dto';
|
|
21
|
+
export * from './dtos/put-security.dto';
|
|
22
|
+
export * from './dtos/no-settings.dto';
|
|
23
|
+
|
|
24
|
+
// Services
|
|
25
|
+
export * from './services/base-data.service';
|
|
26
|
+
export * from './services/top-bar.service';
|
|
27
|
+
export * from './services/security.service';
|
|
28
|
+
export * from './services/security-data.service';
|
|
29
|
+
export * from './services/msg.service';
|
|
30
|
+
export * from './services/auth.service';
|
|
31
|
+
export * from './services/app.layout.service';
|
|
32
|
+
export * from './services/app.menu.service';
|
|
33
|
+
export * from './services/user.service';
|
|
34
|
+
export * from './services/security-storage.service';
|
|
35
|
+
|
|
36
|
+
// Events
|
|
37
|
+
export * from './events/menu-change.event';
|
|
38
|
+
|
|
39
|
+
// other
|
|
40
|
+
export { langIntercept } from './intercepts/i18n-intercept.service';
|
|
41
|
+
export { SecurePipe } from './modules/secure.pipe';
|
|
42
|
+
export { httpLoaderAuthFactory } from './modules/http-loader.factory';
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { inject, Injectable } from '@angular/core';
|
|
2
|
+
import { BehaviorSubject } from 'rxjs';
|
|
3
|
+
import { Title } from '@angular/platform-browser';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Service to manage the application title.
|
|
7
|
+
*/
|
|
8
|
+
@Injectable({ providedIn: 'root' })
|
|
9
|
+
export class AppTitleService {
|
|
10
|
+
private readonly title = inject(Title);
|
|
11
|
+
private titleSubject = new BehaviorSubject<string>('');
|
|
12
|
+
public title$ = this.titleSubject.asObservable();
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Set the title of the current HTML document.
|
|
16
|
+
* @param newTitle
|
|
17
|
+
*/
|
|
18
|
+
setTitle(newTitle: string): void {
|
|
19
|
+
this.title.setTitle(newTitle);
|
|
20
|
+
this.titleSubject.next(newTitle);
|
|
21
|
+
}
|
|
22
|
+
}
|