@corp-products/app-core 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 +3 -0
- package/eslint.config.js +48 -0
- package/ng-package.json +7 -0
- package/package.json +20 -0
- package/project.json +29 -0
- package/src/index.ts +9 -0
- package/src/lib/core/components/app-breadcrumb/app-breadcrumb.component.html +7 -0
- package/src/lib/core/components/app-breadcrumb/app-breadcrumb.component.scss +8 -0
- package/src/lib/core/components/app-breadcrumb/app-breadcrumb.component.ts +125 -0
- package/src/lib/core/components/app-breadcrumb/app-breadcrumb.interface.ts +15 -0
- package/src/lib/core/components/app-header/app-header.component.html +26 -0
- package/src/lib/core/components/app-header/app-header.component.scss +0 -0
- package/src/lib/core/components/app-header/app-header.component.ts +48 -0
- package/src/lib/core/components/app-side-menu/app-side-menu.component.html +20 -0
- package/src/lib/core/components/app-side-menu/app-side-menu.component.scss +0 -0
- package/src/lib/core/components/app-side-menu/app-side-menu.component.ts +30 -0
- package/src/lib/core/components/app-side-menu/routes-names.ts +28 -0
- package/src/lib/core/components/app-side-menu/side-menu-items.ts +45 -0
- package/src/lib/core/components/app-side-menu/side-menu.ts +12 -0
- package/src/lib/core/components/global-loader/global-loader.component.html +18 -0
- package/src/lib/core/components/global-loader/global-loader.component.scss +0 -0
- package/src/lib/core/components/global-loader/global-loader.component.spec.ts +24 -0
- package/src/lib/core/components/global-loader/global-loader.component.ts +16 -0
- package/src/lib/core/components/index.ts +4 -0
- package/src/lib/core/components/no-access/no-access.component.html +10 -0
- package/src/lib/core/components/no-access/no-access.component.scss +0 -0
- package/src/lib/core/components/no-access/no-access.component.ts +19 -0
- package/src/lib/core/core-config.ts +11 -0
- package/src/lib/core/directives/index.ts +1 -0
- package/src/lib/core/directives/permissions.directive.ts +114 -0
- package/src/lib/core/enums/index.ts +1 -0
- package/src/lib/core/enums/user-permissions.enum.ts +36 -0
- package/src/lib/core/guards/auth.guard.ts +17 -0
- package/src/lib/core/guards/index.ts +2 -0
- package/src/lib/core/guards/permissions.guard.ts +17 -0
- package/src/lib/core/handlers/http-context-handler.ts +9 -0
- package/src/lib/core/handlers/storage-handler.ts +37 -0
- package/src/lib/core/handlers/stortage.ts +7 -0
- package/src/lib/core/interceptors/app-http-config.ts +8 -0
- package/src/lib/core/interceptors/base-http.interceptor.ts +44 -0
- package/src/lib/core/interceptors/global-http-error.interceptor.ts +21 -0
- package/src/lib/core/interceptors/index.ts +3 -0
- package/src/lib/core/interfaces/index.ts +3 -0
- package/src/lib/core/interfaces/jwt-token-decoded.interface.ts +7 -0
- package/src/lib/core/interfaces/list-response.interface.ts +5 -0
- package/src/lib/core/interfaces/router-data.interface.ts +20 -0
- package/src/lib/core/interfaces/user-info.interface.ts +4 -0
- package/src/lib/core/interfaces/user-profile-data.ts +19 -0
- package/src/lib/core/services/auth.service.ts +63 -0
- package/src/lib/core/services/base-http-service/base-http-response.ts +14 -0
- package/src/lib/core/services/base-http-service/base-http-types.ts +42 -0
- package/src/lib/core/services/base-http-service/base-http-utils.ts +48 -0
- package/src/lib/core/services/base-http-service/base-http.service.ts +106 -0
- package/src/lib/core/services/base-http-service/http-context-handler.ts +9 -0
- package/src/lib/core/services/base-http-service/index.ts +4 -0
- package/src/lib/core/services/base-pagination-service/base-pagination.service.ts +59 -0
- package/src/lib/core/services/base-pagination-service/index.ts +3 -0
- package/src/lib/core/services/base-pagination-service/list-options.interface.ts +8 -0
- package/src/lib/core/services/base-pagination-service/pagination.ts +6 -0
- package/src/lib/core/services/enviroment.base.service.ts +16 -0
- package/src/lib/core/services/error-handler.service.ts +74 -0
- package/src/lib/core/services/index.ts +8 -0
- package/src/lib/core/services/jwt-decoder.service.ts +23 -0
- package/src/lib/core/services/loader.service.ts +32 -0
- package/src/lib/core/services/lov.service.ts +22 -0
- package/src/lib/core/services/permissions.service.ts +66 -0
- package/src/lib/core/services/toaster.service.ts +65 -0
- package/tsconfig.json +26 -0
- package/tsconfig.lib.json +11 -0
- package/tsconfig.lib.prod.json +9 -0
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Directive,
|
|
3
|
+
Input,
|
|
4
|
+
OnDestroy,
|
|
5
|
+
OnInit,
|
|
6
|
+
TemplateRef,
|
|
7
|
+
ViewContainerRef,
|
|
8
|
+
} from '@angular/core';
|
|
9
|
+
import { PermissionsService } from '../services';
|
|
10
|
+
import { PermissionsActions, UserPermissionsEnum } from '../enums';
|
|
11
|
+
import { Subscription } from 'rxjs';
|
|
12
|
+
|
|
13
|
+
@Directive({
|
|
14
|
+
selector: '[hasPermissions]',
|
|
15
|
+
standalone: true
|
|
16
|
+
})
|
|
17
|
+
export class HasPermissionsDirective implements OnInit, OnDestroy {
|
|
18
|
+
private _actions: string[];
|
|
19
|
+
private _key: string;
|
|
20
|
+
private _newPermissions: string[];
|
|
21
|
+
private _isDomain: boolean;
|
|
22
|
+
|
|
23
|
+
private isViewCreated = false;
|
|
24
|
+
private subscription: Subscription;
|
|
25
|
+
|
|
26
|
+
constructor(
|
|
27
|
+
private permissionsService: PermissionsService,
|
|
28
|
+
private viewContainer: ViewContainerRef,
|
|
29
|
+
private templateRef: TemplateRef<unknown>
|
|
30
|
+
) {}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* make sure that the key value looks like the UserPermissionsEnum enum
|
|
34
|
+
* usage: *permissions="[PermissionsActions.EDIT]; key: LOOKUPS
|
|
35
|
+
*/
|
|
36
|
+
|
|
37
|
+
@Input({ required: true }) set hasPermissions(actions: string[]) {
|
|
38
|
+
if (this.arraysChanged(this._actions, actions)) {
|
|
39
|
+
this._actions = actions;
|
|
40
|
+
this.updateView();
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
@Input({ required: true }) set hasPermissionsKey(key: string) {
|
|
45
|
+
if (this._key !== key) {
|
|
46
|
+
this._key = key;
|
|
47
|
+
this.updateView();
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
@Input() set hasPermissionsNewPermissions(newPermissions: string[]) {
|
|
52
|
+
if (this.arraysChanged(this._newPermissions, newPermissions)) {
|
|
53
|
+
this._newPermissions = newPermissions;
|
|
54
|
+
this.updateView();
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
@Input() set hasPermissionsIsDomain(isDomain: boolean) {
|
|
59
|
+
if (this._isDomain !== isDomain) {
|
|
60
|
+
this._isDomain = isDomain;
|
|
61
|
+
this.updateView();
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
ngOnInit(): void {
|
|
66
|
+
this.subscription = this.permissionsService.domainPermissions$.subscribe(() => {
|
|
67
|
+
if (this._isDomain) {
|
|
68
|
+
this.updateView();
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
ngOnDestroy(): void {
|
|
74
|
+
this.subscription?.unsubscribe();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
private updateView() {
|
|
78
|
+
const canShow = this._actions.some(action => this.hasPermission(action));
|
|
79
|
+
|
|
80
|
+
if (canShow && !this.isViewCreated) {
|
|
81
|
+
this.viewContainer.createEmbeddedView(this.templateRef);
|
|
82
|
+
this.isViewCreated = true;
|
|
83
|
+
} else if (!canShow && this.isViewCreated) {
|
|
84
|
+
this.clearView();
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
private hasPermission(action: string): boolean {
|
|
89
|
+
if (this._isDomain) {
|
|
90
|
+
return this.permissionsService.checkDomainHasPermission(
|
|
91
|
+
this._key as UserPermissionsEnum,
|
|
92
|
+
action as PermissionsActions
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return this.permissionsService.checkKeyHasPermission(
|
|
97
|
+
this._key as UserPermissionsEnum,
|
|
98
|
+
action as PermissionsActions,
|
|
99
|
+
this._newPermissions
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
private clearView() {
|
|
104
|
+
this.viewContainer.clear();
|
|
105
|
+
this.isViewCreated = false;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
private arraysChanged(a?: string[], b?: string[]): boolean {
|
|
109
|
+
if (a === b) return false;
|
|
110
|
+
if (!a || !b) return true;
|
|
111
|
+
if (a.length !== b.length) return true;
|
|
112
|
+
return a.some((v, i) => v !== b[i]);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './user-permissions.enum';
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export enum UserPermissionsEnum {
|
|
2
|
+
BOD = "BOD",
|
|
3
|
+
BOARD_COMMITTEES = "BOARD_COMMITTEES",
|
|
4
|
+
BOARD_MEETINGS = "BOARD_MEETINGS",
|
|
5
|
+
MEETING_INSIDE_THE_BOD = "MEETING_INSIDE_THE_BOD",
|
|
6
|
+
COMMITTEE = "COMMITTEE",
|
|
7
|
+
MEETING_INSIDE_THE_COMMITTEE = "MEETING_INSIDE_THE_COMMITTEE",
|
|
8
|
+
MEETING = "MEETING",
|
|
9
|
+
DECISION = "DECISION",
|
|
10
|
+
RECOMMENDATION = "RECOMMENDATION",
|
|
11
|
+
TASK = "TASK",
|
|
12
|
+
VOTING = "VOTING",
|
|
13
|
+
VOTING_SECRETARY_RESULTS = "VOTING_SECRETARY_RESULTS",
|
|
14
|
+
VOTING_RESULTS = "VOTING_RESULTS",
|
|
15
|
+
DRAFT_MOM = "DRAFT_MOM",
|
|
16
|
+
FINAL_MOM = "FINAL_MOM",
|
|
17
|
+
MOM = "MOM",
|
|
18
|
+
AGENDA = "AGENDA",
|
|
19
|
+
ATTENDEES = "ATTENDEES",
|
|
20
|
+
COMMENT = "COMMENT"
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export enum PermissionsActions {
|
|
24
|
+
VIEW = "VIEW",
|
|
25
|
+
CREATE = "CREATE",
|
|
26
|
+
EDIT = "EDIT",
|
|
27
|
+
DELETE = "DELETE",
|
|
28
|
+
REORDER = "REORDER",
|
|
29
|
+
DOWNLOAD = "DOWNLOAD",
|
|
30
|
+
MARK = "MARK",
|
|
31
|
+
GENERATE = "GENERATE",
|
|
32
|
+
TERMINATE = "TERMINATE",
|
|
33
|
+
START = "START",
|
|
34
|
+
END = "END",
|
|
35
|
+
ACTIVATE = "ACTIVATE"
|
|
36
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { inject } from "@angular/core";
|
|
2
|
+
import { CanActivateFn } from "@angular/router";
|
|
3
|
+
import { AuthService } from "../services";
|
|
4
|
+
import { CORE_CONFIG, CoreConfig } from "../core-config";
|
|
5
|
+
|
|
6
|
+
export const authGuard: CanActivateFn = () => {
|
|
7
|
+
const appConfig = inject<CoreConfig>(CORE_CONFIG);
|
|
8
|
+
const authService = inject(AuthService);
|
|
9
|
+
const token: string | null = authService.getUserToken();
|
|
10
|
+
|
|
11
|
+
if (!token) {
|
|
12
|
+
window.location.href = appConfig.loginUrl;
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return true;
|
|
17
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { inject } from "@angular/core";
|
|
2
|
+
import { CanActivateFn, Router } from "@angular/router";
|
|
3
|
+
import { PermissionsService } from "../services";
|
|
4
|
+
import { RoutePermissionsInfo } from "../interfaces";
|
|
5
|
+
|
|
6
|
+
export const permissionsGuard: CanActivateFn = (route) => {
|
|
7
|
+
const permissionsService = inject(PermissionsService);
|
|
8
|
+
const router = inject(Router);
|
|
9
|
+
const { key, action }: RoutePermissionsInfo = route.data["permissions"];
|
|
10
|
+
|
|
11
|
+
if (!route.data["permissions"] || (route.data["permissions"] && permissionsService.routeGuardChecker(key, action))) {
|
|
12
|
+
return true;
|
|
13
|
+
} else {
|
|
14
|
+
router.navigate(["no-access"]);
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { HttpContext, HttpContextToken } from '@angular/common/http';
|
|
2
|
+
|
|
3
|
+
export const IS_SYSTEM_LOADER = new HttpContextToken<boolean>(() => true); // set default loader
|
|
4
|
+
|
|
5
|
+
export class HttpContextHandler {
|
|
6
|
+
static setLoaderType(isSystemLoader: boolean) {
|
|
7
|
+
return new HttpContext().set(IS_SYSTEM_LOADER, isSystemLoader);
|
|
8
|
+
}
|
|
9
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// Important for global context
|
|
2
|
+
import { LocalStorageKeys, SessionStorageKeys, StorageEnum } from "./stortage";
|
|
3
|
+
|
|
4
|
+
const setLocalStorage = (key: string, value: string) => localStorage.setItem(key, value);
|
|
5
|
+
const getLocalStorage = (key: string) => localStorage.getItem(key);
|
|
6
|
+
const clearLocalStorage = () => localStorage.clear();
|
|
7
|
+
const setSessionStorage = (key: string, value: string) => sessionStorage.setItem(key, value);
|
|
8
|
+
const getSessionStorage = (key: string) => sessionStorage.getItem(key);
|
|
9
|
+
const clearSessionStorage = () => sessionStorage.clear();
|
|
10
|
+
|
|
11
|
+
export class StorageHandler {
|
|
12
|
+
public static local = {
|
|
13
|
+
set: (key: LocalStorageKeys, value: unknown) => this.setItem(key, value, setLocalStorage),
|
|
14
|
+
get: <T>(key: LocalStorageKeys) => this.getItem<T>(key, getLocalStorage),
|
|
15
|
+
clear: () => this.clearStorage(clearLocalStorage)
|
|
16
|
+
};
|
|
17
|
+
public static session = {
|
|
18
|
+
set: (key: SessionStorageKeys, value: unknown) => this.setItem(key, value, setSessionStorage),
|
|
19
|
+
get: <T>(key: SessionStorageKeys) => this.getItem<T>(key, getSessionStorage),
|
|
20
|
+
clear: () => this.clearStorage(clearSessionStorage)
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
private static setItem(key: StorageEnum, value: unknown, set: (key: string, value: string) => void): void {
|
|
24
|
+
if (!key) return;
|
|
25
|
+
set(key as string, typeof value === "string" ? value : JSON.stringify(value));
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
private static getItem<T>(key: StorageEnum, get: (key: string) => string | null, withParsing = false): T | null {
|
|
29
|
+
const data: string | null = get(key as string) ?? null;
|
|
30
|
+
if (data) return withParsing ? JSON.parse(data) : data;
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
private static clearStorage(clear: () => void): void {
|
|
35
|
+
clear();
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export const COMMON_BASE_URL = "common/api/v1";
|
|
2
|
+
export const APP_HTTP_CONFIG = {
|
|
3
|
+
BOARD_BASE_URL: "board-acl/private/v1",
|
|
4
|
+
MEETING_BASE_URL: "meeting-acl/private/v1",
|
|
5
|
+
IGATE_BASE_URL: COMMON_BASE_URL + "/igate-users",
|
|
6
|
+
LOV_BASE_URL: COMMON_BASE_URL + "/lov",
|
|
7
|
+
ATTACHMENT_BASE_URL: COMMON_BASE_URL + "/attachment"
|
|
8
|
+
};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from "@angular/common/http";
|
|
2
|
+
import { Inject, inject, Injectable } from "@angular/core";
|
|
3
|
+
import { finalize, Observable, tap } from "rxjs";
|
|
4
|
+
import { IS_SYSTEM_LOADER } from "../handlers/http-context-handler";
|
|
5
|
+
import { AuthService } from "../services/auth.service";
|
|
6
|
+
import { LoaderService } from "../services/loader.service";
|
|
7
|
+
import { CORE_CONFIG, CoreConfig } from "../core-config";
|
|
8
|
+
|
|
9
|
+
@Injectable()
|
|
10
|
+
export class HttpBaseInterceptor implements HttpInterceptor {
|
|
11
|
+
authService = inject(AuthService);
|
|
12
|
+
loaderService = inject(LoaderService);
|
|
13
|
+
|
|
14
|
+
constructor(@Inject(CORE_CONFIG) protected appConfig: CoreConfig) {}
|
|
15
|
+
|
|
16
|
+
intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
|
|
17
|
+
console.log("interceptor", this.appConfig);
|
|
18
|
+
const URL = `${this.appConfig.gatewayUrl}${request.url}`;
|
|
19
|
+
const token = this.authService.getUserToken();
|
|
20
|
+
|
|
21
|
+
// Handler header
|
|
22
|
+
request = request.clone({
|
|
23
|
+
url: URL,
|
|
24
|
+
setHeaders: {
|
|
25
|
+
Authorization: `Bearer ${token}`,
|
|
26
|
+
Language: "en"
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
// Handle loader
|
|
31
|
+
const IS_SYSTEM_LOADER_CHECK = request.context.get(IS_SYSTEM_LOADER);
|
|
32
|
+
this.loaderService.setLoading(true, IS_SYSTEM_LOADER_CHECK, request.url);
|
|
33
|
+
|
|
34
|
+
return next.handle(request).pipe(
|
|
35
|
+
// Handler Token
|
|
36
|
+
tap((event) => {
|
|
37
|
+
// if (event instanceof HttpResponse && request.url.includes(User_Token)) {
|
|
38
|
+
// Set user token ..
|
|
39
|
+
// }
|
|
40
|
+
}),
|
|
41
|
+
finalize(() => this.loaderService.setLoading(false, IS_SYSTEM_LOADER_CHECK, request.url))
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';
|
|
2
|
+
import { Observable, retry, timer } from 'rxjs';
|
|
3
|
+
|
|
4
|
+
export class GlobalHttpErrorInterceptor implements HttpInterceptor {
|
|
5
|
+
// check if it is a server error to retry the request
|
|
6
|
+
shouldRetry(error: HttpErrorResponse) {
|
|
7
|
+
if (error.status >= 500) {
|
|
8
|
+
return timer(1000);
|
|
9
|
+
}
|
|
10
|
+
throw error;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
|
|
14
|
+
return next.handle(request).pipe(
|
|
15
|
+
retry({
|
|
16
|
+
count: 1,
|
|
17
|
+
delay: this.shouldRetry
|
|
18
|
+
})
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Route } from "@angular/router";
|
|
2
|
+
import {PermissionsActions, UserPermissionsEnum} from "../enums";
|
|
3
|
+
|
|
4
|
+
export interface RouteData {
|
|
5
|
+
breadcrumb?: ((data: RouteData) => string) | string;
|
|
6
|
+
showBreadcrumb?: boolean;
|
|
7
|
+
isClickableInBreadcrumb?: boolean;
|
|
8
|
+
permissions?: RoutePermissionsInfo;
|
|
9
|
+
|
|
10
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
11
|
+
[key: string]: any;
|
|
12
|
+
}
|
|
13
|
+
export interface RoutePermissionsInfo {
|
|
14
|
+
key: UserPermissionsEnum;
|
|
15
|
+
action: PermissionsActions;
|
|
16
|
+
}
|
|
17
|
+
export interface AppRoute extends Route {
|
|
18
|
+
data?: RouteData;
|
|
19
|
+
children?: AppRoute[];
|
|
20
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export interface UserProfileData {
|
|
2
|
+
contact: Contact;
|
|
3
|
+
position: Position;
|
|
4
|
+
email: string;
|
|
5
|
+
profileImage: string;
|
|
6
|
+
nameEn: string;
|
|
7
|
+
nameAr: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface Contact {
|
|
11
|
+
mobile: string;
|
|
12
|
+
work: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface Position {
|
|
16
|
+
code: string;
|
|
17
|
+
name: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { Inject, Injectable } from "@angular/core";
|
|
2
|
+
import { BehaviorSubject } from "rxjs";
|
|
3
|
+
import { StorageHandler } from "../handlers/storage-handler";
|
|
4
|
+
import { LocalStorageKeys } from "../handlers/stortage";
|
|
5
|
+
import { BaseHttpService, HttpConfig } from "./base-http-service";
|
|
6
|
+
import { CORE_CONFIG, CoreConfig } from "../core-config";
|
|
7
|
+
|
|
8
|
+
@Injectable({
|
|
9
|
+
providedIn: "root"
|
|
10
|
+
})
|
|
11
|
+
export class AuthService extends BaseHttpService {
|
|
12
|
+
public isUserLoggedIn$ = new BehaviorSubject(this.isLoggedIn());
|
|
13
|
+
|
|
14
|
+
constructor(@Inject(CORE_CONFIG) protected appConfig: CoreConfig) {
|
|
15
|
+
super();
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
override setApiConfig(): HttpConfig {
|
|
19
|
+
return {
|
|
20
|
+
apiUrl: "",
|
|
21
|
+
microServiceUrl: ""
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
initAuthentication() {
|
|
26
|
+
const token: string | null = new URLSearchParams(window.location.search).get("token");
|
|
27
|
+
if (token) {
|
|
28
|
+
this.setAuthentication(token);
|
|
29
|
+
window.location.search = "";
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
setAuthentication(token: string) {
|
|
34
|
+
this.isUserLoggedIn$.next(true);
|
|
35
|
+
this.setUserToken(token);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
isLoggedIn(): boolean {
|
|
39
|
+
return !!this.getUserToken();
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
getUserToken(): string | null {
|
|
43
|
+
return StorageHandler.local.get(LocalStorageKeys.TOKEN);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
setUserToken(token?: string) {
|
|
47
|
+
return StorageHandler.local.set(LocalStorageKeys.TOKEN, token);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
logoutFromSSO() {
|
|
51
|
+
const logoutUrl = this.appConfig.logoutEndpoint;
|
|
52
|
+
return this.single<unknown>("", {
|
|
53
|
+
urlRewrite: logoutUrl
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
clearAuth() {
|
|
58
|
+
StorageHandler.local.clear();
|
|
59
|
+
StorageHandler.session.clear();
|
|
60
|
+
this.isUserLoggedIn$.next(false);
|
|
61
|
+
window.location.href = this.appConfig.loginUrl;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { HttpContext, HttpHeaders, HttpParams } from '@angular/common/http';
|
|
2
|
+
import { Params as RouterParams } from '@angular/router';
|
|
3
|
+
|
|
4
|
+
export interface HttpConfig {
|
|
5
|
+
apiUrl?: string;
|
|
6
|
+
microServiceUrl:string;
|
|
7
|
+
globalMapFn?: (res: any) => any;
|
|
8
|
+
methods?: ServiceConfig;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export type Params =
|
|
12
|
+
| HttpParams
|
|
13
|
+
| { [param: string]: string | string[] | unknown };
|
|
14
|
+
export type HttpRequestOptions = {
|
|
15
|
+
headers?:
|
|
16
|
+
| HttpHeaders
|
|
17
|
+
| RouterParams
|
|
18
|
+
| { [header: string]: string | string[] };
|
|
19
|
+
params?: HttpParams | { [param: string]: string | string[] } | Params;
|
|
20
|
+
context?: HttpContext;
|
|
21
|
+
reportProgress?: boolean;
|
|
22
|
+
observe?: 'events';
|
|
23
|
+
responseType?: string;
|
|
24
|
+
};
|
|
25
|
+
export const HttpRequestOptionsKeys: Array<keyof Required<HttpRequestOptions>> =
|
|
26
|
+
['headers', 'params', 'context', 'reportProgress', 'observe', 'responseType'];
|
|
27
|
+
|
|
28
|
+
export type HttpOptions<T = any> = HttpRequestOptions & {
|
|
29
|
+
urlRewrite?: string;
|
|
30
|
+
urlPostfix?: string;
|
|
31
|
+
mapFn?: (res: any) => T;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export interface ServiceConfig {
|
|
35
|
+
single?: HttpOptions;
|
|
36
|
+
getAll?: HttpOptions;
|
|
37
|
+
add?: HttpOptions;
|
|
38
|
+
update?: HttpOptions;
|
|
39
|
+
delete?: HttpOptions;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE';
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { map } from 'rxjs/operators';
|
|
2
|
+
import { HttpOptions, HttpRequestOptionsKeys } from './base-http-types';
|
|
3
|
+
import { BaseHttpResponse } from './base-http-response';
|
|
4
|
+
|
|
5
|
+
export const resolveUrl = (
|
|
6
|
+
baseUrl: string,
|
|
7
|
+
options?: HttpOptions,
|
|
8
|
+
...args: string[]
|
|
9
|
+
): string => {
|
|
10
|
+
const { urlRewrite, urlPostfix } = options || {};
|
|
11
|
+
|
|
12
|
+
if (urlRewrite) {
|
|
13
|
+
return urlRewrite;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
let result = baseUrl;
|
|
17
|
+
|
|
18
|
+
if (args && args.length > 0 && args[0] !== '') {
|
|
19
|
+
result += `/${args.join('/')}`;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (urlPostfix) {
|
|
23
|
+
result += `/${urlPostfix}`;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return result;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export const extractRequestOptions = (options?: any) => {
|
|
30
|
+
if (!options || typeof options !== 'object') {
|
|
31
|
+
return {};
|
|
32
|
+
}
|
|
33
|
+
/* eslint-disable */
|
|
34
|
+
return HttpRequestOptionsKeys.reduce((requestOptions: any, key) => {
|
|
35
|
+
const value = options[key];
|
|
36
|
+
|
|
37
|
+
if (value !== undefined) {
|
|
38
|
+
requestOptions[key] = value;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return requestOptions;
|
|
42
|
+
}, {});
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export const mapResponse = <T>(options?: HttpOptions) =>
|
|
46
|
+
map((res: T) => (options?.mapFn ? options.mapFn(res) : res));
|
|
47
|
+
|
|
48
|
+
export const epmDefaultMapper = <T>(res: BaseHttpResponse<T>) => res.payload;
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { HttpClient } from '@angular/common/http';
|
|
2
|
+
import { inject } from '@angular/core';
|
|
3
|
+
import { Observable } from 'rxjs';
|
|
4
|
+
import { HttpConfig, HttpOptions, ServiceConfig } from './base-http-types';
|
|
5
|
+
import { epmDefaultMapper, extractRequestOptions, mapResponse, resolveUrl } from './base-http-utils';
|
|
6
|
+
|
|
7
|
+
export abstract class BaseHttpService {
|
|
8
|
+
private readonly http: HttpClient = inject(HttpClient);
|
|
9
|
+
|
|
10
|
+
get url(): string {
|
|
11
|
+
const { apiUrl,microServiceUrl } = this.setApiConfig();
|
|
12
|
+
return `${microServiceUrl}${apiUrl ? `/${apiUrl}` : ''}`;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
get methodsConfig(): ServiceConfig | undefined {
|
|
16
|
+
const { methods } = this.setApiConfig();
|
|
17
|
+
return methods;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
get globalMapFn(): (res: any) => any {
|
|
21
|
+
const { globalMapFn } = this.setApiConfig();
|
|
22
|
+
return globalMapFn || epmDefaultMapper;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
abstract setApiConfig(): HttpConfig;
|
|
26
|
+
|
|
27
|
+
single<T>(id: string | number, options?: HttpOptions): Observable<T> {
|
|
28
|
+
const activeOptions = options || this.getMethodConfig('single');
|
|
29
|
+
const url = resolveUrl(this.url, activeOptions, id?.toString());
|
|
30
|
+
const requestOptions = extractRequestOptions(activeOptions);
|
|
31
|
+
|
|
32
|
+
return this.http
|
|
33
|
+
.get<T>(url, requestOptions)
|
|
34
|
+
.pipe(mapResponse(this.getMapFun(activeOptions)));
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
getAll<T>(options?: HttpOptions): Observable<T> {
|
|
38
|
+
const activeOptions = options || this.getMethodConfig('getAll');
|
|
39
|
+
const url = resolveUrl(this.url, activeOptions);
|
|
40
|
+
const requestOptions = extractRequestOptions(activeOptions);
|
|
41
|
+
|
|
42
|
+
return this.http
|
|
43
|
+
.get<T>(url, requestOptions)
|
|
44
|
+
.pipe(mapResponse(this.getMapFun(activeOptions)));
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
add<T>(body: unknown, options?: HttpOptions): Observable<T> {
|
|
48
|
+
const activeOptions = options || this.getMethodConfig('add');
|
|
49
|
+
const url = resolveUrl(this.url, activeOptions);
|
|
50
|
+
const requestOptions = { ...extractRequestOptions(activeOptions) };
|
|
51
|
+
|
|
52
|
+
return this.http
|
|
53
|
+
.post<T>(url, body, requestOptions)
|
|
54
|
+
.pipe(mapResponse(this.getMapFun(activeOptions)));
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
update<T>(
|
|
58
|
+
id: string | number,
|
|
59
|
+
body: unknown,
|
|
60
|
+
options?: HttpOptions
|
|
61
|
+
): Observable<T> {
|
|
62
|
+
const activeOptions = options || this.getMethodConfig('update');
|
|
63
|
+
const url = resolveUrl(this.url, activeOptions, id?.toString());
|
|
64
|
+
const requestOptions = { ...extractRequestOptions(activeOptions) };
|
|
65
|
+
|
|
66
|
+
return this.http
|
|
67
|
+
.put<T>(url, body, requestOptions)
|
|
68
|
+
.pipe(mapResponse(this.getMapFun(activeOptions)));
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
delete<T>(id: string | number, options?: HttpOptions): Observable<T> {
|
|
72
|
+
const activeOptions = options || this.getMethodConfig('delete');
|
|
73
|
+
const url = resolveUrl(this.url, activeOptions, id?.toString());
|
|
74
|
+
const requestOptions = extractRequestOptions(activeOptions);
|
|
75
|
+
|
|
76
|
+
return this.http
|
|
77
|
+
.delete<T>(url, requestOptions)
|
|
78
|
+
.pipe(mapResponse(this.getMapFun(activeOptions)));
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
protected getMethodConfig(method: keyof ServiceConfig): any {
|
|
82
|
+
return this.methodsConfig ? this.methodsConfig[method] : undefined;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
protected getMapFun(options?: HttpOptions) {
|
|
86
|
+
const getMapFunObj = {
|
|
87
|
+
default: { mapFn: epmDefaultMapper },
|
|
88
|
+
custom: { mapFn: this.globalMapFn },
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
if (
|
|
92
|
+
options &&
|
|
93
|
+
(options?.reportProgress || options?.responseType === 'blob')
|
|
94
|
+
) {
|
|
95
|
+
return { mapFn: (res: unknown) => res };
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (options && options?.mapFn) {
|
|
99
|
+
return { mapFn: options.mapFn };
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const isGlobalMapFnValid =
|
|
103
|
+
this.globalMapFn && typeof this.globalMapFn === 'function';
|
|
104
|
+
return isGlobalMapFnValid ? getMapFunObj.custom : getMapFunObj.default;
|
|
105
|
+
}
|
|
106
|
+
}
|