nexus-shared 1.1.18 → 1.1.19
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/client.ts +2 -1
- package/src/components/documents/button.tsx +5 -1
- package/src/components/inputs/input-box.tsx +4 -2
- package/src/components/inputs/input-form.tsx +126 -14
- package/src/helpers/browser-helpers.tsx +41 -1
- package/src/helpers/input-helpers.tsx +34 -4
- package/src/helpers/validation-helpers.tsx +302 -0
- package/src/index.ts +2 -18
- package/src/interface.ts +4 -1
- package/src/interfaces/button-interfaces.tsx +2 -2
- package/src/interfaces/http-interfaces.tsx +89 -8
- package/src/interfaces/input-interfaces.tsx +61 -11
- package/src/interfaces/message-interfaces.tsx +32 -0
- package/src/interfaces/storage-interfaces.tsx +1 -0
- package/src/interfaces/type-interfaces.tsx +8 -2
- package/src/interfaces/user-interfaces.tsx +17 -0
- package/src/nexus.environments.tsx +6 -1
- package/src/proxy-api/proxy-backend.tsx +60 -0
- package/src/proxy-api/proxy-constants.tsx +28 -0
- package/src/proxy-api/proxy-helpers.tsx +91 -0
- package/src/proxy-api-client.tsx +1 -0
- package/src/proxy-api-server.tsx +2 -0
- package/src/services/http-client-services.tsx +26 -11
- package/src/services/http-services.tsx +37 -22
- package/src/services/loader-service.tsx +5 -4
- package/src/services/localstorage-service.tsx +59 -0
- package/src/services/message-services.tsx +383 -0
- package/src/sso-config/auth-token-validation.ts +20 -0
- package/src/sso-config/callback-route.tsx +34 -0
- package/src/sso-config/cookie-encryption.ts +51 -0
- package/src/sso-config/cookie-helpers.tsx +77 -0
- package/src/sso-config/forgot-password-route.tsx +36 -0
- package/src/sso-config/oauth-callback-state.ts +58 -0
- package/src/sso-config/pkce-helpers.ts +16 -0
- package/src/sso-config/provider-complete-route.tsx +15 -0
- package/src/sso-config/redirect-context-actions.tsx +46 -0
- package/src/sso-config/redirect-context-helpers.tsx +144 -0
- package/src/sso-config/redirect-context-persist.tsx +18 -0
- package/src/sso-config/redirect-context-route.tsx +45 -0
- package/src/sso-config/refresh-route.tsx +22 -0
- package/src/sso-config/sign-in-route.tsx +69 -0
- package/src/sso-config/sign-out-route.tsx +53 -0
- package/src/sso-config/sign-up-route.tsx +46 -0
- package/src/sso-config/sso-backend-flow.ts +45 -0
- package/src/sso-config/sso-complete-route.tsx +46 -0
- package/src/sso-config/sso-config.ts +57 -0
- package/src/{interfaces → sso-config}/sso-interfaces.tsx +17 -1
- package/src/sso-config/sso-response-helpers.tsx +20 -0
- package/src/sso-server.tsx +21 -1
- package/src/styles/nexus.dialog.css +3 -0
- package/src/load-env.ts +0 -2
package/src/index.ts
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import "./load-env";
|
|
2
|
-
|
|
3
1
|
// API services
|
|
4
2
|
export * from "./api-services/preference-service";
|
|
5
3
|
export * from "./api-services/system-service";
|
|
@@ -19,24 +17,10 @@ export * from "./helpers/string-helpers";
|
|
|
19
17
|
export * from "./helpers/utility-helpers";
|
|
20
18
|
|
|
21
19
|
// Interfaces
|
|
22
|
-
export * from "./
|
|
23
|
-
export * from "./interfaces/button-interfaces";
|
|
24
|
-
export * from "./interfaces/datasource-interfaces";
|
|
25
|
-
export * from "./interfaces/datatable-interfaces";
|
|
26
|
-
export * from "./interfaces/dialogbox-interfaces";
|
|
27
|
-
export * from "./interfaces/http-interfaces";
|
|
28
|
-
export * from "./interfaces/icon-interfaces";
|
|
29
|
-
export * from "./interfaces/input-interfaces";
|
|
30
|
-
export * from "./interfaces/layout-interfaces";
|
|
31
|
-
export * from "./interfaces/menu-interfaces";
|
|
32
|
-
export * from "./interfaces/permission-interfaces";
|
|
33
|
-
export * from "./interfaces/storage-interfaces";
|
|
34
|
-
export * from "./interfaces/system-interfaces";
|
|
35
|
-
export * from "./interfaces/theme-interfaces";
|
|
36
|
-
export * from "./interfaces/type-interfaces";
|
|
20
|
+
export * from "./interface";
|
|
37
21
|
|
|
38
22
|
// Core
|
|
39
23
|
export * from "./nexus.environments";
|
|
24
|
+
export * from "./proxy-api-server";
|
|
40
25
|
export * from "./services/http-services";
|
|
41
26
|
export * from "./sso-server";
|
|
42
|
-
|
package/src/interface.ts
CHANGED
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
export * from "./interfaces/browser-interfaces";
|
|
2
2
|
export * from "./interfaces/button-interfaces";
|
|
3
|
-
export * from "./interfaces/datatable-interfaces";
|
|
4
3
|
export * from "./interfaces/datasource-interfaces";
|
|
4
|
+
export * from "./interfaces/datatable-interfaces";
|
|
5
5
|
export * from "./interfaces/dialogbox-interfaces";
|
|
6
|
+
export * from "./interfaces/exception-interfaces";
|
|
6
7
|
export * from "./interfaces/http-interfaces";
|
|
7
8
|
export * from "./interfaces/icon-interfaces";
|
|
8
9
|
export * from "./interfaces/input-interfaces";
|
|
9
10
|
export * from "./interfaces/layout-interfaces";
|
|
10
11
|
export * from "./interfaces/menu-interfaces";
|
|
12
|
+
export * from "./interfaces/message-interfaces";
|
|
11
13
|
export * from "./interfaces/permission-interfaces";
|
|
12
14
|
export * from "./interfaces/storage-interfaces";
|
|
13
15
|
export * from "./interfaces/system-interfaces";
|
|
14
16
|
export * from "./interfaces/theme-interfaces";
|
|
15
17
|
export * from "./interfaces/type-interfaces";
|
|
18
|
+
export * from "./interfaces/user-interfaces";
|
|
@@ -40,10 +40,10 @@ export interface IButton {
|
|
|
40
40
|
iconSize?: ESizes | UN;
|
|
41
41
|
columnSize?: number | UN;
|
|
42
42
|
borderSize?: number | UN;
|
|
43
|
-
target?: ETargets | UN;
|
|
43
|
+
target?: ETargets | EButtonTypes | UN;
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
export const GetButton = (label?: string | StringCallbackHandler | string | UN, id?: string | UN, url?: string | UN, icon?: EIcons | UN, onClick?: ClickHandler | UN, data?: any | Event | UN, background?: EBackgrounds | UN, defaultClass?: string | UN, displayOrder?: number | UN, iconType?: EIconTypes | UN, iconSize?: ESizes | UN, columnSize?: number | UN, borderSize?: number | UN, target?: ETargets | UN): IButton => {
|
|
46
|
+
export const GetButton = (label?: string | StringCallbackHandler | string | UN, id?: string | UN, url?: string | UN, icon?: EIcons | UN, onClick?: ClickHandler | UN, data?: any | Event | UN, background?: EBackgrounds | UN, defaultClass?: string | UN, displayOrder?: number | UN, iconType?: EIconTypes | UN, iconSize?: ESizes | UN, columnSize?: number | UN, borderSize?: number | UN, target?: ETargets | EButtonTypes | UN): IButton => {
|
|
47
47
|
return {
|
|
48
48
|
label: label,
|
|
49
49
|
id: id,
|
|
@@ -1,6 +1,11 @@
|
|
|
1
|
+
import { NEXUS_INFO } from "../nexus.environments";
|
|
2
|
+
import { GetRootApi } from "../services/http-services";
|
|
1
3
|
import { EErrorCodes, EExceptionCategories } from "./exception-interfaces";
|
|
4
|
+
import { EModules } from "./system-interfaces";
|
|
2
5
|
import { IInterface, UN } from "./type-interfaces";
|
|
3
6
|
|
|
7
|
+
export const PROXY_PUBLIC_BASE_PATH = "/api/app";
|
|
8
|
+
|
|
4
9
|
export enum EActions {
|
|
5
10
|
Get = 1,
|
|
6
11
|
Post = 2,
|
|
@@ -8,28 +13,32 @@ export enum EActions {
|
|
|
8
13
|
Delete = 4,
|
|
9
14
|
}
|
|
10
15
|
export interface IApiInformations {
|
|
11
|
-
|
|
16
|
+
module: EModules;
|
|
17
|
+
apiName: string;
|
|
12
18
|
method: EActions;
|
|
13
19
|
}
|
|
20
|
+
|
|
14
21
|
export interface IGetApiInformations extends IApiInformations {
|
|
15
22
|
cacheCode?: string;
|
|
16
23
|
/** In Minutes */
|
|
17
24
|
cacheLifetime?: number;
|
|
25
|
+
bodyType?: EBody;
|
|
26
|
+
parameters?: any;
|
|
18
27
|
}
|
|
19
28
|
|
|
20
29
|
export interface IRootRespose extends IInterface {
|
|
21
|
-
message: string | UN;
|
|
22
|
-
messageCode: string | UN;
|
|
23
30
|
isSuccess: boolean;
|
|
24
31
|
code: number;
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
32
|
+
message?: string | UN;
|
|
33
|
+
messageCode?: string | UN;
|
|
34
|
+
exceptionCategory?: EExceptionCategories | UN;
|
|
35
|
+
errorCode?: EErrorCodes | UN;
|
|
36
|
+
inputName?: string | UN;
|
|
37
|
+
result?: any | UN;
|
|
29
38
|
}
|
|
30
39
|
|
|
31
40
|
export interface IErrorRespose extends IRootRespose {
|
|
32
|
-
exception
|
|
41
|
+
exception?: any | UN;
|
|
33
42
|
}
|
|
34
43
|
export interface ISuccessResponse extends IRootRespose {}
|
|
35
44
|
export enum EBody {
|
|
@@ -49,3 +58,75 @@ export const GetMethod = (method?: EActions) => {
|
|
|
49
58
|
return "GET";
|
|
50
59
|
}
|
|
51
60
|
};
|
|
61
|
+
|
|
62
|
+
export const GetResponse = {
|
|
63
|
+
ERROR: (message: string = "An error occurred.", messageCode: string = "GENERIC_ERROR", exceptionCategory: EExceptionCategories = EExceptionCategories.Application, errorCode: EErrorCodes = EErrorCodes.Unknown, inputName: string | undefined = undefined, exception: any | null = null, code: number = 400): IErrorRespose => ({
|
|
64
|
+
isSuccess: false,
|
|
65
|
+
exception: exception,
|
|
66
|
+
code: code,
|
|
67
|
+
message: message,
|
|
68
|
+
messageCode: messageCode,
|
|
69
|
+
exceptionCategory: exceptionCategory,
|
|
70
|
+
errorCode: errorCode,
|
|
71
|
+
inputName: inputName,
|
|
72
|
+
result: null,
|
|
73
|
+
}),
|
|
74
|
+
SUCCESS: (result: any | UN, message: string = "Success", messageCode: string = "SUCCESS", code: number = 200): ISuccessResponse => ({
|
|
75
|
+
isSuccess: true,
|
|
76
|
+
result: result,
|
|
77
|
+
message: message,
|
|
78
|
+
messageCode: messageCode,
|
|
79
|
+
code: code,
|
|
80
|
+
exceptionCategory: EExceptionCategories.Application,
|
|
81
|
+
errorCode: EErrorCodes.Unknown,
|
|
82
|
+
inputName: undefined,
|
|
83
|
+
}),
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const GET_API_KEYS = ["cacheCode", "cacheLifetime", "bodyType", "parameters"];
|
|
87
|
+
const _isApi = (apiInformations: any | IApiInformations): boolean => {
|
|
88
|
+
if (!apiInformations || (!apiInformations.module && apiInformations.module !== EModules.None) || !apiInformations.apiName || !apiInformations.method) return false;
|
|
89
|
+
return true;
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
export const GetApiInformations = {
|
|
93
|
+
API: (module: EModules, apiName: string, method?: EActions | UN): IApiInformations => ({
|
|
94
|
+
module: module,
|
|
95
|
+
apiName: apiName,
|
|
96
|
+
method: method ?? EActions.Get,
|
|
97
|
+
}),
|
|
98
|
+
IS_API: _isApi,
|
|
99
|
+
GET_API: (module: EModules, apiName: string, method?: EActions | UN, cacheCode?: string | UN, cacheLifetime?: number | UN, bodyType?: EBody | UN, parameters?: any | UN): IGetApiInformations => {
|
|
100
|
+
return {
|
|
101
|
+
module: module,
|
|
102
|
+
apiName: apiName,
|
|
103
|
+
method: method ?? EActions.Get,
|
|
104
|
+
cacheCode: cacheCode,
|
|
105
|
+
cacheLifetime: cacheLifetime,
|
|
106
|
+
bodyType: bodyType,
|
|
107
|
+
parameters: parameters,
|
|
108
|
+
} as IGetApiInformations;
|
|
109
|
+
},
|
|
110
|
+
IS_GET_API: (apiInformations: any | IApiInformations): boolean => {
|
|
111
|
+
if (!_isApi(apiInformations)) return false;
|
|
112
|
+
const keys = Object.keys(apiInformations);
|
|
113
|
+
return GET_API_KEYS.every((key) => keys.includes(key));
|
|
114
|
+
},
|
|
115
|
+
BUILD_API: (apiInformations: IApiInformations, parameters?: any | UN) => {
|
|
116
|
+
const isProxyServer = NEXUS_INFO.IS_PROXY_SERVER;
|
|
117
|
+
if (isProxyServer) return BuildProxyApiUrl(apiInformations.module, apiInformations.apiName);
|
|
118
|
+
return `${GetRootApi(apiInformations.module)}/${apiInformations.apiName}`;
|
|
119
|
+
},
|
|
120
|
+
};
|
|
121
|
+
export const BuildProxyApiUrl = (module: EModules, relativePath: string, query?: string): string => {
|
|
122
|
+
const normalizedPath = relativePath.replace(/^\/+/, "");
|
|
123
|
+
if (module === EModules.None) return `/api/${normalizedPath}`;
|
|
124
|
+
const base = `${PROXY_PUBLIC_BASE_PATH}/${module}/${normalizedPath}`;
|
|
125
|
+
|
|
126
|
+
if (!query) {
|
|
127
|
+
return base;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const normalizedQuery = query.replace(/^\?+/, "");
|
|
131
|
+
return normalizedQuery ? `${base}?${normalizedQuery}` : base;
|
|
132
|
+
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { EButtonTypes, ETargets } from "./button-interfaces";
|
|
2
2
|
import { DatasourceTypes } from "./datasource-interfaces";
|
|
3
3
|
import { IColumn } from "./datatable-interfaces";
|
|
4
|
+
import { EActions, IApiInformations, IErrorRespose, ISuccessResponse } from "./http-interfaces";
|
|
4
5
|
import { EIcons, EIconTypes } from "./icon-interfaces";
|
|
5
6
|
import { EFlexPositions, EOpacity, ESizes } from "./layout-interfaces";
|
|
6
7
|
import { EBackgrounds } from "./theme-interfaces";
|
|
@@ -11,11 +12,16 @@ export type OnElementLoad = (inputBox: HTMLElement | null, inputWrapper: HTMLEle
|
|
|
11
12
|
export type AutoCompletes = "on" | "off";
|
|
12
13
|
export type ForceChangeDefaultValue<T> = (defaultValue: T, type?: EValueChangeTypes, invokeOnChanged?: boolean | UN) => boolean;
|
|
13
14
|
export type OnFormChangeHandler = (currentValue: any, isInitilizing: boolean, isForcedChanged: boolean, isDisposed: boolean, currentKey: string, index: number, type?: EValueChangeTypes, dataSource?: any | any[]) => void;
|
|
15
|
+
export type OnFormSubmitHandler = (currentValue: any, isValidForm: boolean, event: Event, form: IFormCreateResults, defaultValue: any | UN) => void;
|
|
16
|
+
export type OnApiFormSubmitHandler = (currentValue: any, apiResponse: ISuccessResponse | IErrorRespose, formResults: ISubmitFormCreateResults, defaultValue: any | UN) => void;
|
|
17
|
+
export type OverrideValidation = (currentValue: any, isValidForm: boolean, formReference: IFormCreateResults | null, action?: EActions | UN) => boolean;
|
|
18
|
+
export type RegexTypes = ERegexs | string[] | UN;
|
|
14
19
|
|
|
15
20
|
export const DEFAULT_INPUT_BACKGROUND = EBackgrounds.Background1;
|
|
16
21
|
export const MAX_INPUT_TYPE = 1900;
|
|
17
22
|
export const DEFAULT_INPUT_BORDER_SIZE = 1;
|
|
18
23
|
export const DEFAULT_TEXTAREA_ROWS = 3;
|
|
24
|
+
export const DEFAULT_ROW_GAP = 3;
|
|
19
25
|
|
|
20
26
|
export const REQUIRED_TEXT1 = "✲";
|
|
21
27
|
|
|
@@ -28,18 +34,20 @@ export enum EValueChangeTypes {
|
|
|
28
34
|
}
|
|
29
35
|
export enum ERegexs {
|
|
30
36
|
Fullname = "^[A-Za-z]+(?: [A-Za-z]+)*[0-9]?$",
|
|
31
|
-
Username = "^[A-Za-z0-9]{7,30}$",
|
|
32
|
-
ContactNumber = "^[0-9]{10}$",
|
|
33
|
-
/** Allow Null */
|
|
34
|
-
ContactNumber_NULL = "^$|^[0-9]{10}$",
|
|
35
37
|
Email = "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$",
|
|
36
38
|
/** Allow Null */
|
|
37
39
|
Email_NULL = "^$|^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,}$",
|
|
38
40
|
EmailUsername = "^(?=.*[a-zA-Z0-9])[a-zA-Z0-9]+@[a-zA-Z0-9]+\\.[a-zA-Z]{2,}$|[a-zA-Z0-9]{8,100}$",
|
|
39
41
|
Password = "^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!@#$%^&*()_+{}|:,.<>?/~]).{8,30}$",
|
|
40
|
-
EmailUsernameContact = "^(?:[A-Za-z]+[A-Za-z0-9]{2,30}|[0-9]{10}|[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,})$",
|
|
41
42
|
Hexadecimal = "^[0-9A-Fa-f]{8}$",
|
|
42
43
|
ContactEmail = "^(?:[0-9]{10}|[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,})$",
|
|
44
|
+
|
|
45
|
+
/** Allow Null */
|
|
46
|
+
ContactNumber_NULL = "^$|^[0-9]{10}$",
|
|
47
|
+
|
|
48
|
+
ContactNumber = "^[0-9]{10}$",
|
|
49
|
+
Username = "^(?!.*[._-]{2})[a-zA-Z0-9]+(?:[._-][a-zA-Z0-9]+)*$",
|
|
50
|
+
EmailUsernameMobile = "^(?:[0-9]{10}|(?!.*[._-]{2})[a-zA-Z0-9]+(?:[._-][a-zA-Z0-9]+)*|[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[A-Za-z]{2,})$",
|
|
43
51
|
}
|
|
44
52
|
export enum EInputTypes {
|
|
45
53
|
Textbox = 1,
|
|
@@ -119,7 +127,7 @@ export interface ISubmitbutton extends IElement {
|
|
|
119
127
|
}
|
|
120
128
|
export interface ITextbox extends IInputParameters<string | null> {
|
|
121
129
|
isPassword?: boolean | UN;
|
|
122
|
-
regexKey?:
|
|
130
|
+
regexKey?: RegexTypes | UN;
|
|
123
131
|
}
|
|
124
132
|
export interface ISlider extends IInputParameters<number | null> {
|
|
125
133
|
step?: number | UN;
|
|
@@ -133,7 +141,7 @@ export interface IRadiobox extends ICheckbox {}
|
|
|
133
141
|
export interface ITextarea extends IInputParameters<string | null> {
|
|
134
142
|
isShowPreview?: boolean | UN;
|
|
135
143
|
rows?: number | UN;
|
|
136
|
-
regexKey?:
|
|
144
|
+
regexKey?: RegexTypes | UN;
|
|
137
145
|
}
|
|
138
146
|
export interface INumber extends IInputParameters<number | null> {
|
|
139
147
|
floatingPoint?: number | UN;
|
|
@@ -176,18 +184,60 @@ export interface IForm {
|
|
|
176
184
|
onChanged: OnFormChangeHandler;
|
|
177
185
|
index: number;
|
|
178
186
|
isTabularForm: boolean;
|
|
179
|
-
parentIdentifier: HTMLElement | UN | string;
|
|
180
187
|
defaultValue?: any | UN;
|
|
181
188
|
}
|
|
189
|
+
export interface ISubmitForm {
|
|
190
|
+
inputs: IInputRootParameters<any>[];
|
|
191
|
+
onChanged: OnFormChangeHandler | UN;
|
|
192
|
+
defaultValue?: DatasourceTypes | UN;
|
|
193
|
+
onSubmit: OnFormSubmitHandler;
|
|
194
|
+
onCancelClick?: ClickHandler | UN;
|
|
195
|
+
overrideValidation?: OverrideValidation | UN;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export interface IApiForm {
|
|
199
|
+
inputs: IInputRootParameters<any>[];
|
|
200
|
+
api: IApiInformations;
|
|
201
|
+
onApiSubmit: OnApiFormSubmitHandler;
|
|
202
|
+
onChanged: OnFormChangeHandler | UN;
|
|
203
|
+
onSubmit: OnFormSubmitHandler | UN;
|
|
204
|
+
defaultValue?: DatasourceTypes | UN;
|
|
205
|
+
onCancelClick?: ClickHandler | UN;
|
|
206
|
+
overrideValidation?: OverrideValidation | UN;
|
|
207
|
+
}
|
|
208
|
+
|
|
182
209
|
export interface IFormCreateResults {
|
|
183
210
|
inputResults: IInputCreateResults[];
|
|
184
211
|
formElement: HTMLElement;
|
|
185
212
|
dispose: (invokeOnChanged?: boolean) => void;
|
|
186
213
|
forceChangeDefaultValue: ForceChangeDefaultValue<any> | null;
|
|
187
214
|
}
|
|
215
|
+
export interface ISubmitFormCreateResults {
|
|
216
|
+
formElement: HTMLElement;
|
|
217
|
+
dispose: () => void;
|
|
218
|
+
formObject: IFormCreateResults;
|
|
219
|
+
}
|
|
188
220
|
|
|
189
221
|
export const GetForms = {
|
|
190
|
-
FORM: (inputs: IInputRootParameters<any>[],
|
|
222
|
+
FORM: (inputs: IInputRootParameters<any>[], onChanged: OnFormChangeHandler, defaultValue?: any | UN, index?: number | UN, isTabularForm?: boolean | UN): IForm => ({ inputs, onChanged, index: index ?? 0, isTabularForm: isTabularForm ?? false, defaultValue }),
|
|
223
|
+
SUBMITFORM: (inputs: IInputRootParameters<any>[], onSubmit: OnFormSubmitHandler, defaultValue?: DatasourceTypes | UN, overrideValidation?: OverrideValidation | UN, onChanged?: OnFormChangeHandler | UN, onCancelClick?: ClickHandler | UN): ISubmitForm => ({
|
|
224
|
+
inputs,
|
|
225
|
+
onChanged,
|
|
226
|
+
defaultValue,
|
|
227
|
+
onSubmit,
|
|
228
|
+
onCancelClick,
|
|
229
|
+
overrideValidation,
|
|
230
|
+
}),
|
|
231
|
+
APIFORM: (inputs: IInputRootParameters<any>[], api: IApiInformations, onApiSubmit: OnApiFormSubmitHandler, defaultValue?: DatasourceTypes | UN, onSubmit?: OnFormSubmitHandler | UN, overrideValidation?: OverrideValidation | UN, onCancelClick?: ClickHandler | UN, onChanged?: OnFormChangeHandler | UN): IApiForm => ({
|
|
232
|
+
inputs,
|
|
233
|
+
api,
|
|
234
|
+
onApiSubmit,
|
|
235
|
+
onChanged,
|
|
236
|
+
defaultValue,
|
|
237
|
+
onSubmit,
|
|
238
|
+
onCancelClick,
|
|
239
|
+
overrideValidation,
|
|
240
|
+
}),
|
|
191
241
|
};
|
|
192
242
|
export const GetInputs = {
|
|
193
243
|
ICON: (icon: EIcons, iconSize?: ESizes | UN, boxSize?: ESizes | UN, opacity?: EOpacity | UN, type?: EIconTypes | UN, background?: EBackgrounds | UN, onClicked?: ClickHandler | UN, onMouseEnter?: ClickHandler | UN): IInputIcon => ({
|
|
@@ -200,7 +250,7 @@ export const GetInputs = {
|
|
|
200
250
|
onClicked: onClicked,
|
|
201
251
|
onMouseEnter: onMouseEnter,
|
|
202
252
|
}),
|
|
203
|
-
TEXTBOX: (id: string, columnSize: number, displayOrder: number, label?: string | UN, placeholder?: string | UN, validatorKey?: string | UN, minLength?: number | UN, maxLength?: number | UN, regexKey?:
|
|
253
|
+
TEXTBOX: (id: string, columnSize: number, displayOrder: number, label?: string | UN, placeholder?: string | UN, validatorKey?: string | UN, minLength?: number | UN, maxLength?: number | UN, regexKey?: RegexTypes | UN, isPassword?: boolean | UN, isReadonly?: boolean | UN, isAutoComplete?: boolean | UN, defaultValue?: string | UN, background?: EBackgrounds | UN, borderSize?: number | UN, inputSize?: ESizes | UN, icon?: IInputIcon | UN, defaultClass?: string | UN, onChanged?: OnInputChangeHandler<string | null> | UN): ITextbox => ({
|
|
204
254
|
id: id,
|
|
205
255
|
columnSize: columnSize,
|
|
206
256
|
displayOrder: displayOrder,
|
|
@@ -223,7 +273,7 @@ export const GetInputs = {
|
|
|
223
273
|
inputType: EInputTypes.Textbox,
|
|
224
274
|
autoComplete: isAutoComplete !== false ? undefined : "off",
|
|
225
275
|
}),
|
|
226
|
-
TEXTAREA: (id: string, columnSize: number, displayOrder: number, label?: string | UN, placeholder?: string | UN, validatorKey?: string | UN, minLength?: number | UN, maxLength?: number | UN, rows?: number | UN, isShowPreview?: boolean | UN, defaultValue?: string | UN, regexKey?:
|
|
276
|
+
TEXTAREA: (id: string, columnSize: number, displayOrder: number, label?: string | UN, placeholder?: string | UN, validatorKey?: string | UN, minLength?: number | UN, maxLength?: number | UN, rows?: number | UN, isShowPreview?: boolean | UN, defaultValue?: string | UN, regexKey?: RegexTypes | UN, isReadonly?: boolean | UN, isAutoComplete?: boolean | UN, background?: EBackgrounds | UN, borderSize?: number | UN, inputSize?: ESizes | UN, icon?: IInputIcon | UN, defaultClass?: string | UN, onChanged?: OnInputChangeHandler<string | null> | UN): ITextarea => ({
|
|
227
277
|
id: id,
|
|
228
278
|
columnSize: columnSize,
|
|
229
279
|
displayOrder: displayOrder,
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { EIcons } from "./icon-interfaces";
|
|
2
|
+
import { EBackgrounds } from "./theme-interfaces";
|
|
3
|
+
|
|
4
|
+
export enum EMessageTypes {
|
|
5
|
+
Information = EBackgrounds.InfoColor,
|
|
6
|
+
Success = EBackgrounds.SuccessColor,
|
|
7
|
+
Warning = EBackgrounds.WarningColor,
|
|
8
|
+
Error = EBackgrounds.ErrorColor,
|
|
9
|
+
}
|
|
10
|
+
export enum EMessageIcons {
|
|
11
|
+
Information = EIcons.INFORMATION,
|
|
12
|
+
Success = EIcons.CHECKMARK_DOUBLE,
|
|
13
|
+
Warning = EIcons.WARNING,
|
|
14
|
+
Error = EIcons.CLOSE,
|
|
15
|
+
Question = EIcons.HELP,
|
|
16
|
+
Delete = EIcons.TRASH,
|
|
17
|
+
Bin = EIcons.TRASH_BIN,
|
|
18
|
+
}
|
|
19
|
+
export enum EMessageResult {
|
|
20
|
+
OK = "OK",
|
|
21
|
+
Yes = "Yes",
|
|
22
|
+
No = "No",
|
|
23
|
+
Cancel = "Cancel",
|
|
24
|
+
Undefined = "Undefined",
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export enum EMessageBoxButtons {
|
|
28
|
+
OK = "OK",
|
|
29
|
+
YES_NO = "Yes_No",
|
|
30
|
+
YES_CANCEL = "Yes_Cancel",
|
|
31
|
+
YES_NO_CANCEL = "Yes_No_Cancel",
|
|
32
|
+
}
|
|
@@ -15,8 +15,14 @@ export type ObjectCallbackHandler<T> = CallbackHandler<T, T>;
|
|
|
15
15
|
export type ObjectNullCallbackHandler<T> = CallbackHandler<T | UN, T | UN>;
|
|
16
16
|
export type ArrayCallbackHandler<T> = CallbackHandler<T[], T[]>;
|
|
17
17
|
export type ArrayNullCallbackHandler<T> = CallbackHandler<T[] | UN, T[] | UN>;
|
|
18
|
-
export type VoidCallbackHandler = CallbackHandler<void, void>;
|
|
19
|
-
|
|
18
|
+
export type VoidCallbackHandler = CallbackHandler<void | any | any[] | UN, void>;
|
|
19
|
+
export type PopupMessageCallback = (type: ECallbackTypes) => void;
|
|
20
20
|
export interface IInterface {
|
|
21
21
|
// Blank Interface
|
|
22
22
|
}
|
|
23
|
+
|
|
24
|
+
export enum ECallbackTypes {
|
|
25
|
+
Initialize = 0,
|
|
26
|
+
Destroy = 1,
|
|
27
|
+
ForceChangeValue = 2,
|
|
28
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { EModules } from "./system-interfaces";
|
|
2
|
+
|
|
3
|
+
export enum EUserRoles {
|
|
4
|
+
Guest = 0,
|
|
5
|
+
User = 1,
|
|
6
|
+
Admin = 2,
|
|
7
|
+
SuperAdmin = 3,
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface IUserValidationResponse {
|
|
11
|
+
isValidUser: boolean;
|
|
12
|
+
isValidCompany: boolean;
|
|
13
|
+
isValidBranch: boolean;
|
|
14
|
+
isValidFiscalYear: boolean;
|
|
15
|
+
userRole: EUserRoles;
|
|
16
|
+
assignedModules: EModules;
|
|
17
|
+
}
|
|
@@ -16,8 +16,12 @@ export const URLS = {
|
|
|
16
16
|
CURRENT: process.env.URL_CURRENT as string,
|
|
17
17
|
AUTHENTICATION: process.env.URL_AUTHENTICATION as string,
|
|
18
18
|
SIGN_IN: process.env.URL_AUTHENTICATION_SIGN_IN as string,
|
|
19
|
+
ALLOWED_ORIGINS: (process.env.ALLOWED_ORIGINS ?? "")
|
|
20
|
+
.split(",")
|
|
21
|
+
.map((origin) => origin.trim())
|
|
22
|
+
.filter(Boolean),
|
|
19
23
|
};
|
|
20
|
-
const FILE = (apiName: string, filePath: string, fileName: string) => `${
|
|
24
|
+
const FILE = (apiName: string, filePath: string, fileName: string) => `${process.env.API_RESOURCES_BASE_PATH ?? `/api/app/${EModules.Drive}/Resources`}/${apiName}/${encodeURIComponent(filePath)}/${encodeURIComponent(fileName)}`;
|
|
21
25
|
export const FILES = {
|
|
22
26
|
LOGO: {
|
|
23
27
|
ICON: FILE("Image", "logos", "icon-logo"),
|
|
@@ -37,6 +41,7 @@ export const NEXUS_INFO = {
|
|
|
37
41
|
SOFTWARE_VERSION: process.env.SOFTWARE_VERSION as string,
|
|
38
42
|
SOFTWARE_LAST_UPDATE_DATE: process.env.SOFTWARE_LAST_UPDATE_DATE as string,
|
|
39
43
|
SOFTWARE_COMPANY_WEBSITE: process.env.SOFTWARE_COMPANY_WEBSITE as string,
|
|
44
|
+
IS_PROXY_SERVER: process.env.IS_PROXY_SERVER !== "false",
|
|
40
45
|
};
|
|
41
46
|
export const NEXUS_CONFIG = {
|
|
42
47
|
/** Default revalidate time for static generation (in minutes) */
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from "next/server";
|
|
2
|
+
import { EErrorCodes } from "../interfaces/exception-interfaces";
|
|
3
|
+
import { GetRootApi } from "../services/http-services";
|
|
4
|
+
import { DecryptCookieValue } from "../sso-config/cookie-encryption";
|
|
5
|
+
import { ALLOWED_MODULES, PROXY_ALLOWED_METHODS } from "./proxy-constants";
|
|
6
|
+
import { BuildBackendUrl, BuildForwardHeaders, ProxyErrorResponse, ReadRequestBody, SanitizeResponseHeaders } from "./proxy-helpers";
|
|
7
|
+
|
|
8
|
+
const GENERIC_PROXY_ERROR = "Unable to reach the backend service.";
|
|
9
|
+
const MODULE_NOT_ALLOWED_ERROR = "This module is not allowed for proxy requests.";
|
|
10
|
+
|
|
11
|
+
const ACCESS_TOKEN_COOKIE = "access_token";
|
|
12
|
+
|
|
13
|
+
export async function ProxyToBackend(req: NextRequest, moduleParam: string, pathSegments: string[]): Promise<NextResponse> {
|
|
14
|
+
const method = req.method.toUpperCase();
|
|
15
|
+
|
|
16
|
+
if (!PROXY_ALLOWED_METHODS.has(method)) {
|
|
17
|
+
return ProxyErrorResponse(`Method ${method} is not supported.`, 405, "METHOD_NOT_SUPPORTED", EErrorCodes.Forbidden);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const module = ALLOWED_MODULES.includes(moduleParam) ? parseInt(moduleParam) : null;
|
|
21
|
+
if (!module) return ProxyErrorResponse(MODULE_NOT_ALLOWED_ERROR, 400, "INVALID_MODULE");
|
|
22
|
+
const backendBase = GetRootApi(module);
|
|
23
|
+
const backendUrl = BuildBackendUrl(backendBase, pathSegments, req.nextUrl.searchParams);
|
|
24
|
+
|
|
25
|
+
return ProxyToBackendRequest(backendUrl, backendBase, module, method, req);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export async function ProxyToBackendRequest(backendFullUrl: string, backendUrlBase: string, module: number, method: string, req: NextRequest): Promise<NextResponse> {
|
|
29
|
+
// BuildForwardHeaders already strips the Cookie header (see PROXY_REQUEST_HEADERS_TO_SKIP).
|
|
30
|
+
const headers = BuildForwardHeaders(req);
|
|
31
|
+
|
|
32
|
+
// Decrypt the access token from the httpOnly cookie and inject it as a Bearer token.
|
|
33
|
+
// The raw encrypted blob is never forwarded — only the plaintext token reaches the backend.
|
|
34
|
+
const encryptedToken = req.cookies.get(ACCESS_TOKEN_COOKIE)?.value;
|
|
35
|
+
if (encryptedToken) {
|
|
36
|
+
const token = DecryptCookieValue(encryptedToken);
|
|
37
|
+
if (token) headers.set("Authorization", `Bearer ${token}`);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const body = await ReadRequestBody(req, method);
|
|
41
|
+
|
|
42
|
+
let backendResponse: Response;
|
|
43
|
+
try {
|
|
44
|
+
backendResponse = await fetch(backendFullUrl, {
|
|
45
|
+
method,
|
|
46
|
+
headers,
|
|
47
|
+
body,
|
|
48
|
+
redirect: "manual",
|
|
49
|
+
});
|
|
50
|
+
} catch {
|
|
51
|
+
return ProxyErrorResponse(GENERIC_PROXY_ERROR, 502, "BACKEND_UNREACHABLE");
|
|
52
|
+
}
|
|
53
|
+
const responseHeaders = SanitizeResponseHeaders(backendResponse.headers, backendUrlBase, module);
|
|
54
|
+
const responseBody = await backendResponse.arrayBuffer();
|
|
55
|
+
|
|
56
|
+
return new NextResponse(responseBody, {
|
|
57
|
+
status: backendResponse.status,
|
|
58
|
+
headers: responseHeaders,
|
|
59
|
+
});
|
|
60
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { EModules } from "../interfaces/system-interfaces";
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
/** HTTP methods supported by the backend proxy. */
|
|
5
|
+
export const PROXY_ALLOWED_METHODS = new Set(["GET", "POST", "PUT", "DELETE", "PATCH"]);
|
|
6
|
+
|
|
7
|
+
/** Request headers that must not be forwarded to the backend. */
|
|
8
|
+
export const PROXY_REQUEST_HEADERS_TO_SKIP = new Set(["host", "connection", "content-length", "transfer-encoding", "keep-alive", "upgrade", "cookie"]);
|
|
9
|
+
|
|
10
|
+
/** Response headers that must not be forwarded to the browser. */
|
|
11
|
+
export const PROXY_RESPONSE_HEADERS_TO_SKIP = new Set(["connection", "transfer-encoding", "keep-alive", "proxy-authenticate", "proxy-authorization", "te", "trailer", "upgrade"]);
|
|
12
|
+
|
|
13
|
+
/** Public path prefix the browser uses to reach the proxy. */
|
|
14
|
+
|
|
15
|
+
/** Allowed modules for proxy requests. */
|
|
16
|
+
|
|
17
|
+
export const ALLOWED_MODULES = [
|
|
18
|
+
EModules.Authentication.toString(),
|
|
19
|
+
EModules.Configuration.toString(),
|
|
20
|
+
EModules.Enterprise.toString(),
|
|
21
|
+
EModules.Drive.toString(),
|
|
22
|
+
EModules.Message.toString(),
|
|
23
|
+
EModules.Accounting.toString(),
|
|
24
|
+
EModules.Report.toString(),
|
|
25
|
+
EModules.Portfolio.toString(),
|
|
26
|
+
EModules.Subscription.toString(),
|
|
27
|
+
EModules.Reservation.toString(),
|
|
28
|
+
]
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from "next/server";
|
|
2
|
+
import { EErrorCodes, EExceptionCategories } from "../interfaces/exception-interfaces";
|
|
3
|
+
import { GetResponse, PROXY_PUBLIC_BASE_PATH } from "../interfaces/http-interfaces";
|
|
4
|
+
import { EModules } from "../interfaces/system-interfaces";
|
|
5
|
+
import { PROXY_REQUEST_HEADERS_TO_SKIP, PROXY_RESPONSE_HEADERS_TO_SKIP } from "./proxy-constants";
|
|
6
|
+
|
|
7
|
+
const BACKEND_NOT_CONFIGURED_ERROR = "Backend API is not configured.";
|
|
8
|
+
|
|
9
|
+
export function BuildBackendUrl(backendBase: string, pathSegments: string[], searchParams: URLSearchParams): string {
|
|
10
|
+
if (pathSegments.some((segment) => segment === ".." || segment === "." || segment.includes("\\"))) throw new Error("Invalid path.");
|
|
11
|
+
else if (!backendBase) throw new Error(BACKEND_NOT_CONFIGURED_ERROR);
|
|
12
|
+
|
|
13
|
+
const path = pathSegments.map(encodeURIComponent).join("/");
|
|
14
|
+
const query = searchParams.toString();
|
|
15
|
+
|
|
16
|
+
return query ? `${backendBase}/${path}?${query}` : `${backendBase}/${path}`;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function BuildForwardHeaders(req: NextRequest): Headers {
|
|
20
|
+
const headers = new Headers();
|
|
21
|
+
|
|
22
|
+
req.headers.forEach((value, key) => {
|
|
23
|
+
if (PROXY_REQUEST_HEADERS_TO_SKIP.has(key.toLowerCase())) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
headers.set(key, value);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
return headers;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export async function ReadRequestBody(req: NextRequest, method: string): Promise<BodyInit | undefined> {
|
|
34
|
+
if (method === "GET" || method === "HEAD" || method === "OPTIONS") {
|
|
35
|
+
return undefined;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return await req.arrayBuffer();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function ProxyErrorResponse(message: string, status: number, messageCode = "PROXY_ERROR", errorCode: EErrorCodes = EErrorCodes.Unknown): NextResponse {
|
|
42
|
+
return NextResponse.json(GetResponse.ERROR(message, messageCode, EExceptionCategories.Application, errorCode, undefined, null, status), { status });
|
|
43
|
+
}
|
|
44
|
+
function stripBackendBasePath(pathname: string, backendBaseUrl: string): string {
|
|
45
|
+
try {
|
|
46
|
+
const basePath = new URL(backendBaseUrl).pathname.replace(/\/+$/, "");
|
|
47
|
+
if (pathname.startsWith(basePath)) {
|
|
48
|
+
return pathname.slice(basePath.length).replace(/^\/+/, "");
|
|
49
|
+
}
|
|
50
|
+
} catch {
|
|
51
|
+
// Fall through to pathname cleanup below.
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return pathname.replace(/^\/+/, "");
|
|
55
|
+
}
|
|
56
|
+
export function SanitizeResponseHeaders(headers: Headers, backendBaseUrl: string, module: EModules): Headers {
|
|
57
|
+
const sanitized = new Headers();
|
|
58
|
+
let backendHost: string | undefined;
|
|
59
|
+
|
|
60
|
+
try {
|
|
61
|
+
backendHost = new URL(backendBaseUrl).host;
|
|
62
|
+
} catch {
|
|
63
|
+
backendHost = undefined;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
headers.forEach((value, key) => {
|
|
67
|
+
const lowerKey = key.toLowerCase();
|
|
68
|
+
|
|
69
|
+
if (PROXY_RESPONSE_HEADERS_TO_SKIP.has(lowerKey)) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (lowerKey === "location" && backendHost) {
|
|
74
|
+
try {
|
|
75
|
+
const location = new URL(value, backendBaseUrl);
|
|
76
|
+
if (location.host === backendHost) {
|
|
77
|
+
const relativePath = stripBackendBasePath(location.pathname, backendBaseUrl);
|
|
78
|
+
const proxyPath = `${PROXY_PUBLIC_BASE_PATH}/${module}/${relativePath}${location.search}`;
|
|
79
|
+
sanitized.set(key, proxyPath);
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
} catch {
|
|
83
|
+
// Relative locations are forwarded as-is.
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
sanitized.set(key, value);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
return sanitized;
|
|
91
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./proxy-api/proxy-constants";
|