@carecard/common-util 3.1.1 → 3.1.2
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/index.d.ts +53 -0
- package/index.mjs +51 -1
- package/package.json +4 -4
- package/src/index.js +21 -4
- package/src/lib/appErrorHandlers.js +95 -23
- package/src/lib/errorConstants.js +39 -0
- package/src/lib/errorUtils.js +137 -0
- package/src/middleware/requestContext.js +14 -3
package/index.d.ts
CHANGED
|
@@ -68,6 +68,19 @@ export function throwInputNotUuidError(params?: { userMessage?: string; details?
|
|
|
68
68
|
export function throwFileTooLargeError(params?: { userMessage?: string; details?: any }): never;
|
|
69
69
|
/** Throws an Invalid_Time_Value error. */
|
|
70
70
|
export function throwInvalidTimeValueError(params?: { userMessage?: string; details?: any }): never;
|
|
71
|
+
/** Throws a Not_Found error. */
|
|
72
|
+
export function throwNotFoundError(params?: { userMessage?: string; details?: any }): never;
|
|
73
|
+
/** Throws a Record_Save_Failure error. */
|
|
74
|
+
export function throwRecordSaveFailureError(params?: {
|
|
75
|
+
userMessage?: string;
|
|
76
|
+
details?: any;
|
|
77
|
+
}): never;
|
|
78
|
+
/** Throws an Application_Error error. */
|
|
79
|
+
export function throwApplicationError(params?: { userMessage?: string; details?: any }): never;
|
|
80
|
+
/** Throws a Network_Error error. */
|
|
81
|
+
export function throwNetworkError(params?: { userMessage?: string; details?: any }): never;
|
|
82
|
+
/** Throws an Unexpected_Error error. */
|
|
83
|
+
export function throwUnexpectedError(params?: { userMessage?: string; details?: any }): never;
|
|
71
84
|
|
|
72
85
|
/**
|
|
73
86
|
* Application-level error handlers and throwers.
|
|
@@ -95,6 +108,11 @@ export const error: {
|
|
|
95
108
|
throwInputNotUuidError: typeof throwInputNotUuidError;
|
|
96
109
|
throwFileTooLargeError: typeof throwFileTooLargeError;
|
|
97
110
|
throwInvalidTimeValueError: typeof throwInvalidTimeValueError;
|
|
111
|
+
throwNotFoundError: typeof throwNotFoundError;
|
|
112
|
+
throwRecordSaveFailureError: typeof throwRecordSaveFailureError;
|
|
113
|
+
throwApplicationError: typeof throwApplicationError;
|
|
114
|
+
throwNetworkError: typeof throwNetworkError;
|
|
115
|
+
throwUnexpectedError: typeof throwUnexpectedError;
|
|
98
116
|
};
|
|
99
117
|
|
|
100
118
|
/** Sets 200 OK status and optionally an ETag header. */
|
|
@@ -188,3 +206,38 @@ export function createError(params: {
|
|
|
188
206
|
message?: string;
|
|
189
207
|
fields?: Record<string, string>;
|
|
190
208
|
}): ApiError;
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Standard API error codes.
|
|
212
|
+
*/
|
|
213
|
+
export enum ApiErrorType {
|
|
214
|
+
VALIDATION_FAILURE = 'VALIDATION_FAILURE',
|
|
215
|
+
WRONG_CREDENTIALS = 'WRONG_CREDENTIALS',
|
|
216
|
+
RECORD_NOT_FOUND = 'RECORD_NOT_FOUND',
|
|
217
|
+
RECORD_NOT_SAVED = 'RECORD_NOT_SAVED',
|
|
218
|
+
RECORD_SAVE_FAILURE = 'RECORD_SAVE_FAILURE',
|
|
219
|
+
APPLICATION_ERROR = 'APPLICATION_ERROR',
|
|
220
|
+
NOT_FOUND = 'NOT_FOUND',
|
|
221
|
+
LOGIN_REQUIRED = 'LOGIN_REQUIRED',
|
|
222
|
+
NETWORK_ERROR = 'NETWORK_ERROR',
|
|
223
|
+
UNEXPECTED_ERROR = 'UNEXPECTED_ERROR',
|
|
224
|
+
ACCOUNT_SUSPENDED = 'ACCOUNT_SUSPENDED',
|
|
225
|
+
ACCOUNT_BLOCKED = 'ACCOUNT_BLOCKED',
|
|
226
|
+
ACCOUNT_INACTIVE = 'ACCOUNT_INACTIVE',
|
|
227
|
+
RECORD_EXIST = 'RECORD_EXIST',
|
|
228
|
+
UPDATE_FAILED = 'UPDATE_FAILED',
|
|
229
|
+
TRANSACTION_FAILED = 'TRANSACTION_FAILED',
|
|
230
|
+
USED_TOKEN = 'USED_TOKEN',
|
|
231
|
+
BAD_VISITOR_TOKEN = 'BAD_VISITOR_TOKEN',
|
|
232
|
+
FILE_FORMAT_NOT_SUPPORTED = 'FILE_FORMAT_NOT_SUPPORTED',
|
|
233
|
+
NOT_AUTHORIZED = 'NOT_AUTHORIZED',
|
|
234
|
+
BAD_INPUT = 'BAD_INPUT',
|
|
235
|
+
INPUT_NOT_UUID = 'INPUT_NOT_UUID',
|
|
236
|
+
FILE_TOO_LARGE = 'FILE_TOO_LARGE',
|
|
237
|
+
INVALID_TIME_VALUE = 'INVALID_TIME_VALUE'
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Extracts error message from standardized error data.
|
|
242
|
+
*/
|
|
243
|
+
export function getApiErrorMessage(errorData: any, t: (key: string) => string): string;
|
package/index.mjs
CHANGED
|
@@ -1,5 +1,55 @@
|
|
|
1
1
|
import commonUtil from './index.js';
|
|
2
2
|
|
|
3
|
-
export const {
|
|
3
|
+
export const {
|
|
4
|
+
// Legacy objects
|
|
5
|
+
util,
|
|
6
|
+
error,
|
|
7
|
+
resCode,
|
|
8
|
+
|
|
9
|
+
// Top-level utility functions
|
|
10
|
+
extractObjectWithProperties,
|
|
11
|
+
|
|
12
|
+
// Top-level error handlers/throwers
|
|
13
|
+
throwAccountSuspendedError,
|
|
14
|
+
throwAccountBlockedError,
|
|
15
|
+
throwAccountInactiveError,
|
|
16
|
+
throwNotFoundError,
|
|
17
|
+
throwRecordSaveFailureError,
|
|
18
|
+
throwApplicationError,
|
|
19
|
+
throwNetworkError,
|
|
20
|
+
throwUnexpectedError,
|
|
21
|
+
notFound404,
|
|
22
|
+
appErrorHandler,
|
|
23
|
+
throwValidationFailureError,
|
|
24
|
+
throwRecordExistError,
|
|
25
|
+
throwWrongCredentialsError,
|
|
26
|
+
throwLoginRequiredError,
|
|
27
|
+
throwRecordNotFoundError,
|
|
28
|
+
throwRecordNotSavedError,
|
|
29
|
+
throwUpdateFailedError,
|
|
30
|
+
throwTransactionFailedError,
|
|
31
|
+
throwUsedTokenError,
|
|
32
|
+
throwBadVisitorTokenError,
|
|
33
|
+
throwFileFormatNotSupportedError,
|
|
34
|
+
throwNotAuthorizedError,
|
|
35
|
+
throwBadInputError,
|
|
36
|
+
throwInputNotUuidError,
|
|
37
|
+
throwFileTooLargeError,
|
|
38
|
+
throwInvalidTimeValueError,
|
|
39
|
+
|
|
40
|
+
// Top-level response status functions
|
|
41
|
+
setOk200,
|
|
42
|
+
setCreated201,
|
|
43
|
+
setBadRequest400ClientError,
|
|
44
|
+
|
|
45
|
+
// Core functions
|
|
46
|
+
requestContext,
|
|
47
|
+
sendResponse,
|
|
48
|
+
createError,
|
|
49
|
+
|
|
50
|
+
// Constants and utils
|
|
51
|
+
ApiErrorType,
|
|
52
|
+
getApiErrorMessage
|
|
53
|
+
} = commonUtil;
|
|
4
54
|
|
|
5
55
|
export default commonUtil;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@carecard/common-util",
|
|
3
|
-
"version": "3.1.
|
|
3
|
+
"version": "3.1.2",
|
|
4
4
|
"description": "Standardized API response and utility functions for Express.js and Next.js microservices",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"module": "index.mjs",
|
|
@@ -28,10 +28,10 @@
|
|
|
28
28
|
},
|
|
29
29
|
"scripts": {
|
|
30
30
|
"test": "export NODE_ENV=test && mocha --recursive",
|
|
31
|
-
"test:types": "tsc --noEmit && mocha -r ts-node/register test/types.test.
|
|
31
|
+
"test:types": "tsc --noEmit && mocha -r ts-node/register test/types.test.mts",
|
|
32
32
|
"test:coverage": "export NODE_ENV=test && nyc mocha --recursive",
|
|
33
|
-
"test:types:coverage": "tsc --noEmit && export NODE_ENV=test && nyc mocha -r ts-node/register 'test/**/*.{js,ts}'",
|
|
34
|
-
"coverage": "export NODE_ENV=test && nyc mocha --recursive -r ts-node/register 'test/**/*.{js,ts}' && nyc check-coverage",
|
|
33
|
+
"test:types:coverage": "tsc --noEmit && export NODE_ENV=test && nyc mocha -r ts-node/register 'test/**/*.{js,ts,mts}'",
|
|
34
|
+
"coverage": "export NODE_ENV=test && nyc mocha --recursive -r ts-node/register 'test/**/*.{js,ts,mts}' && nyc check-coverage",
|
|
35
35
|
"prettier": "prettier --write .",
|
|
36
36
|
"prettier:check": "prettier --check .",
|
|
37
37
|
"prepare": "husky"
|
package/src/index.js
CHANGED
|
@@ -1,12 +1,29 @@
|
|
|
1
1
|
const requestContext = require('./middleware/requestContext');
|
|
2
2
|
const sendResponse = require('./utils/sendResponse');
|
|
3
3
|
const createError = require('./utils/createError');
|
|
4
|
+
const utilityFunctions = require('./lib/utilityFunctions');
|
|
5
|
+
const appErrorHandlers = require('./lib/appErrorHandlers');
|
|
6
|
+
const responseStatus = require('./lib/responseStatus');
|
|
7
|
+
const errorConstants = require('./lib/errorConstants');
|
|
8
|
+
const errorUtils = require('./lib/errorUtils');
|
|
4
9
|
|
|
5
10
|
module.exports = {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
11
|
+
// Nested exports (Legacy/Deprecated)
|
|
12
|
+
util: utilityFunctions,
|
|
13
|
+
error: appErrorHandlers,
|
|
14
|
+
resCode: responseStatus,
|
|
15
|
+
|
|
16
|
+
// Direct top-level exports
|
|
17
|
+
...utilityFunctions,
|
|
18
|
+
...appErrorHandlers,
|
|
19
|
+
...responseStatus,
|
|
20
|
+
|
|
21
|
+
// Core utilities
|
|
9
22
|
requestContext,
|
|
10
23
|
sendResponse,
|
|
11
|
-
createError
|
|
24
|
+
createError,
|
|
25
|
+
|
|
26
|
+
// Error system
|
|
27
|
+
ApiErrorType: errorConstants.ApiErrorType,
|
|
28
|
+
getApiErrorMessage: errorUtils.getApiErrorMessage
|
|
12
29
|
};
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const sendResponse = require('../utils/sendResponse');
|
|
4
4
|
const createError = require('../utils/createError');
|
|
5
|
+
const { ApiErrorType } = require('./errorConstants');
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* These are application level error handlers. All error responses should be send using these functions.
|
|
@@ -11,6 +12,11 @@ module.exports = {
|
|
|
11
12
|
throwAccountSuspendedError,
|
|
12
13
|
throwAccountBlockedError,
|
|
13
14
|
throwAccountInactiveError,
|
|
15
|
+
throwNotFoundError,
|
|
16
|
+
throwRecordSaveFailureError,
|
|
17
|
+
throwApplicationError,
|
|
18
|
+
throwNetworkError,
|
|
19
|
+
throwUnexpectedError,
|
|
14
20
|
notFound404,
|
|
15
21
|
appErrorHandler,
|
|
16
22
|
throwValidationFailureError,
|
|
@@ -39,7 +45,7 @@ function notFound404(req, res, next) {
|
|
|
39
45
|
success: false,
|
|
40
46
|
message: 'Not found',
|
|
41
47
|
error: createError({
|
|
42
|
-
code:
|
|
48
|
+
code: ApiErrorType.NOT_FOUND,
|
|
43
49
|
message: 'Not found',
|
|
44
50
|
details: null
|
|
45
51
|
})
|
|
@@ -49,11 +55,17 @@ function notFound404(req, res, next) {
|
|
|
49
55
|
function appErrorHandler(err, req, res, next) {
|
|
50
56
|
let statusCode = 500;
|
|
51
57
|
const errorMessage = err?.message || 'Internal Server Error';
|
|
58
|
+
const errorCode = err?.code;
|
|
52
59
|
|
|
53
|
-
switch (err?.message) {
|
|
60
|
+
switch (errorCode || err?.message) {
|
|
54
61
|
case 'Account_Suspended':
|
|
55
62
|
case 'Account_Blocked':
|
|
56
63
|
case 'Account_Inactive':
|
|
64
|
+
case ApiErrorType.ACCOUNT_SUSPENDED:
|
|
65
|
+
case ApiErrorType.ACCOUNT_BLOCKED:
|
|
66
|
+
case ApiErrorType.ACCOUNT_INACTIVE:
|
|
67
|
+
case 'Invalid time value':
|
|
68
|
+
case ApiErrorType.INVALID_TIME_VALUE:
|
|
57
69
|
statusCode = 403;
|
|
58
70
|
break;
|
|
59
71
|
|
|
@@ -63,6 +75,12 @@ function appErrorHandler(err, req, res, next) {
|
|
|
63
75
|
case 'Bad_Visitor_Token':
|
|
64
76
|
case 'Login_Required':
|
|
65
77
|
case 'Not_Authorized':
|
|
78
|
+
case ApiErrorType.VALIDATION_FAILURE:
|
|
79
|
+
case ApiErrorType.USED_TOKEN:
|
|
80
|
+
case ApiErrorType.WRONG_CREDENTIALS:
|
|
81
|
+
case ApiErrorType.BAD_VISITOR_TOKEN:
|
|
82
|
+
case ApiErrorType.LOGIN_REQUIRED:
|
|
83
|
+
case ApiErrorType.NOT_AUTHORIZED:
|
|
66
84
|
statusCode = 401;
|
|
67
85
|
break;
|
|
68
86
|
|
|
@@ -71,27 +89,41 @@ function appErrorHandler(err, req, res, next) {
|
|
|
71
89
|
case 'Transaction_Failed':
|
|
72
90
|
case 'Bad_Input':
|
|
73
91
|
case 'Input_Not_Uuid':
|
|
92
|
+
case ApiErrorType.RECORD_NOT_SAVED:
|
|
93
|
+
case ApiErrorType.UPDATE_FAILED:
|
|
94
|
+
case ApiErrorType.TRANSACTION_FAILED:
|
|
95
|
+
case ApiErrorType.BAD_INPUT:
|
|
96
|
+
case ApiErrorType.INPUT_NOT_UUID:
|
|
74
97
|
statusCode = 400;
|
|
75
98
|
break;
|
|
76
99
|
|
|
77
100
|
case 'Record_Exist':
|
|
101
|
+
case ApiErrorType.RECORD_EXIST:
|
|
78
102
|
statusCode = 409;
|
|
79
103
|
break;
|
|
80
104
|
|
|
81
105
|
case 'Record_NotFound':
|
|
106
|
+
case 'Not found':
|
|
107
|
+
case ApiErrorType.RECORD_NOT_FOUND:
|
|
108
|
+
case ApiErrorType.NOT_FOUND:
|
|
82
109
|
statusCode = 404;
|
|
83
110
|
break;
|
|
84
111
|
|
|
85
112
|
case 'File_Format_Not_Supported':
|
|
113
|
+
case ApiErrorType.FILE_FORMAT_NOT_SUPPORTED:
|
|
86
114
|
statusCode = 415;
|
|
87
115
|
break;
|
|
88
116
|
|
|
89
117
|
case 'File too large':
|
|
118
|
+
case ApiErrorType.FILE_TOO_LARGE:
|
|
90
119
|
statusCode = 413;
|
|
91
120
|
break;
|
|
92
121
|
|
|
93
|
-
case
|
|
94
|
-
|
|
122
|
+
case ApiErrorType.RECORD_SAVE_FAILURE:
|
|
123
|
+
case ApiErrorType.APPLICATION_ERROR:
|
|
124
|
+
case ApiErrorType.NETWORK_ERROR:
|
|
125
|
+
case ApiErrorType.UNEXPECTED_ERROR:
|
|
126
|
+
statusCode = 500;
|
|
95
127
|
break;
|
|
96
128
|
|
|
97
129
|
default:
|
|
@@ -114,7 +146,7 @@ function appErrorHandler(err, req, res, next) {
|
|
|
114
146
|
|
|
115
147
|
function throwAccountSuspendedError(params = {}) {
|
|
116
148
|
const error = new Error('Account_Suspended');
|
|
117
|
-
error.code =
|
|
149
|
+
error.code = ApiErrorType.ACCOUNT_SUSPENDED;
|
|
118
150
|
error.userMessage = params.userMessage ?? null;
|
|
119
151
|
error.details = params.details ?? null;
|
|
120
152
|
throw error;
|
|
@@ -122,7 +154,7 @@ function throwAccountSuspendedError(params = {}) {
|
|
|
122
154
|
|
|
123
155
|
function throwAccountBlockedError(params = {}) {
|
|
124
156
|
const error = new Error('Account_Blocked');
|
|
125
|
-
error.code =
|
|
157
|
+
error.code = ApiErrorType.ACCOUNT_BLOCKED;
|
|
126
158
|
error.userMessage = params.userMessage ?? null;
|
|
127
159
|
error.details = params.details ?? null;
|
|
128
160
|
throw error;
|
|
@@ -130,7 +162,7 @@ function throwAccountBlockedError(params = {}) {
|
|
|
130
162
|
|
|
131
163
|
function throwAccountInactiveError(params = {}) {
|
|
132
164
|
const error = new Error('Account_Inactive');
|
|
133
|
-
error.code =
|
|
165
|
+
error.code = ApiErrorType.ACCOUNT_INACTIVE;
|
|
134
166
|
error.userMessage = params.userMessage ?? null;
|
|
135
167
|
error.details = params.details ?? null;
|
|
136
168
|
throw error;
|
|
@@ -138,7 +170,7 @@ function throwAccountInactiveError(params = {}) {
|
|
|
138
170
|
|
|
139
171
|
function throwValidationFailureError(params = {}) {
|
|
140
172
|
const error = new Error('Validation_Failure');
|
|
141
|
-
error.code =
|
|
173
|
+
error.code = ApiErrorType.VALIDATION_FAILURE;
|
|
142
174
|
error.userMessage = params.userMessage ?? null;
|
|
143
175
|
error.details = params.details ?? null;
|
|
144
176
|
throw error;
|
|
@@ -146,7 +178,7 @@ function throwValidationFailureError(params = {}) {
|
|
|
146
178
|
|
|
147
179
|
function throwWrongCredentialsError(params = {}) {
|
|
148
180
|
const error = new Error('Wrong_Credentials');
|
|
149
|
-
error.code =
|
|
181
|
+
error.code = ApiErrorType.WRONG_CREDENTIALS;
|
|
150
182
|
error.userMessage = params.userMessage ?? null;
|
|
151
183
|
error.details = params.details ?? null;
|
|
152
184
|
throw error;
|
|
@@ -154,7 +186,7 @@ function throwWrongCredentialsError(params = {}) {
|
|
|
154
186
|
|
|
155
187
|
function throwRecordExistError(params = {}) {
|
|
156
188
|
const error = new Error('Record_Exist');
|
|
157
|
-
error.code =
|
|
189
|
+
error.code = ApiErrorType.RECORD_EXIST;
|
|
158
190
|
error.userMessage = params.userMessage ?? null;
|
|
159
191
|
error.details = params.details ?? null;
|
|
160
192
|
throw error;
|
|
@@ -162,7 +194,7 @@ function throwRecordExistError(params = {}) {
|
|
|
162
194
|
|
|
163
195
|
function throwRecordNotFoundError(params = {}) {
|
|
164
196
|
const error = new Error('Record_NotFound');
|
|
165
|
-
error.code =
|
|
197
|
+
error.code = ApiErrorType.RECORD_NOT_FOUND;
|
|
166
198
|
error.userMessage = params.userMessage ?? null;
|
|
167
199
|
error.details = params.details ?? null;
|
|
168
200
|
throw error;
|
|
@@ -170,7 +202,7 @@ function throwRecordNotFoundError(params = {}) {
|
|
|
170
202
|
|
|
171
203
|
function throwRecordNotSavedError(params = {}) {
|
|
172
204
|
const error = new Error('Record_NotSaved');
|
|
173
|
-
error.code =
|
|
205
|
+
error.code = ApiErrorType.RECORD_NOT_SAVED;
|
|
174
206
|
error.userMessage = params.userMessage ?? null;
|
|
175
207
|
error.details = params.details ?? null;
|
|
176
208
|
throw error;
|
|
@@ -178,7 +210,7 @@ function throwRecordNotSavedError(params = {}) {
|
|
|
178
210
|
|
|
179
211
|
function throwLoginRequiredError(params = {}) {
|
|
180
212
|
const error = new Error('Login_Required');
|
|
181
|
-
error.code =
|
|
213
|
+
error.code = ApiErrorType.LOGIN_REQUIRED;
|
|
182
214
|
error.userMessage = params.userMessage ?? null;
|
|
183
215
|
error.details = params.details ?? null;
|
|
184
216
|
throw error;
|
|
@@ -186,7 +218,7 @@ function throwLoginRequiredError(params = {}) {
|
|
|
186
218
|
|
|
187
219
|
function throwUpdateFailedError(params = {}) {
|
|
188
220
|
const error = new Error('Update_Failed');
|
|
189
|
-
error.code =
|
|
221
|
+
error.code = ApiErrorType.UPDATE_FAILED;
|
|
190
222
|
error.userMessage = params.userMessage ?? null;
|
|
191
223
|
error.details = params.details ?? null;
|
|
192
224
|
throw error;
|
|
@@ -194,7 +226,7 @@ function throwUpdateFailedError(params = {}) {
|
|
|
194
226
|
|
|
195
227
|
function throwTransactionFailedError(params = {}) {
|
|
196
228
|
const error = new Error('Transaction_Failed');
|
|
197
|
-
error.code =
|
|
229
|
+
error.code = ApiErrorType.TRANSACTION_FAILED;
|
|
198
230
|
error.userMessage = params.userMessage ?? null;
|
|
199
231
|
error.details = params.details ?? null;
|
|
200
232
|
throw error;
|
|
@@ -202,7 +234,7 @@ function throwTransactionFailedError(params = {}) {
|
|
|
202
234
|
|
|
203
235
|
function throwUsedTokenError(params = {}) {
|
|
204
236
|
const error = new Error('Used_Token');
|
|
205
|
-
error.code =
|
|
237
|
+
error.code = ApiErrorType.USED_TOKEN;
|
|
206
238
|
error.userMessage = params.userMessage ?? null;
|
|
207
239
|
error.details = params.details ?? null;
|
|
208
240
|
throw error;
|
|
@@ -210,7 +242,7 @@ function throwUsedTokenError(params = {}) {
|
|
|
210
242
|
|
|
211
243
|
function throwVisitorTokenError(params = {}) {
|
|
212
244
|
const error = new Error('Bad_Visitor_Token');
|
|
213
|
-
error.code =
|
|
245
|
+
error.code = ApiErrorType.BAD_VISITOR_TOKEN;
|
|
214
246
|
error.userMessage = params.userMessage ?? null;
|
|
215
247
|
error.details = params.details ?? null;
|
|
216
248
|
throw error;
|
|
@@ -218,7 +250,7 @@ function throwVisitorTokenError(params = {}) {
|
|
|
218
250
|
|
|
219
251
|
function throwFileFormatNotSupportedError(params = {}) {
|
|
220
252
|
const error = new Error('File_Format_Not_Supported');
|
|
221
|
-
error.code =
|
|
253
|
+
error.code = ApiErrorType.FILE_FORMAT_NOT_SUPPORTED;
|
|
222
254
|
error.userMessage = params.userMessage ?? null;
|
|
223
255
|
error.details = params.details ?? null;
|
|
224
256
|
throw error;
|
|
@@ -226,7 +258,7 @@ function throwFileFormatNotSupportedError(params = {}) {
|
|
|
226
258
|
|
|
227
259
|
function throwNotAuthorizedError(params = {}) {
|
|
228
260
|
const error = new Error('Not_Authorized');
|
|
229
|
-
error.code =
|
|
261
|
+
error.code = ApiErrorType.NOT_AUTHORIZED;
|
|
230
262
|
error.userMessage = params.userMessage ?? null;
|
|
231
263
|
error.details = params.details ?? null;
|
|
232
264
|
throw error;
|
|
@@ -234,7 +266,7 @@ function throwNotAuthorizedError(params = {}) {
|
|
|
234
266
|
|
|
235
267
|
function throwBadInputError(params = {}) {
|
|
236
268
|
const error = new Error('Bad_Input');
|
|
237
|
-
error.code =
|
|
269
|
+
error.code = ApiErrorType.BAD_INPUT;
|
|
238
270
|
error.userMessage = params.userMessage ?? null;
|
|
239
271
|
error.details = params.details ?? null;
|
|
240
272
|
throw error;
|
|
@@ -242,7 +274,7 @@ function throwBadInputError(params = {}) {
|
|
|
242
274
|
|
|
243
275
|
function throwInputNotUuidError(params = {}) {
|
|
244
276
|
const error = new Error('Input_Not_Uuid');
|
|
245
|
-
error.code =
|
|
277
|
+
error.code = ApiErrorType.INPUT_NOT_UUID;
|
|
246
278
|
error.userMessage = params.userMessage ?? null;
|
|
247
279
|
error.details = params.details ?? null;
|
|
248
280
|
throw error;
|
|
@@ -250,7 +282,7 @@ function throwInputNotUuidError(params = {}) {
|
|
|
250
282
|
|
|
251
283
|
function throwFileTooLargeError(params = {}) {
|
|
252
284
|
const error = new Error('File too large');
|
|
253
|
-
error.code =
|
|
285
|
+
error.code = ApiErrorType.FILE_TOO_LARGE;
|
|
254
286
|
error.userMessage = params.userMessage ?? null;
|
|
255
287
|
error.details = params.details ?? null;
|
|
256
288
|
throw error;
|
|
@@ -258,7 +290,47 @@ function throwFileTooLargeError(params = {}) {
|
|
|
258
290
|
|
|
259
291
|
function throwInvalidTimeValueError(params = {}) {
|
|
260
292
|
const error = new Error('Invalid time value');
|
|
261
|
-
error.code =
|
|
293
|
+
error.code = ApiErrorType.INVALID_TIME_VALUE;
|
|
294
|
+
error.userMessage = params.userMessage ?? null;
|
|
295
|
+
error.details = params.details ?? null;
|
|
296
|
+
throw error;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
function throwNotFoundError(params = {}) {
|
|
300
|
+
const error = new Error('Not found');
|
|
301
|
+
error.code = ApiErrorType.NOT_FOUND;
|
|
302
|
+
error.userMessage = params.userMessage ?? null;
|
|
303
|
+
error.details = params.details ?? null;
|
|
304
|
+
throw error;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
function throwRecordSaveFailureError(params = {}) {
|
|
308
|
+
const error = new Error('Record_Save_Failure');
|
|
309
|
+
error.code = ApiErrorType.RECORD_SAVE_FAILURE;
|
|
310
|
+
error.userMessage = params.userMessage ?? null;
|
|
311
|
+
error.details = params.details ?? null;
|
|
312
|
+
throw error;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
function throwApplicationError(params = {}) {
|
|
316
|
+
const error = new Error('Application_Error');
|
|
317
|
+
error.code = ApiErrorType.APPLICATION_ERROR;
|
|
318
|
+
error.userMessage = params.userMessage ?? null;
|
|
319
|
+
error.details = params.details ?? null;
|
|
320
|
+
throw error;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
function throwNetworkError(params = {}) {
|
|
324
|
+
const error = new Error('Network_Error');
|
|
325
|
+
error.code = ApiErrorType.NETWORK_ERROR;
|
|
326
|
+
error.userMessage = params.userMessage ?? null;
|
|
327
|
+
error.details = params.details ?? null;
|
|
328
|
+
throw error;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
function throwUnexpectedError(params = {}) {
|
|
332
|
+
const error = new Error('Unexpected_Error');
|
|
333
|
+
error.code = ApiErrorType.UNEXPECTED_ERROR;
|
|
262
334
|
error.userMessage = params.userMessage ?? null;
|
|
263
335
|
error.details = params.details ?? null;
|
|
264
336
|
throw error;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Standard API error codes used across the system.
|
|
5
|
+
* This should be kept in sync between the backend and frontend.
|
|
6
|
+
*/
|
|
7
|
+
const ApiErrorType = {
|
|
8
|
+
// Common codes
|
|
9
|
+
VALIDATION_FAILURE: 'VALIDATION_FAILURE',
|
|
10
|
+
WRONG_CREDENTIALS: 'WRONG_CREDENTIALS',
|
|
11
|
+
RECORD_NOT_FOUND: 'RECORD_NOT_FOUND',
|
|
12
|
+
RECORD_NOT_SAVED: 'RECORD_NOT_SAVED',
|
|
13
|
+
RECORD_SAVE_FAILURE: 'RECORD_SAVE_FAILURE',
|
|
14
|
+
APPLICATION_ERROR: 'APPLICATION_ERROR',
|
|
15
|
+
NOT_FOUND: 'NOT_FOUND',
|
|
16
|
+
LOGIN_REQUIRED: 'LOGIN_REQUIRED',
|
|
17
|
+
NETWORK_ERROR: 'NETWORK_ERROR',
|
|
18
|
+
UNEXPECTED_ERROR: 'UNEXPECTED_ERROR',
|
|
19
|
+
ACCOUNT_SUSPENDED: 'ACCOUNT_SUSPENDED',
|
|
20
|
+
ACCOUNT_BLOCKED: 'ACCOUNT_BLOCKED',
|
|
21
|
+
ACCOUNT_INACTIVE: 'ACCOUNT_INACTIVE',
|
|
22
|
+
|
|
23
|
+
// Additional codes from appErrorHandlers
|
|
24
|
+
RECORD_EXIST: 'RECORD_EXIST',
|
|
25
|
+
UPDATE_FAILED: 'UPDATE_FAILED',
|
|
26
|
+
TRANSACTION_FAILED: 'TRANSACTION_FAILED',
|
|
27
|
+
USED_TOKEN: 'USED_TOKEN',
|
|
28
|
+
BAD_VISITOR_TOKEN: 'BAD_VISITOR_TOKEN',
|
|
29
|
+
FILE_FORMAT_NOT_SUPPORTED: 'FILE_FORMAT_NOT_SUPPORTED',
|
|
30
|
+
NOT_AUTHORIZED: 'NOT_AUTHORIZED',
|
|
31
|
+
BAD_INPUT: 'BAD_INPUT',
|
|
32
|
+
INPUT_NOT_UUID: 'INPUT_NOT_UUID',
|
|
33
|
+
FILE_TOO_LARGE: 'FILE_TOO_LARGE',
|
|
34
|
+
INVALID_TIME_VALUE: 'INVALID_TIME_VALUE'
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
module.exports = {
|
|
38
|
+
ApiErrorType
|
|
39
|
+
};
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { ApiErrorType } = require('./errorConstants');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Extracts error message from standardized error data.
|
|
7
|
+
*
|
|
8
|
+
* @param {Object} errorData - The error data from API or legacy source.
|
|
9
|
+
* @param {Function} t - Translation function (key => translated string).
|
|
10
|
+
* @returns {string} The formatted error message.
|
|
11
|
+
*/
|
|
12
|
+
function getApiErrorMessage(errorData, t) {
|
|
13
|
+
if (!errorData) {
|
|
14
|
+
return t('errors.unexpected_error');
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Try to get message from direct field or nested error object
|
|
18
|
+
let message;
|
|
19
|
+
|
|
20
|
+
// Prefer nested error message if available
|
|
21
|
+
if (
|
|
22
|
+
errorData.error &&
|
|
23
|
+
typeof errorData.error === 'object' &&
|
|
24
|
+
errorData.error !== null &&
|
|
25
|
+
errorData.error.message
|
|
26
|
+
) {
|
|
27
|
+
message = errorData.error.message;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Fallback to top-level message
|
|
31
|
+
if (!message && typeof errorData.message === 'string' && errorData.message) {
|
|
32
|
+
message = errorData.message;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (message) {
|
|
36
|
+
return message;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Try to get error code
|
|
40
|
+
let errorCode;
|
|
41
|
+
if (errorData.error) {
|
|
42
|
+
if (typeof errorData.error === 'string') {
|
|
43
|
+
errorCode = errorData.error;
|
|
44
|
+
} else if (
|
|
45
|
+
typeof errorData.error === 'object' &&
|
|
46
|
+
errorData.error !== null &&
|
|
47
|
+
errorData.error.code
|
|
48
|
+
) {
|
|
49
|
+
errorCode = errorData.error.code;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (!errorCode) {
|
|
54
|
+
return t('errors.unexpected_error');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const code = errorCode.toLowerCase();
|
|
58
|
+
|
|
59
|
+
switch (code) {
|
|
60
|
+
case ApiErrorType.VALIDATION_FAILURE.toLowerCase():
|
|
61
|
+
return t('errors.validation_failure');
|
|
62
|
+
|
|
63
|
+
case ApiErrorType.WRONG_CREDENTIALS.toLowerCase():
|
|
64
|
+
return t('errors.wrong_credentials');
|
|
65
|
+
|
|
66
|
+
case ApiErrorType.RECORD_NOT_FOUND.toLowerCase():
|
|
67
|
+
return t('errors.record_not_found');
|
|
68
|
+
|
|
69
|
+
case ApiErrorType.RECORD_NOT_SAVED.toLowerCase():
|
|
70
|
+
return t('errors.record_not_saved');
|
|
71
|
+
|
|
72
|
+
case ApiErrorType.RECORD_SAVE_FAILURE.toLowerCase():
|
|
73
|
+
return t('errors.record_save_failure');
|
|
74
|
+
|
|
75
|
+
case ApiErrorType.APPLICATION_ERROR.toLowerCase():
|
|
76
|
+
return t('errors.application_error');
|
|
77
|
+
|
|
78
|
+
case ApiErrorType.NOT_FOUND.toLowerCase():
|
|
79
|
+
return t('errors.not_found');
|
|
80
|
+
|
|
81
|
+
case ApiErrorType.LOGIN_REQUIRED.toLowerCase():
|
|
82
|
+
return t('errors.login_required');
|
|
83
|
+
|
|
84
|
+
case ApiErrorType.NETWORK_ERROR.toLowerCase():
|
|
85
|
+
return t('errors.network_error');
|
|
86
|
+
|
|
87
|
+
case ApiErrorType.ACCOUNT_SUSPENDED.toLowerCase():
|
|
88
|
+
return t('errors.account_suspended');
|
|
89
|
+
|
|
90
|
+
case ApiErrorType.ACCOUNT_BLOCKED.toLowerCase():
|
|
91
|
+
return t('errors.account_blocked');
|
|
92
|
+
|
|
93
|
+
case ApiErrorType.ACCOUNT_INACTIVE.toLowerCase():
|
|
94
|
+
return t('errors.account_inactive');
|
|
95
|
+
|
|
96
|
+
case ApiErrorType.RECORD_EXIST.toLowerCase():
|
|
97
|
+
return t('errors.record_exist');
|
|
98
|
+
|
|
99
|
+
case ApiErrorType.UPDATE_FAILED.toLowerCase():
|
|
100
|
+
return t('errors.update_failed');
|
|
101
|
+
|
|
102
|
+
case ApiErrorType.TRANSACTION_FAILED.toLowerCase():
|
|
103
|
+
return t('errors.transaction_failed');
|
|
104
|
+
|
|
105
|
+
case ApiErrorType.USED_TOKEN.toLowerCase():
|
|
106
|
+
return t('errors.used_token');
|
|
107
|
+
|
|
108
|
+
case ApiErrorType.BAD_VISITOR_TOKEN.toLowerCase():
|
|
109
|
+
return t('errors.bad_visitor_token');
|
|
110
|
+
|
|
111
|
+
case ApiErrorType.FILE_FORMAT_NOT_SUPPORTED.toLowerCase():
|
|
112
|
+
return t('errors.file_format_not_supported');
|
|
113
|
+
|
|
114
|
+
case ApiErrorType.NOT_AUTHORIZED.toLowerCase():
|
|
115
|
+
return t('errors.not_authorized');
|
|
116
|
+
|
|
117
|
+
case ApiErrorType.BAD_INPUT.toLowerCase():
|
|
118
|
+
return t('errors.bad_input');
|
|
119
|
+
|
|
120
|
+
case ApiErrorType.INPUT_NOT_UUID.toLowerCase():
|
|
121
|
+
return t('errors.input_not_uuid');
|
|
122
|
+
|
|
123
|
+
case ApiErrorType.FILE_TOO_LARGE.toLowerCase():
|
|
124
|
+
return t('errors.file_too_large');
|
|
125
|
+
|
|
126
|
+
case ApiErrorType.INVALID_TIME_VALUE.toLowerCase():
|
|
127
|
+
return t('errors.invalid_time_value');
|
|
128
|
+
|
|
129
|
+
case ApiErrorType.UNEXPECTED_ERROR.toLowerCase():
|
|
130
|
+
default:
|
|
131
|
+
return t('errors.unexpected_error');
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
module.exports = {
|
|
136
|
+
getApiErrorMessage
|
|
137
|
+
};
|
|
@@ -1,4 +1,14 @@
|
|
|
1
|
-
const
|
|
1
|
+
const getUuid = () => {
|
|
2
|
+
try {
|
|
3
|
+
const crypto = require('crypto');
|
|
4
|
+
if (crypto.randomUUID) {
|
|
5
|
+
return crypto.randomUUID();
|
|
6
|
+
}
|
|
7
|
+
return '00000000-0000-0000-0000-000000000000';
|
|
8
|
+
} catch (e) {
|
|
9
|
+
return '00000000-0000-0000-0000-000000000000';
|
|
10
|
+
}
|
|
11
|
+
};
|
|
2
12
|
|
|
3
13
|
/**
|
|
4
14
|
* Express middleware to generate and attach request context.
|
|
@@ -13,8 +23,9 @@ const { randomUUID } = require('node:crypto');
|
|
|
13
23
|
* @param {import('express').NextFunction} next
|
|
14
24
|
*/
|
|
15
25
|
const requestContext = (req, res, next) => {
|
|
16
|
-
|
|
17
|
-
req.
|
|
26
|
+
const requestId = getUuid();
|
|
27
|
+
req.requestId = requestId;
|
|
28
|
+
req.traceId = req.headers['x-trace-id'] || requestId;
|
|
18
29
|
|
|
19
30
|
const appId = req.headers['x-app-id'];
|
|
20
31
|
const ip = req.ip || req.headers['x-forwarded-for'] || req.socket.remoteAddress;
|