nexus-shared 1.1.19 → 1.1.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/package.json +1 -1
- package/src/api-services/authentication-service.tsx +23 -0
- package/src/api-services/system-service.tsx +24 -316
- package/src/client.ts +1 -2
- package/src/components/documents/button.tsx +9 -16
- package/src/components/documents/icon-box.tsx +1 -1
- package/src/components/documents/tab-button.tsx +2 -2
- package/src/components/documents/tag.tsx +30 -0
- package/src/components/inputs/input-element.tsx +1 -1
- package/src/components/inputs/input-form.tsx +2 -2
- package/src/components/layouts/global-dialogbox.tsx +5 -9
- package/src/components/layouts/global-layout.tsx +8 -0
- package/src/components/layouts/layout-helpers.tsx +1 -4
- package/src/components/layouts/panels/user-panel.tsx +112 -0
- package/src/components/layouts/utility-menu.tsx +9 -29
- package/src/components/panels/theme-panel.tsx +2 -2
- package/src/helpers/browser-helpers.tsx +48 -3
- package/src/helpers/element-helpers.tsx +66 -0
- package/src/interface.ts +1 -0
- package/src/interfaces/auth-token-interfaces.tsx +6 -0
- package/src/interfaces/browser-interfaces.tsx +13 -0
- package/src/interfaces/http-interfaces.tsx +0 -3
- package/src/interfaces/icon-interfaces.tsx +3 -0
- package/src/interfaces/menu-interfaces.tsx +18 -1
- package/src/interfaces/storage-interfaces.tsx +1 -0
- package/src/interfaces/theme-interfaces.tsx +43 -129
- package/src/interfaces/user-interfaces.tsx +30 -0
- package/src/nexus.environments.tsx +39 -27
- package/src/services/http-services.tsx +6 -6
- package/src/services/loader-service.tsx +33 -17
- package/src/services/localstorage-service.tsx +23 -6
- package/src/services/message-services.tsx +1 -1
- package/src/services/theme-service.tsx +32 -16
- package/src/services/user-services.tsx +10 -0
- package/src/sso-config/auth-token-validation.ts +4 -8
- package/src/sso-config/callback-route.tsx +3 -2
- package/src/sso-config/cookie-helpers.tsx +47 -22
- package/src/sso-config/oauth-callback-state.ts +5 -2
- package/src/sso-config/pkce-helpers.ts +2 -9
- package/src/sso-config/provider-complete-route.tsx +7 -4
- package/src/sso-config/redirect-context-actions.tsx +2 -3
- package/src/sso-config/redirect-context-helpers.tsx +20 -5
- package/src/sso-config/refresh-route.tsx +5 -4
- package/src/sso-config/sign-in-route.tsx +9 -4
- package/src/sso-config/sign-out-route.tsx +7 -3
- package/src/sso-config/sign-up-route.tsx +14 -3
- package/src/sso-config/sso-backend-flow.ts +10 -17
- package/src/sso-config/sso-complete-route.tsx +7 -3
- package/src/sso-config/sso-config.ts +7 -5
- package/src/sso-config/sso-interfaces.tsx +34 -3
- package/src/styles/nexus.grid-view.css +42 -0
- package/src/styles/nexus.icon.css +1 -0
- package/src/styles/nexus.logic.css +34 -5
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { EModules } from "./system-interfaces";
|
|
2
|
+
import { UN } from "./type-interfaces";
|
|
2
3
|
|
|
3
4
|
export enum EUserRoles {
|
|
4
5
|
Guest = 0,
|
|
@@ -6,6 +7,12 @@ export enum EUserRoles {
|
|
|
6
7
|
Admin = 2,
|
|
7
8
|
SuperAdmin = 3,
|
|
8
9
|
}
|
|
10
|
+
export const IsAdmin = (userRole: EUserRoles | string | UN) => userRole === EUserRoles.Admin || EUserRoles[EUserRoles.Admin] === userRole;
|
|
11
|
+
export const IsSuperAdmin = (userRole: EUserRoles | string | UN) => userRole === EUserRoles.SuperAdmin || EUserRoles[EUserRoles.SuperAdmin] === userRole;
|
|
12
|
+
export const IsAdmin_SuperAdmin = (userRole: EUserRoles | string | UN) => IsAdmin(userRole) || IsSuperAdmin(userRole);
|
|
13
|
+
export const IsUser = (userRole: EUserRoles | string | UN) => userRole === EUserRoles.User || EUserRoles[EUserRoles.User] === userRole;
|
|
14
|
+
export const IsGuest = (userRole: EUserRoles | string | UN) => userRole === EUserRoles.Guest || EUserRoles[EUserRoles.Guest] === userRole;
|
|
15
|
+
export const IsUser_Guest = (userRole: EUserRoles | string | UN) => IsUser(userRole) || IsGuest(userRole);
|
|
9
16
|
|
|
10
17
|
export interface IUserValidationResponse {
|
|
11
18
|
isValidUser: boolean;
|
|
@@ -15,3 +22,26 @@ export interface IUserValidationResponse {
|
|
|
15
22
|
userRole: EUserRoles;
|
|
16
23
|
assignedModules: EModules;
|
|
17
24
|
}
|
|
25
|
+
export interface ICurrentUser {
|
|
26
|
+
id: string;
|
|
27
|
+
fullname: string;
|
|
28
|
+
username: string;
|
|
29
|
+
roleName: string;
|
|
30
|
+
profilePicture: string | UN;
|
|
31
|
+
emailAddress: string | UN;
|
|
32
|
+
contactNumber: string | UN;
|
|
33
|
+
isImpersonating: boolean | UN;
|
|
34
|
+
isMultiTenant: boolean | UN;
|
|
35
|
+
enableAuditHistory: boolean | UN;
|
|
36
|
+
rootAccess: boolean | UN;
|
|
37
|
+
branchId: string | UN;
|
|
38
|
+
companyId: string | UN;
|
|
39
|
+
fiscalYearId: string | UN;
|
|
40
|
+
modules: EModules | UN;
|
|
41
|
+
}
|
|
42
|
+
export enum ESigninRequestMode {
|
|
43
|
+
DEFAULT = "0",
|
|
44
|
+
SIGN_OUT = "1",
|
|
45
|
+
SWITCH_BRANCH = "2",
|
|
46
|
+
SWITCH_FISCAL_YEAR = "3",
|
|
47
|
+
}
|
|
@@ -1,54 +1,66 @@
|
|
|
1
1
|
import { EModules } from "./interfaces/system-interfaces";
|
|
2
2
|
import { EBackgrounds } from "./interfaces/theme-interfaces";
|
|
3
3
|
|
|
4
|
+
// @ts-ignore
|
|
5
|
+
const processEnv = process.env;
|
|
6
|
+
|
|
4
7
|
export const APIS = {
|
|
5
|
-
DRIVE:
|
|
6
|
-
AUTHENTICATION:
|
|
7
|
-
CONFIGURATION:
|
|
8
|
-
CURRENT:
|
|
9
|
-
ENTERPRISE:
|
|
10
|
-
MESSAGE:
|
|
11
|
-
ACCOUNTING:
|
|
12
|
-
REPORT:
|
|
8
|
+
DRIVE: processEnv.API_DRIVE as string,
|
|
9
|
+
AUTHENTICATION: processEnv.API_AUTHENTICATION as string,
|
|
10
|
+
CONFIGURATION: processEnv.API_CONFIGURATION as string,
|
|
11
|
+
CURRENT: processEnv.API_CURRENT as string,
|
|
12
|
+
ENTERPRISE: processEnv.API_ENTERPRISE as string,
|
|
13
|
+
MESSAGE: processEnv.API_MESSAGE as string,
|
|
14
|
+
ACCOUNTING: processEnv.API_ACCOUNTING as string,
|
|
15
|
+
REPORT: processEnv.API_REPORT as string,
|
|
13
16
|
};
|
|
14
17
|
|
|
15
18
|
export const URLS = {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
19
|
+
GLOBAL_SIGNIN: `${processEnv.URL_AUTHENTICATION}/api/auth/redirect-context`,
|
|
20
|
+
CURRENT: processEnv.URL_CURRENT as string,
|
|
21
|
+
AUTHENTICATION: processEnv.URL_AUTHENTICATION as string,
|
|
22
|
+
SIGN_IN: processEnv.URL_AUTHENTICATION_SIGN_IN as string,
|
|
23
|
+
ALLOWED_ORIGINS: (processEnv.ALLOWED_ORIGINS ?? "")
|
|
20
24
|
.split(",")
|
|
21
|
-
.map((origin) => origin.trim())
|
|
25
|
+
.map((origin: string) => origin.trim())
|
|
22
26
|
.filter(Boolean),
|
|
23
27
|
};
|
|
24
|
-
const FILE = (apiName: string, filePath: string, fileName: string) => `${
|
|
28
|
+
const FILE = (apiName: string, filePath: string, fileName: string) => `${processEnv.API_RESOURCES_BASE_PATH ?? `/api/app/${EModules.Drive}/Resources`}/${apiName}/${encodeURIComponent(filePath)}/${encodeURIComponent(fileName)}`;
|
|
25
29
|
export const FILES = {
|
|
26
30
|
LOGO: {
|
|
27
31
|
ICON: FILE("Image", "logos", "icon-logo"),
|
|
28
32
|
FULL: FILE("Image", "logos", "full-logo"),
|
|
29
33
|
FULL_TEXT: FILE("Image", "logos", "full-text-logo"),
|
|
30
34
|
},
|
|
35
|
+
ICONS: {
|
|
36
|
+
USER: FILE("Image", "icons", "user"),
|
|
37
|
+
NO_IMAGE: FILE("Image", "icons", "no-image"),
|
|
38
|
+
},
|
|
31
39
|
SCRIPT: (fileName: string, isStyles: boolean = false) => FILE("Script", isStyles ? "css" : "js", fileName),
|
|
32
40
|
};
|
|
33
41
|
export const NEXUS_INFO = {
|
|
34
|
-
IS_ADMIN_MODULE:
|
|
35
|
-
CURRENT_MODULE:
|
|
36
|
-
CURRENT_MODULE_NAME:
|
|
37
|
-
CURRENT_MODULE_SHORT_NAME:
|
|
38
|
-
IS_PRODUCTION:
|
|
39
|
-
SOFTWARE_COMPANY_SHORT_NAME:
|
|
40
|
-
SOFTWARE_COMPANY_FULL_NAME:
|
|
41
|
-
SOFTWARE_VERSION:
|
|
42
|
-
SOFTWARE_LAST_UPDATE_DATE:
|
|
43
|
-
SOFTWARE_COMPANY_WEBSITE:
|
|
44
|
-
IS_PROXY_SERVER:
|
|
42
|
+
IS_ADMIN_MODULE: processEnv.IS_ADMIN_MODULE !== "false",
|
|
43
|
+
CURRENT_MODULE: processEnv.CURRENT_MODULE !== undefined ? (parseInt(processEnv.CURRENT_MODULE) as EModules) : undefined,
|
|
44
|
+
CURRENT_MODULE_NAME: processEnv.CURRENT_MODULE_NAME as string,
|
|
45
|
+
CURRENT_MODULE_SHORT_NAME: processEnv.CURRENT_MODULE_SHORT_NAME as string,
|
|
46
|
+
IS_PRODUCTION: processEnv.NODE_ENV === "production",
|
|
47
|
+
SOFTWARE_COMPANY_SHORT_NAME: processEnv.SOFTWARE_COMPANY_SHORT_NAME as string,
|
|
48
|
+
SOFTWARE_COMPANY_FULL_NAME: processEnv.SOFTWARE_COMPANY_FULL_NAME as string,
|
|
49
|
+
SOFTWARE_VERSION: processEnv.SOFTWARE_VERSION as string,
|
|
50
|
+
SOFTWARE_LAST_UPDATE_DATE: processEnv.SOFTWARE_LAST_UPDATE_DATE as string,
|
|
51
|
+
SOFTWARE_COMPANY_WEBSITE: processEnv.SOFTWARE_COMPANY_WEBSITE as string,
|
|
52
|
+
IS_PROXY_SERVER: processEnv.IS_PROXY_SERVER !== "false",
|
|
45
53
|
};
|
|
46
54
|
export const NEXUS_CONFIG = {
|
|
47
55
|
/** Default revalidate time for static generation (in minutes) */
|
|
48
56
|
DEFAULT_REVALIDATE_TIME: NEXUS_INFO.IS_PRODUCTION ? 60 * 24 : 0,
|
|
49
|
-
|
|
57
|
+
DEFAULT_REVALIDATE_TIME_ONE_HOUR: NEXUS_INFO.IS_PRODUCTION ? 60 : 0,
|
|
58
|
+
DEFAULT_REVALIDATE_TIME_ONE_DAY: NEXUS_INFO.IS_PRODUCTION ? 60 * 24 : 0,
|
|
59
|
+
DEFAULT_REVALIDATE_TIME_ONE_WEEK: NEXUS_INFO.IS_PRODUCTION ? 60 * 24 * 7 : 0,
|
|
60
|
+
DEFAULT_REVALIDATE_TIME_ONE_MONTH: NEXUS_INFO.IS_PRODUCTION ? 60 * 24 * 30 : 0,
|
|
61
|
+
DEFAULT_REVALIDATE_TIME_ONE_YEAR: NEXUS_INFO.IS_PRODUCTION ? 60 * 24 * 365 : 0,
|
|
50
62
|
/** Default timeout for HTTP requests (in milliseconds) */
|
|
51
63
|
DEFAULT_HTTP_REQUEST_TIMEOUT: NEXUS_INFO.IS_PRODUCTION ? 60000 : 1000000,
|
|
52
64
|
|
|
53
|
-
DEFAULT_BACKGROUND: (
|
|
65
|
+
DEFAULT_BACKGROUND: (processEnv.DEFAULT_BACKGROUND as EBackgrounds) ?? EBackgrounds.Background1,
|
|
54
66
|
};
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* For browser/client components use `http-client-services` (`Post`, `Get`, …) via `nexus-shared/client`.
|
|
4
4
|
*/
|
|
5
5
|
import { Delay } from "../helpers/utility-helpers";
|
|
6
|
-
import { EActions, EBody, EModules, GetMethod, IErrorRespose, ISuccessResponse, UN } from "../interface";
|
|
6
|
+
import { EActions, EBody, EModules, GetMethod, GetResponse, IErrorRespose, ISuccessResponse, UN } from "../interface";
|
|
7
7
|
import { EErrorCodes, EExceptionCategories } from "../interfaces/exception-interfaces";
|
|
8
8
|
import { APIS, NEXUS_CONFIG, NEXUS_INFO } from "../nexus.environments";
|
|
9
9
|
|
|
@@ -15,7 +15,7 @@ export const BuildApiHeader = (formdata?: any, httpMethod?: EActions | string, b
|
|
|
15
15
|
bodyType = bodyType ?? EBody.JSON;
|
|
16
16
|
|
|
17
17
|
const headers = new Headers();
|
|
18
|
-
if (token && token.length > 50) headers.append("authorization",
|
|
18
|
+
if (token && token.length > 50) headers.append("authorization", token.startsWith("Bearer ") ? token : `Bearer ${token}`);
|
|
19
19
|
if (browserId) headers.append("bid", browserId);
|
|
20
20
|
headers.append("Content-Type", bodyType);
|
|
21
21
|
|
|
@@ -115,8 +115,9 @@ const handleResponse = async (response: Response): Promise<IErrorRespose> => {
|
|
|
115
115
|
const status = response.status;
|
|
116
116
|
try {
|
|
117
117
|
const errorData = await response.json();
|
|
118
|
-
|
|
119
|
-
|
|
118
|
+
|
|
119
|
+
return handleError({ ...errorData, code: status, status: status });
|
|
120
|
+
} catch (error: any) {
|
|
120
121
|
let message = "Unexpected server error.";
|
|
121
122
|
switch (status) {
|
|
122
123
|
case 401:
|
|
@@ -131,7 +132,7 @@ const handleResponse = async (response: Response): Promise<IErrorRespose> => {
|
|
|
131
132
|
message = "Method not allowed.";
|
|
132
133
|
break;
|
|
133
134
|
}
|
|
134
|
-
return handleError(
|
|
135
|
+
return handleError(GetResponse.ERROR(message, "GENERIC_ERROR", EExceptionCategories.Application, EErrorCodes.Unknown, undefined, undefined, status));
|
|
135
136
|
}
|
|
136
137
|
};
|
|
137
138
|
export const handleError = (error: any): IErrorRespose => {
|
|
@@ -148,7 +149,6 @@ export const handleError = (error: any): IErrorRespose => {
|
|
|
148
149
|
};
|
|
149
150
|
};
|
|
150
151
|
const handleNetworkError = (error: any): IErrorRespose => {
|
|
151
|
-
console.error("\n\nNetwork error encountered:", error);
|
|
152
152
|
return handleError({
|
|
153
153
|
status: 503,
|
|
154
154
|
message: "Unable to connect to the server. Please check your network or backend status.",
|
|
@@ -36,17 +36,17 @@ const _findExistingLoader = (parent: HTMLElement): HTMLElement | null => {
|
|
|
36
36
|
const loaderElement = FindChildElement(parent, `#${LOADER_IDENTITY}`);
|
|
37
37
|
return loaderElement as HTMLElement | null;
|
|
38
38
|
};
|
|
39
|
-
const _updateLoaderProperties = (loaderElement: HTMLElement, type: EAnimationClasses, size: ESizes) => {
|
|
39
|
+
const _updateLoaderProperties = (loaderElement: HTMLElement, type: EAnimationClasses, size: ESizes, isInputLoader: boolean) => {
|
|
40
40
|
if (type === EAnimationClasses.SPIN) {
|
|
41
41
|
if (size === ESizes.VeryTiny) {
|
|
42
|
-
loaderElement.style.setProperty("--SPIN_BOX_SIZE", "2.5rem");
|
|
43
|
-
loaderElement.style.setProperty("--SPIN_BOX_BORDER_SIZE", "0.34rem");
|
|
42
|
+
loaderElement.style.setProperty("--SPIN_BOX_SIZE", isInputLoader ? "1.5rem" : "2.5rem");
|
|
43
|
+
loaderElement.style.setProperty("--SPIN_BOX_BORDER_SIZE", isInputLoader ? "0.28rem" : "0.34rem");
|
|
44
44
|
} else if (size === ESizes.Tiny) {
|
|
45
|
-
loaderElement.style.setProperty("--SPIN_BOX_SIZE", "2.8rem");
|
|
46
|
-
loaderElement.style.setProperty("--SPIN_BOX_BORDER_SIZE", "0.36rem");
|
|
45
|
+
loaderElement.style.setProperty("--SPIN_BOX_SIZE", isInputLoader ? "2.0rem" : "2.8rem");
|
|
46
|
+
loaderElement.style.setProperty("--SPIN_BOX_BORDER_SIZE", isInputLoader ? "0.31rem" : "0.36rem");
|
|
47
47
|
} else if (size === ESizes.VerySmall) {
|
|
48
|
-
loaderElement.style.setProperty("--SPIN_BOX_SIZE", "3rem");
|
|
49
|
-
loaderElement.style.setProperty("--SPIN_BOX_BORDER_SIZE", "0.38rem");
|
|
48
|
+
loaderElement.style.setProperty("--SPIN_BOX_SIZE", isInputLoader ? "2.4rem" : "3rem");
|
|
49
|
+
loaderElement.style.setProperty("--SPIN_BOX_BORDER_SIZE", isInputLoader ? "0.34rem" : "0.38rem");
|
|
50
50
|
} else if (size === ESizes.Small) {
|
|
51
51
|
loaderElement.style.setProperty("--SPIN_BOX_SIZE", "3.5rem");
|
|
52
52
|
loaderElement.style.setProperty("--SPIN_BOX_BORDER_SIZE", "0.45rem");
|
|
@@ -68,26 +68,30 @@ const _updateLoaderProperties = (loaderElement: HTMLElement, type: EAnimationCla
|
|
|
68
68
|
}
|
|
69
69
|
}
|
|
70
70
|
};
|
|
71
|
-
const _updateLoaderAttributes = (loaderElement: HTMLElement, type: EAnimationClasses, size: ESizes, disabledInputs: boolean, count: number) => {
|
|
71
|
+
const _updateLoaderAttributes = (loaderElement: HTMLElement, type: EAnimationClasses, size: ESizes, disabledInputs: boolean, count: number, isInputLoader: boolean) => {
|
|
72
72
|
loaderElement.setAttribute(LOADER_DATA_COUNT_ATTRIBUTE, count.toString());
|
|
73
73
|
loaderElement.setAttribute(LOADER_DATA_TYPE_ATTRIBUTE, type);
|
|
74
74
|
loaderElement.setAttribute(LOADER_DATA_SIZE_ATTRIBUTE, size);
|
|
75
75
|
loaderElement.setAttribute(LOADER_DATA_DISABLED_INPUTS_ATTRIBUTE, disabledInputs ? "true" : "false");
|
|
76
76
|
};
|
|
77
|
-
const _createLoaderElement = (parent: HTMLElement, type: EAnimationClasses, size: ESizes, showLogo: boolean, disabledInputs: boolean, background: EBackgrounds, defaultClass?: string | UN): HTMLElement | null => {
|
|
77
|
+
const _createLoaderElement = (parent: HTMLElement, type: EAnimationClasses, size: ESizes, showLogo: boolean, disabledInputs: boolean, background: EBackgrounds, defaultClass?: string | UN, isInputLoader?: boolean): HTMLElement | null => {
|
|
78
78
|
const loaderElement = _findExistingLoader(parent);
|
|
79
79
|
if (loaderElement && !loaderElement.classList.contains(DISPOSED_LOADER_CLASS)) {
|
|
80
80
|
const count = parseInt(loaderElement.getAttribute(LOADER_DATA_COUNT_ATTRIBUTE) ?? "0") + 1;
|
|
81
|
-
_updateLoaderAttributes(loaderElement, type, size, disabledInputs, count);
|
|
82
|
-
_updateLoaderProperties(loaderElement, type, size);
|
|
81
|
+
_updateLoaderAttributes(loaderElement, type, size, disabledInputs, count, isInputLoader ?? false);
|
|
82
|
+
_updateLoaderProperties(loaderElement, type, size, isInputLoader ?? false);
|
|
83
83
|
return loaderElement as HTMLElement;
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
const newLoaderElement = document.createElement("div");
|
|
87
87
|
newLoaderElement.id = LOADER_IDENTITY;
|
|
88
88
|
newLoaderElement.classList.add("nexus-container", "o0", EAnimationClasses.FADE_IN, background);
|
|
89
|
-
if (defaultClass) newLoaderElement.classList.add(defaultClass);
|
|
90
|
-
_updateLoaderAttributes(newLoaderElement, type, size, disabledInputs, 1);
|
|
89
|
+
if (defaultClass) newLoaderElement.classList.add(...defaultClass.split(" "));
|
|
90
|
+
_updateLoaderAttributes(newLoaderElement, type, size, disabledInputs, 1, isInputLoader ?? false);
|
|
91
|
+
_updateLoaderProperties(newLoaderElement, type, size, isInputLoader ?? false);
|
|
92
|
+
|
|
93
|
+
newLoaderElement.style.maxHeight = "100%!important";
|
|
94
|
+
newLoaderElement.style.maxWidth = "100%!important";
|
|
91
95
|
|
|
92
96
|
const loader = document.createElement("div");
|
|
93
97
|
newLoaderElement.appendChild(loader);
|
|
@@ -99,8 +103,6 @@ const _createLoaderElement = (parent: HTMLElement, type: EAnimationClasses, size
|
|
|
99
103
|
loader.classList.add("tx52");
|
|
100
104
|
loader.style.fontSize = "1.5rem";
|
|
101
105
|
} else if (showLogo && type !== EAnimationClasses.SLIDE_BAR && type !== EAnimationClasses.BOUNCE_BAR) {
|
|
102
|
-
_updateLoaderProperties(newLoaderElement, type, size);
|
|
103
|
-
|
|
104
106
|
const logoElement = document.createElement("img");
|
|
105
107
|
logoElement.src = FILES.LOGO.ICON;
|
|
106
108
|
logoElement.alt = "Logo";
|
|
@@ -124,7 +126,7 @@ const _createLoaderElement = (parent: HTMLElement, type: EAnimationClasses, size
|
|
|
124
126
|
return newLoaderElement;
|
|
125
127
|
};
|
|
126
128
|
|
|
127
|
-
export const ShowLoader = async (parent?: String | HTMLElement | UN, type?: EAnimationClasses | UN, disabledInputs?: boolean | UN, size?: ESizes | UN, delay?: number | UN, showLogo?: boolean | UN, background?: EBackgrounds | UN, defaultClass?: string | UN): Promise<HTMLElement | null> => {
|
|
129
|
+
export const ShowLoader = async (parent?: String | HTMLElement | UN, type?: EAnimationClasses | UN, disabledInputs?: boolean | UN, size?: ESizes | UN, delay?: number | UN, showLogo?: boolean | UN, background?: EBackgrounds | UN, defaultClass?: string | UN, isInputLoader?: boolean | UN): Promise<HTMLElement | null> => {
|
|
128
130
|
if (!IsBrowser()) return Promise.resolve(null);
|
|
129
131
|
type = type ?? DEFAULT_LOADER_TYPE;
|
|
130
132
|
size = size ?? DEFAULT_LOADER_SIZE;
|
|
@@ -134,7 +136,7 @@ export const ShowLoader = async (parent?: String | HTMLElement | UN, type?: EAni
|
|
|
134
136
|
const timeout = setTimeout(() => {
|
|
135
137
|
clearTimeout(timeout);
|
|
136
138
|
const parentElement = _findParentElement(parent);
|
|
137
|
-
const loaderElement = _createLoaderElement(parentElement, type, size, showLogo ?? true, disabledInputs ?? false, background, defaultClass);
|
|
139
|
+
const loaderElement = _createLoaderElement(parentElement, type, size, showLogo ?? true, disabledInputs ?? false, background, defaultClass, isInputLoader ?? false);
|
|
138
140
|
resolve(loaderElement);
|
|
139
141
|
}, delay ?? DEFAULT_LOADER_DELAY);
|
|
140
142
|
});
|
|
@@ -167,3 +169,17 @@ export const HideLoader = (parent?: String | HTMLElement | UN, force?: boolean |
|
|
|
167
169
|
}, delay ?? DEFAULT_LOADER_DELAY);
|
|
168
170
|
});
|
|
169
171
|
};
|
|
172
|
+
|
|
173
|
+
export const InputLoader = async (parent: String | HTMLElement, isShow: boolean, defaultClass?: string | UN): Promise<HTMLElement | null> => {
|
|
174
|
+
if (isShow !== false) {
|
|
175
|
+
const loaderElement = await ShowLoader(parent, EAnimationClasses.SPIN, false, ESizes.VeryTiny, null, false, EBackgrounds.None, defaultClass, true);
|
|
176
|
+
if (loaderElement) {
|
|
177
|
+
loaderElement.style.width = "100%";
|
|
178
|
+
loaderElement.style.height = "100%";
|
|
179
|
+
}
|
|
180
|
+
return loaderElement;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
HideLoader(parent);
|
|
184
|
+
return null;
|
|
185
|
+
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { CreateUpdatePreference } from "../api-services/preference-service";
|
|
2
2
|
import { IsBrowser } from "../helpers/browser-helpers";
|
|
3
3
|
import { Debounce } from "../helpers/utility-helpers";
|
|
4
|
-
import { EFlexPositions } from "../interface";
|
|
4
|
+
import { EFlexPositions, UN } from "../interface";
|
|
5
5
|
import { LOCAL_STORAGE_KEYS } from "../interfaces/storage-interfaces";
|
|
6
6
|
import { EPreferenceTargets } from "../interfaces/system-interfaces";
|
|
7
7
|
import { IThemeInformation } from "../interfaces/theme-interfaces";
|
|
@@ -24,10 +24,10 @@ const _createUpdatePreferenceDebounce = Debounce(CreateUpdatePreference, 1000);
|
|
|
24
24
|
* @param isSyncWithServer Whether to sync with the server
|
|
25
25
|
* @param target The target of the local storage
|
|
26
26
|
*/
|
|
27
|
-
const _syncStorageToServer = async (key: string, value: string |
|
|
27
|
+
const _syncStorageToServer = async (key: string, value: string | UN, isSyncWithServer?: boolean | UN, target?: EPreferenceTargets | UN) => {
|
|
28
28
|
if (!IsBrowser() || !isSyncWithServer || !target) return;
|
|
29
29
|
|
|
30
|
-
await _createUpdatePreferenceDebounce(target, key, value);
|
|
30
|
+
await _createUpdatePreferenceDebounce(target, key, value ?? null);
|
|
31
31
|
};
|
|
32
32
|
|
|
33
33
|
/**
|
|
@@ -38,11 +38,28 @@ const _syncStorageToServer = async (key: string, value: string | null, isSyncWit
|
|
|
38
38
|
* @param isSyncWithServer Whether to sync with the server
|
|
39
39
|
* @param target The target of the local storage
|
|
40
40
|
*/
|
|
41
|
-
export const SetLocalStorage = (key: string, value: string, isSyncWithServer?: boolean, target?: EPreferenceTargets) => {
|
|
41
|
+
export const SetLocalStorage = (key: string, value: string | UN, isSyncWithServer?: boolean | UN, target?: EPreferenceTargets | UN) => {
|
|
42
42
|
if (!IsBrowser()) return;
|
|
43
43
|
|
|
44
44
|
_syncStorageToServer(key, value, isSyncWithServer, target);
|
|
45
|
-
|
|
45
|
+
|
|
46
|
+
if (value === null || value === undefined) RemoveLocalStorage(key, isSyncWithServer, target);
|
|
47
|
+
else localStorage.setItem(key, value);
|
|
48
|
+
};
|
|
49
|
+
export const SetSessionStorage = (key: string, value: string | UN) => {
|
|
50
|
+
if (!IsBrowser()) return;
|
|
51
|
+
if (value === null || value === undefined) RemoveSessionStorage(key);
|
|
52
|
+
else sessionStorage.setItem(key, value);
|
|
53
|
+
};
|
|
54
|
+
export const GetSessionStorage = (key: string): string | null => {
|
|
55
|
+
if (!IsBrowser()) return null;
|
|
56
|
+
const data = sessionStorage.getItem(key);
|
|
57
|
+
if (!data) return null;
|
|
58
|
+
return data;
|
|
59
|
+
};
|
|
60
|
+
export const RemoveSessionStorage = (key: string) => {
|
|
61
|
+
if (!IsBrowser()) return;
|
|
62
|
+
sessionStorage.removeItem(key);
|
|
46
63
|
};
|
|
47
64
|
/**
|
|
48
65
|
* Remove the value of the local storage
|
|
@@ -51,7 +68,7 @@ export const SetLocalStorage = (key: string, value: string, isSyncWithServer?: b
|
|
|
51
68
|
* @param isSyncWithServer Whether to sync with the server
|
|
52
69
|
* @param target The target of the local storage
|
|
53
70
|
*/
|
|
54
|
-
export const RemoveLocalStorage = (key: string, isSyncWithServer?: boolean, target?: EPreferenceTargets) => {
|
|
71
|
+
export const RemoveLocalStorage = (key: string, isSyncWithServer?: boolean | UN, target?: EPreferenceTargets | UN) => {
|
|
55
72
|
_syncStorageToServer(key, null, isSyncWithServer, target);
|
|
56
73
|
localStorage.removeItem(key);
|
|
57
74
|
};
|
|
@@ -157,7 +157,7 @@ const _showMessage = (title: string, message: string, messageType: EMessageTypes
|
|
|
157
157
|
messageContentElement.appendChild(messageElement);
|
|
158
158
|
|
|
159
159
|
const dismiss = () => dismissMessage(messageRootElement, callback);
|
|
160
|
-
CreateIconBox(EIcons.CLOSE, titleElement, null, null, EIconTypes.
|
|
160
|
+
CreateIconBox(EIcons.CLOSE, titleElement, null, null, EIconTypes.OUTLINED, EBackgrounds.Background1, ESizes.VeryTiny, ESizes.Tiny, null, null, null, null, EOpacity.O05, "Clear Message", null, null, null, dismiss);
|
|
161
161
|
|
|
162
162
|
activeMessages.unshift(messageRootElement);
|
|
163
163
|
scheduleReflow();
|
|
@@ -1,10 +1,21 @@
|
|
|
1
1
|
import { DEFAULT_SMALL_ICON_BOX_SIZE } from "../interfaces/icon-interfaces";
|
|
2
2
|
import { DEFAULT_FONT_WEIGHTS } from "../interfaces/layout-interfaces";
|
|
3
3
|
import { O } from "../nexus-client";
|
|
4
|
-
import {
|
|
4
|
+
import { EBackgroundColors, EColorTypes, ETextColors, IThemeInformation } from "./../interfaces/theme-interfaces";
|
|
5
5
|
import { GetCurrentTheme, SetCurrentTheme } from "./localstorage-service";
|
|
6
6
|
|
|
7
7
|
const I = "!important;";
|
|
8
|
+
export const ICON_BOX_SCALES = {
|
|
9
|
+
xxs: 1 / 1.7,
|
|
10
|
+
xs: 1 / 1.5,
|
|
11
|
+
sm: DEFAULT_SMALL_ICON_BOX_SIZE,
|
|
12
|
+
ssm: 1 / 1.35,
|
|
13
|
+
lg: 1.25,
|
|
14
|
+
med: 1.1,
|
|
15
|
+
xl: 1.5,
|
|
16
|
+
xxl: 1.8,
|
|
17
|
+
};
|
|
18
|
+
export const THEME_KEYS = ["c1", "c2", "c3", "c10", "c11", "c12", "c13", "c20", "c30", "c41", "c51", "c52", "c100", "c200", "c400", "c600", "c800", "c950"];
|
|
8
19
|
|
|
9
20
|
const _setVariable = (key: string, value: string | null) => {
|
|
10
21
|
if (value) document.documentElement.style.setProperty(key, value);
|
|
@@ -22,13 +33,12 @@ const _initializeTheme = (theme: IThemeInformation): boolean => {
|
|
|
22
33
|
if (!theme) return false;
|
|
23
34
|
SetCurrentTheme(theme.systemName);
|
|
24
35
|
|
|
25
|
-
const THEME_KEYS = Object.keys(theme.themeColors);
|
|
26
36
|
for (const key of THEME_KEYS) {
|
|
27
|
-
const color = theme
|
|
37
|
+
const color = theme[key as keyof IThemeInformation] as string;
|
|
28
38
|
const [background, hover, focus, text] = color.split("|");
|
|
29
39
|
|
|
30
40
|
if (!background) continue;
|
|
31
|
-
const keyNumber = key.replace("
|
|
41
|
+
const keyNumber = key.replace("c", "");
|
|
32
42
|
|
|
33
43
|
if (background !== "NULL") _setVariable(`--B${keyNumber}`, background);
|
|
34
44
|
if (hover !== "NULL") _setVariable(`--H${keyNumber}`, hover);
|
|
@@ -96,9 +106,8 @@ export const InitializeStyles = () => {
|
|
|
96
106
|
_setRule(sheet, `.bat${s}`, `border: var(--BRD${s}) solid transparent`);
|
|
97
107
|
});
|
|
98
108
|
|
|
99
|
-
const THEME_KEYS = Object.keys(themeInformations[0].themeColors);
|
|
100
109
|
for (const key of THEME_KEYS) {
|
|
101
|
-
const keyNumber = key.replace("
|
|
110
|
+
const keyNumber = key.replace("c", "");
|
|
102
111
|
_setRule(sheet, `.bg${keyNumber}`, `background-color: var(--B${keyNumber}) ${I}; color: var(--T${keyNumber});`);
|
|
103
112
|
_setRule(sheet, `.h.bg${keyNumber}:hover`, `background-color: var(--H${keyNumber}) ${I}`);
|
|
104
113
|
_setRule(sheet, `.h.bg${keyNumber}:focus`, `background-color: var(--F${keyNumber}) ${I}`);
|
|
@@ -118,16 +127,6 @@ export const InitializeStyles = () => {
|
|
|
118
127
|
fs: 2.85,
|
|
119
128
|
};
|
|
120
129
|
|
|
121
|
-
const ICON_BOX_SCALES = {
|
|
122
|
-
xxs: 1 / 1.7,
|
|
123
|
-
xs: 1 / 1.5,
|
|
124
|
-
sm: DEFAULT_SMALL_ICON_BOX_SIZE,
|
|
125
|
-
ssm: 1 / 1.35,
|
|
126
|
-
lg: 1.25,
|
|
127
|
-
med: 1.1,
|
|
128
|
-
xl: 1.5,
|
|
129
|
-
xxl: 1.8,
|
|
130
|
-
};
|
|
131
130
|
Object.entries(ICON_SIZES).forEach(([k, v]) => _setRule(sheet, `.icon.${k}`, `font-size: ${v}rem;`));
|
|
132
131
|
|
|
133
132
|
Object.entries(ICON_BOX_SCALES).forEach(([k, s]) => {
|
|
@@ -135,6 +134,8 @@ export const InitializeStyles = () => {
|
|
|
135
134
|
const css = `min-width: ${size} ${I}; min-height: ${size} ${I}; max-width: ${size} ${I}; max-height: ${size} ${I};`;
|
|
136
135
|
_setRule(sheet, `.ib.${k}`, css);
|
|
137
136
|
});
|
|
137
|
+
|
|
138
|
+
_setVariable(`--TOP_HEIGHT`, `calc(var(--IH) * ${ICON_BOX_SCALES.sm})`);
|
|
138
139
|
};
|
|
139
140
|
export const SwitchTheme = (themeCode: string): boolean => {
|
|
140
141
|
const themeInformations = O.systemThemes;
|
|
@@ -145,3 +146,18 @@ export const SwitchTheme = (themeCode: string): boolean => {
|
|
|
145
146
|
SetCurrentTheme(theme.systemName);
|
|
146
147
|
return _initializeTheme(theme);
|
|
147
148
|
};
|
|
149
|
+
export const GetColorCode = (colorCode: string | EBackgroundColors | ETextColors): number | null => {
|
|
150
|
+
if (colorCode) {
|
|
151
|
+
const match = colorCode.match(/\d+/);
|
|
152
|
+
return match ? parseInt(match[0], 10) : 0;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return null;
|
|
156
|
+
};
|
|
157
|
+
export const GetDynamicColor = (colorCode: string | EBackgroundColors | ETextColors, colorType: EColorTypes): string | null => {
|
|
158
|
+
if (colorCode) {
|
|
159
|
+
const code = GetColorCode(colorCode);
|
|
160
|
+
if (code) return `var(${colorType}${code})`;
|
|
161
|
+
}
|
|
162
|
+
return null;
|
|
163
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { EUserRoles, ICurrentUser, UN } from "../interface";
|
|
2
|
+
|
|
3
|
+
let CURRENT_USER: ICurrentUser | null = null;
|
|
4
|
+
export const SetCurrentUser = (user: ICurrentUser | UN) => (CURRENT_USER = user ?? null);
|
|
5
|
+
export const GetCurrentUser = (): ICurrentUser | null => CURRENT_USER;
|
|
6
|
+
export const GetCurrentUserRole = (): EUserRoles | null => {
|
|
7
|
+
const roleName = CURRENT_USER?.roleName;
|
|
8
|
+
if (!roleName) return null;
|
|
9
|
+
return EUserRoles[roleName as keyof typeof EUserRoles];
|
|
10
|
+
};
|
|
@@ -9,12 +9,8 @@ import { SSO_CONFIG } from "./sso-config";
|
|
|
9
9
|
*/
|
|
10
10
|
export async function VerifyAccessToken(token: string): Promise<boolean> {
|
|
11
11
|
if (!token?.trim()) return false;
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
return validationResponse.isValidUser;
|
|
17
|
-
} catch {
|
|
18
|
-
return false;
|
|
19
|
-
}
|
|
12
|
+
const response = await HttpGet(SSO_CONFIG.BACKEND_ENDPOINTS.VERIFY, token, 0);
|
|
13
|
+
const validationResponse = response.result as IUserValidationResponse;
|
|
14
|
+
if (!response.isSuccess) return false;
|
|
15
|
+
return validationResponse.isValidUser;
|
|
20
16
|
}
|
|
@@ -2,9 +2,9 @@ import { NextRequest, NextResponse } from "next/server";
|
|
|
2
2
|
import { URLS } from "../nexus.environments";
|
|
3
3
|
import { SetAuthCookies } from "./cookie-helpers";
|
|
4
4
|
import { DecodeOAuthCallbackState } from "./oauth-callback-state";
|
|
5
|
-
import { SanitizeReturnPath } from "./redirect-context-helpers";
|
|
6
|
-
import { BuildAuthenticationSignInUrl, SSO_CONFIG } from "./sso-config";
|
|
5
|
+
import { ClearRedirectContextCookie, SanitizeReturnPath } from "./redirect-context-helpers";
|
|
7
6
|
import { ExchangeAuthorizationCode } from "./sso-backend-flow";
|
|
7
|
+
import { BuildAuthenticationSignInUrl, SSO_CONFIG } from "./sso-config";
|
|
8
8
|
|
|
9
9
|
export async function CallbackRoute(req: NextRequest) {
|
|
10
10
|
const code = req.nextUrl.searchParams.get("code");
|
|
@@ -27,6 +27,7 @@ export async function CallbackRoute(req: NextRequest) {
|
|
|
27
27
|
const redirectTarget = new URL(SanitizeReturnPath(tokenData.returnPath ?? callbackState.returnPath), URLS.CURRENT!);
|
|
28
28
|
const response = NextResponse.redirect(redirectTarget);
|
|
29
29
|
SetAuthCookies(response, tokenData);
|
|
30
|
+
ClearRedirectContextCookie(response);
|
|
30
31
|
return response;
|
|
31
32
|
} catch {
|
|
32
33
|
return NextResponse.redirect(new URL(BuildAuthenticationSignInUrl(callbackState?.returnPath)));
|
|
@@ -3,6 +3,7 @@ import { NextRequest, NextResponse } from "next/server";
|
|
|
3
3
|
import { VerifyAccessToken } from "./auth-token-validation";
|
|
4
4
|
import { DecryptCookieValue, EncryptCookieValue } from "./cookie-encryption";
|
|
5
5
|
import { GetAuthCookieOptions } from "./redirect-context-helpers";
|
|
6
|
+
import { SSO_CONFIG } from "./sso-config";
|
|
6
7
|
import { CookieInfo, SSOAuthResponse } from "./sso-interfaces";
|
|
7
8
|
|
|
8
9
|
export const AUTH_COOKIES = {
|
|
@@ -24,11 +25,7 @@ export function SetSsoSessionCookie(response: NextResponse, ssoSession: string,
|
|
|
24
25
|
export function ClearAuthCookies(response: NextResponse) {
|
|
25
26
|
response.cookies.set(AUTH_COOKIES.accessToken, "", GetAuthCookieOptions(0));
|
|
26
27
|
response.cookies.set(AUTH_COOKIES.refreshToken, "", GetAuthCookieOptions(0));
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export function ClearAllAuthCookies(response: NextResponse) {
|
|
30
28
|
response.cookies.set(AUTH_COOKIES.ssoSession, "", GetAuthCookieOptions(0));
|
|
31
|
-
ClearAuthCookies(response);
|
|
32
29
|
}
|
|
33
30
|
|
|
34
31
|
/**
|
|
@@ -44,34 +41,62 @@ export function GetDecryptedRequestCookie(req: NextRequest, name: string): strin
|
|
|
44
41
|
export async function GetCookieInfo(): Promise<CookieInfo | null> {
|
|
45
42
|
const cookieStore = await cookies();
|
|
46
43
|
|
|
44
|
+
const rawRefreshToken = cookieStore.get(AUTH_COOKIES.refreshToken)?.value;
|
|
45
|
+
const refreshToken = rawRefreshToken ? (DecryptCookieValue(rawRefreshToken) ?? undefined) : undefined;
|
|
46
|
+
|
|
47
47
|
const rawSsoSession = cookieStore.get(AUTH_COOKIES.ssoSession)?.value;
|
|
48
|
-
|
|
49
|
-
const ssoSession = DecryptCookieValue(rawSsoSession);
|
|
50
|
-
if (!ssoSession) return null;
|
|
48
|
+
const ssoSession = rawSsoSession ? (DecryptCookieValue(rawSsoSession) ?? undefined) : undefined;
|
|
51
49
|
|
|
52
50
|
const rawAccessToken = cookieStore.get(AUTH_COOKIES.accessToken)?.value;
|
|
53
|
-
const
|
|
51
|
+
const accessToken = rawAccessToken ? (DecryptCookieValue(rawAccessToken) ?? undefined) : undefined;
|
|
52
|
+
|
|
53
|
+
if (!refreshToken && !ssoSession) return null;
|
|
54
54
|
|
|
55
55
|
return {
|
|
56
56
|
ssoSession,
|
|
57
|
-
accessToken
|
|
58
|
-
refreshToken
|
|
57
|
+
accessToken,
|
|
58
|
+
refreshToken,
|
|
59
59
|
};
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
-
/**
|
|
63
|
-
export
|
|
64
|
-
|
|
65
|
-
const raw = cookieStore.get(AUTH_COOKIES.accessToken)?.value;
|
|
66
|
-
if (!raw) return false;
|
|
67
|
-
const token = DecryptCookieValue(raw);
|
|
68
|
-
if (!token) return false;
|
|
69
|
-
return VerifyAccessToken(token);
|
|
62
|
+
/** Where to send the browser after credentials or provider sign-in succeeds. */
|
|
63
|
+
export function ResolvePostLoginRedirectUrl(hasRedirectContext: boolean): string {
|
|
64
|
+
return hasRedirectContext ? SSO_CONFIG.FRONTEND_ENDPOINTS.SSO_COMPLETE : SSO_CONFIG.FRONTEND_ENDPOINTS.SIGN_IN;
|
|
70
65
|
}
|
|
71
66
|
|
|
72
|
-
/**
|
|
73
|
-
|
|
67
|
+
/**
|
|
68
|
+
* When the user already has auth cookies, returns the next URL (or null to show sign-in).
|
|
69
|
+
* - refresh token → home or SSO complete for an external client
|
|
70
|
+
* - sso session only → token exchange (GET sign-in) or SSO complete
|
|
71
|
+
*/
|
|
72
|
+
export async function ResolveAuthenticatedRedirect(redirectContextUri?: string | null): Promise<string | null> {
|
|
73
|
+
const cookieInfo = await GetCookieInfo();
|
|
74
|
+
if (!cookieInfo) return null;
|
|
75
|
+
|
|
76
|
+
if (cookieInfo.refreshToken) {
|
|
77
|
+
return redirectContextUri ? SSO_CONFIG.FRONTEND_ENDPOINTS.SSO_COMPLETE : "/home";
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (cookieInfo.ssoSession) {
|
|
81
|
+
return redirectContextUri ? SSO_CONFIG.FRONTEND_ENDPOINTS.SSO_COMPLETE : SSO_CONFIG.FRONTEND_ENDPOINTS.SIGN_IN;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** True when this client app has a valid access token (use on all non-auth client sites). */
|
|
88
|
+
export async function HasAccessToken(backendValidation: boolean = false): Promise<boolean> {
|
|
74
89
|
const cookieStore = await cookies();
|
|
75
|
-
const
|
|
76
|
-
|
|
90
|
+
const rawRefreshToken = cookieStore.get(AUTH_COOKIES.refreshToken)?.value;
|
|
91
|
+
const refreshToken = rawRefreshToken ? DecryptCookieValue(rawRefreshToken) : null;
|
|
92
|
+
if (!refreshToken) return false;
|
|
93
|
+
|
|
94
|
+
const rawAccessToken = cookieStore.get(AUTH_COOKIES.accessToken)?.value;
|
|
95
|
+
const accessToken = rawAccessToken ? DecryptCookieValue(rawAccessToken) : null;
|
|
96
|
+
|
|
97
|
+
if (!accessToken && !backendValidation) return true;
|
|
98
|
+
|
|
99
|
+
if (!accessToken) return false;
|
|
100
|
+
|
|
101
|
+
return VerifyAccessToken(accessToken);
|
|
77
102
|
}
|
|
@@ -8,11 +8,14 @@ type EncodedCallbackState = {
|
|
|
8
8
|
t: number;
|
|
9
9
|
};
|
|
10
10
|
|
|
11
|
+
// @ts-ignore
|
|
12
|
+
const processEnv = process.env;
|
|
13
|
+
|
|
11
14
|
function getCookieSecret(): string {
|
|
12
|
-
const ssoSecret =
|
|
15
|
+
const ssoSecret = processEnv.SSO_COOKIE_SECRET?.trim();
|
|
13
16
|
const secret = ssoSecret && !ssoSecret.startsWith("$") ? ssoSecret : undefined;
|
|
14
17
|
if (secret) return secret;
|
|
15
|
-
if (
|
|
18
|
+
if (processEnv.NODE_ENV === "development") return "NexusDevAuthSecretKey2026MustBe32CharsLong!";
|
|
16
19
|
throw new Error("SSO_COOKIE_SECRET must be configured.");
|
|
17
20
|
}
|
|
18
21
|
|
|
@@ -1,12 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
export const PKCE_METHOD_S256 = "S256";
|
|
4
|
-
|
|
5
|
-
export type PkcePair = {
|
|
6
|
-
verifier: string;
|
|
7
|
-
challenge: string;
|
|
8
|
-
method: typeof PKCE_METHOD_S256;
|
|
9
|
-
};
|
|
1
|
+
import { createHash, randomBytes } from "crypto";
|
|
2
|
+
import { PKCE_METHOD_S256, PkcePair } from "./sso-interfaces";
|
|
10
3
|
|
|
11
4
|
/** Creates an OAuth PKCE pair (RFC 7636) using S256. */
|
|
12
5
|
export function CreatePkcePair(): PkcePair {
|