@naturalcycles/backend-lib 4.17.0 → 4.17.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/dist/admin/adminMiddleware.js +1 -1
- package/dist/admin/base.admin.service.js +5 -5
- package/dist/admin/secureHeaderMiddleware.js +2 -2
- package/dist/deploy/deployHealthCheck.js +1 -1
- package/dist/server/bodyParserTimeoutMiddleware.d.ts +1 -1
- package/dist/server/bodyParserTimeoutMiddleware.js +4 -4
- package/dist/server/genericErrorMiddleware.d.ts +2 -2
- package/dist/server/genericErrorMiddleware.js +3 -3
- package/dist/server/reqValidationMiddleware.js +2 -2
- package/dist/server/requestTimeoutMiddleware.d.ts +4 -3
- package/dist/server/requestTimeoutMiddleware.js +4 -4
- package/dist/server/validateMiddleware.js +2 -2
- package/dist/server/zodValidateMiddleware.js +2 -2
- package/package.json +1 -1
- package/src/admin/adminMiddleware.ts +2 -2
- package/src/admin/base.admin.service.ts +10 -13
- package/src/admin/secureHeaderMiddleware.ts +3 -3
- package/src/deploy/deployHealthCheck.ts +1 -1
- package/src/server/bodyParserTimeoutMiddleware.ts +6 -6
- package/src/server/genericErrorMiddleware.ts +11 -10
- package/src/server/reqValidationMiddleware.ts +3 -3
- package/src/server/requestTimeoutMiddleware.ts +8 -8
- package/src/server/validateMiddleware.ts +3 -3
- package/src/server/zodValidateMiddleware.ts +3 -9
|
@@ -27,7 +27,7 @@ function requireAdminPermissions(adminService, reqPermissions = [], cfg = {}) {
|
|
|
27
27
|
return next();
|
|
28
28
|
}
|
|
29
29
|
catch (err) {
|
|
30
|
-
if (err instanceof js_lib_1.
|
|
30
|
+
if (err instanceof js_lib_1.AppError && err.data.adminAuthRequired) {
|
|
31
31
|
// Redirect to login.html
|
|
32
32
|
const href = `${loginHtmlPath}?${autoLogin ? 'autoLogin=1&' : ''}returnUrl=\${encodeURIComponent(location.href)}${apiHost ? '&apiHost=' + apiHost : ''}`;
|
|
33
33
|
res.status(401).send(getLoginHtmlRedirect(href));
|
|
@@ -116,9 +116,9 @@ class BaseAdminService {
|
|
|
116
116
|
const adminToken = await this.getAdminToken(req);
|
|
117
117
|
const email = await this.getEmailByToken(req, adminToken);
|
|
118
118
|
if (!email) {
|
|
119
|
-
throw new js_lib_1.
|
|
119
|
+
throw new js_lib_1.AppError('adminToken required', {
|
|
120
120
|
adminAuthRequired: true,
|
|
121
|
-
|
|
121
|
+
backendResponseStatusCode: 401,
|
|
122
122
|
userFriendly: true,
|
|
123
123
|
});
|
|
124
124
|
}
|
|
@@ -142,10 +142,10 @@ class BaseAdminService {
|
|
|
142
142
|
}
|
|
143
143
|
}
|
|
144
144
|
if (!granted) {
|
|
145
|
-
throw new js_lib_1.
|
|
145
|
+
throw new js_lib_1.AppError(`Admin permissions required: [${reqPermissions.join(', ')}]`, {
|
|
146
146
|
adminPermissionsRequired: reqPermissions,
|
|
147
147
|
email,
|
|
148
|
-
|
|
148
|
+
backendResponseStatusCode: 403,
|
|
149
149
|
userFriendly: true,
|
|
150
150
|
});
|
|
151
151
|
}
|
|
@@ -175,7 +175,7 @@ class BaseAdminService {
|
|
|
175
175
|
const token = req.header('authentication');
|
|
176
176
|
(0, js_lib_1._assert)(token, `401 Unauthenticated`, {
|
|
177
177
|
userFriendly: true,
|
|
178
|
-
|
|
178
|
+
backendResponseStatusCode: 401,
|
|
179
179
|
});
|
|
180
180
|
let maxAge = 1000 * 60 * 60 * 24 * 30; // 30 days
|
|
181
181
|
// Special case
|
|
@@ -23,8 +23,8 @@ function requireSecureHeaderOrAdmin(cfg, reqPermissions) {
|
|
|
23
23
|
if (providedHeader) {
|
|
24
24
|
if (!secureHeaderValue || providedHeader === secureHeaderValue)
|
|
25
25
|
return next();
|
|
26
|
-
return next(new js_lib_1.
|
|
27
|
-
|
|
26
|
+
return next(new js_lib_1.AppError('secureHeader or adminToken is required', {
|
|
27
|
+
backendResponseStatusCode: 401,
|
|
28
28
|
adminAuthRequired: true,
|
|
29
29
|
}));
|
|
30
30
|
}
|
|
@@ -84,7 +84,7 @@ async function deployHealthCheck(url, opt = {}) {
|
|
|
84
84
|
attempt++;
|
|
85
85
|
console.log([`>>`, (0, colors_1.dimGrey)(url), (0, node_util_1.inspect)({ attempt }, inspectOpt)].join(' '));
|
|
86
86
|
const started = Date.now();
|
|
87
|
-
const { err, fetchResponse } = await fetcher.
|
|
87
|
+
const { err, fetchResponse } = await fetcher.doFetch(url, {
|
|
88
88
|
mode: 'json',
|
|
89
89
|
timeoutSeconds: timeoutSec,
|
|
90
90
|
retry: {
|
|
@@ -8,9 +8,9 @@ const code = 'BODY_PARSER_TIMEOUT';
|
|
|
8
8
|
* Should be called BEFORE bodyParser
|
|
9
9
|
*/
|
|
10
10
|
function bodyParserTimeoutMiddleware(cfg = {}) {
|
|
11
|
-
const { timeoutSeconds,
|
|
11
|
+
const { timeoutSeconds, backendResponseStatusCode, httpStatus } = {
|
|
12
12
|
timeoutSeconds: 10,
|
|
13
|
-
|
|
13
|
+
backendResponseStatusCode: 400,
|
|
14
14
|
httpStatus: 'Timeout reading request input',
|
|
15
15
|
...cfg,
|
|
16
16
|
};
|
|
@@ -21,9 +21,9 @@ function bodyParserTimeoutMiddleware(cfg = {}) {
|
|
|
21
21
|
if (req.bodyParserTimeout)
|
|
22
22
|
clearTimeout(req.bodyParserTimeout);
|
|
23
23
|
req.bodyParserTimeout = setTimeout(() => {
|
|
24
|
-
(0, index_1.respondWithError)(req, res, new js_lib_1.
|
|
24
|
+
(0, index_1.respondWithError)(req, res, new js_lib_1.AppError(httpStatus, {
|
|
25
25
|
code,
|
|
26
|
-
|
|
26
|
+
backendResponseStatusCode,
|
|
27
27
|
// userFriendly: true, // no, cause this error is not expected
|
|
28
28
|
}));
|
|
29
29
|
}, timeout);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ErrorObject
|
|
1
|
+
import { ErrorObject } from '@naturalcycles/js-lib';
|
|
2
2
|
import { SentrySharedService } from '../sentry/sentry.shared.service';
|
|
3
3
|
import { BackendErrorRequestHandler, BackendRequest, BackendResponse } from './server.model';
|
|
4
4
|
export interface GenericErrorMiddlewareCfg {
|
|
@@ -12,7 +12,7 @@ export interface GenericErrorMiddlewareCfg {
|
|
|
12
12
|
* Generic hook that can be used to **mutate** errors before they are returned to client.
|
|
13
13
|
* This function does not affect data sent to sentry.
|
|
14
14
|
*/
|
|
15
|
-
formatError?: (err: ErrorObject
|
|
15
|
+
formatError?: (err: ErrorObject) => void;
|
|
16
16
|
}
|
|
17
17
|
/**
|
|
18
18
|
* Generic error handler.
|
|
@@ -52,12 +52,12 @@ function respondWithError(req, res, err) {
|
|
|
52
52
|
if (!includeErrorStack)
|
|
53
53
|
delete httpError.stack;
|
|
54
54
|
httpError.data.errorId = errorId;
|
|
55
|
-
httpError.data.
|
|
55
|
+
httpError.data.backendResponseStatusCode ||= 500; // default to 500
|
|
56
56
|
httpError.data.headersSent = headersSent || undefined;
|
|
57
57
|
httpError.data.report ||= undefined; // set to undefined if false
|
|
58
58
|
(0, js_lib_1._filterUndefinedValues)(httpError.data, true);
|
|
59
59
|
formatError?.(httpError); // Mutates
|
|
60
|
-
res.status(httpError.data.
|
|
60
|
+
res.status(httpError.data.backendResponseStatusCode).json({
|
|
61
61
|
error: httpError,
|
|
62
62
|
});
|
|
63
63
|
}
|
|
@@ -76,5 +76,5 @@ function shouldReportToSentry(err) {
|
|
|
76
76
|
// If no httpCode - report
|
|
77
77
|
// if httpCode >= 500 - report
|
|
78
78
|
// Otherwise - report, unless !reportOnly5xx is set
|
|
79
|
-
return !reportOnly5xx || !e.data.
|
|
79
|
+
return (!reportOnly5xx || !e.data.backendResponseStatusCode || e.data.backendResponseStatusCode >= 500);
|
|
80
80
|
}
|
|
@@ -15,8 +15,8 @@ function reqValidation(reqProperty, schema, opt = {}) {
|
|
|
15
15
|
error.data.joiValidationErrorItems.length = 0; // clears the array
|
|
16
16
|
delete error.data.annotation;
|
|
17
17
|
}
|
|
18
|
-
return next(new js_lib_1.
|
|
19
|
-
|
|
18
|
+
return next(new js_lib_1.AppError(error.message, {
|
|
19
|
+
backendResponseStatusCode: 400,
|
|
20
20
|
report,
|
|
21
21
|
...error.data,
|
|
22
22
|
}));
|
|
@@ -1,13 +1,14 @@
|
|
|
1
|
+
import { NumberOfSeconds } from '@naturalcycles/js-lib';
|
|
1
2
|
import { BackendRequest, BackendRequestHandler, BackendResponse } from '../index';
|
|
2
3
|
export interface RequestTimeoutMiddlewareCfg {
|
|
3
4
|
/**
|
|
4
5
|
* @default 120
|
|
5
6
|
*/
|
|
6
|
-
timeoutSeconds?:
|
|
7
|
+
timeoutSeconds?: NumberOfSeconds;
|
|
7
8
|
/**
|
|
8
9
|
* @default 503
|
|
9
10
|
*/
|
|
10
|
-
|
|
11
|
+
backendResponseStatusCode?: number;
|
|
11
12
|
/**
|
|
12
13
|
* @default 'Request timed out'
|
|
13
14
|
*/
|
|
@@ -18,7 +19,7 @@ export interface CustomRequestTimeoutMiddlewareCfg {
|
|
|
18
19
|
/**
|
|
19
20
|
* @default 120
|
|
20
21
|
*/
|
|
21
|
-
timeoutSeconds?:
|
|
22
|
+
timeoutSeconds?: NumberOfSeconds;
|
|
22
23
|
}
|
|
23
24
|
/**
|
|
24
25
|
* Example:
|
|
@@ -6,13 +6,13 @@ const index_1 = require("../index");
|
|
|
6
6
|
const code = 'REQUEST_TIMEOUT';
|
|
7
7
|
const REQUEST_TIMEOUT_QUERY_KEY = 'requestTimeout';
|
|
8
8
|
function requestTimeoutMiddleware(cfg = {}) {
|
|
9
|
-
const { timeoutSeconds: defTimeoutSeconds,
|
|
9
|
+
const { timeoutSeconds: defTimeoutSeconds, backendResponseStatusCode, httpErrorMessage, } = {
|
|
10
10
|
// Considerations about the default value of the timeout.
|
|
11
11
|
// Ideally the default value here would be HIGHER than the default timeout for getGot (in nodejs-lib),
|
|
12
12
|
// so, cross-service communication has a chance to fail SOONER than server times out,
|
|
13
13
|
// so, proper error from exact service is shown, rather than generic "503 request timed out"
|
|
14
14
|
timeoutSeconds: 120,
|
|
15
|
-
|
|
15
|
+
backendResponseStatusCode: 503,
|
|
16
16
|
httpErrorMessage: 'Request timed out',
|
|
17
17
|
...cfg,
|
|
18
18
|
};
|
|
@@ -27,9 +27,9 @@ function requestTimeoutMiddleware(cfg = {}) {
|
|
|
27
27
|
req.requestTimeout = setTimeout(() => {
|
|
28
28
|
const endpoint = (0, index_1.getRequestEndpoint)(req);
|
|
29
29
|
const msg = `${httpErrorMessage} on ${endpoint} after ${(0, js_lib_1._ms)(timeoutSeconds * 1000)}`;
|
|
30
|
-
(0, index_1.respondWithError)(req, res, new js_lib_1.
|
|
30
|
+
(0, index_1.respondWithError)(req, res, new js_lib_1.AppError(msg, {
|
|
31
31
|
code,
|
|
32
|
-
|
|
32
|
+
backendResponseStatusCode,
|
|
33
33
|
endpoint,
|
|
34
34
|
timeoutSeconds,
|
|
35
35
|
// userFriendly: true, // no, cause this error is not expected
|
|
@@ -35,8 +35,8 @@ function validateObject(prop, schema, opt = {}) {
|
|
|
35
35
|
redact(opt.redactPaths, req[prop], error);
|
|
36
36
|
error.data.errors.length = 0; // clears the array
|
|
37
37
|
}
|
|
38
|
-
return next(new js_lib_1.
|
|
39
|
-
|
|
38
|
+
return next(new js_lib_1.AppError(error.message, {
|
|
39
|
+
backendResponseStatusCode: 400,
|
|
40
40
|
report,
|
|
41
41
|
...error.data,
|
|
42
42
|
}));
|
|
@@ -20,8 +20,8 @@ function zodReqValidate(prop, schema, opt = {}) {
|
|
|
20
20
|
if (opt.redactPaths) {
|
|
21
21
|
redact(opt.redactPaths, req[prop], error);
|
|
22
22
|
}
|
|
23
|
-
return next(new js_lib_1.
|
|
24
|
-
|
|
23
|
+
return next(new js_lib_1.AppError(error.message, {
|
|
24
|
+
backendResponseStatusCode: 400,
|
|
25
25
|
report,
|
|
26
26
|
}));
|
|
27
27
|
};
|
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as fs from 'node:fs'
|
|
2
|
-
import {
|
|
2
|
+
import { _memoFn, AppError } from '@naturalcycles/js-lib'
|
|
3
3
|
import * as ejs from 'ejs'
|
|
4
4
|
import { BackendRequestHandler } from '../server/server.model'
|
|
5
5
|
import { BaseAdminService } from './base.admin.service'
|
|
@@ -62,7 +62,7 @@ export function requireAdminPermissions(
|
|
|
62
62
|
await adminService.requirePermissions(req, reqPermissions)
|
|
63
63
|
return next()
|
|
64
64
|
} catch (err) {
|
|
65
|
-
if (err instanceof
|
|
65
|
+
if (err instanceof AppError && err.data.adminAuthRequired) {
|
|
66
66
|
// Redirect to login.html
|
|
67
67
|
const href = `${loginHtmlPath}?${
|
|
68
68
|
autoLogin ? 'autoLogin=1&' : ''
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { _assert,
|
|
1
|
+
import { _assert, AppError } from '@naturalcycles/js-lib'
|
|
2
2
|
import { dimGrey, green, red } from '@naturalcycles/nodejs-lib/dist/colors'
|
|
3
3
|
import type * as FirebaseAdmin from 'firebase-admin'
|
|
4
4
|
import { BackendRequest, BackendRequestHandler } from '../server/server.model'
|
|
@@ -176,9 +176,9 @@ export class BaseAdminService {
|
|
|
176
176
|
const email = await this.getEmailByToken(req, adminToken)
|
|
177
177
|
|
|
178
178
|
if (!email) {
|
|
179
|
-
throw new
|
|
179
|
+
throw new AppError('adminToken required', {
|
|
180
180
|
adminAuthRequired: true,
|
|
181
|
-
|
|
181
|
+
backendResponseStatusCode: 401,
|
|
182
182
|
userFriendly: true,
|
|
183
183
|
})
|
|
184
184
|
}
|
|
@@ -203,15 +203,12 @@ export class BaseAdminService {
|
|
|
203
203
|
}
|
|
204
204
|
|
|
205
205
|
if (!granted) {
|
|
206
|
-
throw new
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
userFriendly: true,
|
|
213
|
-
},
|
|
214
|
-
)
|
|
206
|
+
throw new AppError(`Admin permissions required: [${reqPermissions.join(', ')}]`, {
|
|
207
|
+
adminPermissionsRequired: reqPermissions,
|
|
208
|
+
email,
|
|
209
|
+
backendResponseStatusCode: 403,
|
|
210
|
+
userFriendly: true,
|
|
211
|
+
})
|
|
215
212
|
}
|
|
216
213
|
|
|
217
214
|
return {
|
|
@@ -251,7 +248,7 @@ export class BaseAdminService {
|
|
|
251
248
|
const token = req.header('authentication')
|
|
252
249
|
_assert(token, `401 Unauthenticated`, {
|
|
253
250
|
userFriendly: true,
|
|
254
|
-
|
|
251
|
+
backendResponseStatusCode: 401,
|
|
255
252
|
})
|
|
256
253
|
|
|
257
254
|
let maxAge = 1000 * 60 * 60 * 24 * 30 // 30 days
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { AppError } from '@naturalcycles/js-lib'
|
|
2
2
|
import { BackendRequestHandler } from '../server/server.model'
|
|
3
3
|
import { AdminMiddleware, RequireAdminCfg, requireAdminPermissions } from './adminMiddleware'
|
|
4
4
|
import { BaseAdminService } from './base.admin.service'
|
|
@@ -44,8 +44,8 @@ function requireSecureHeaderOrAdmin(
|
|
|
44
44
|
if (!secureHeaderValue || providedHeader === secureHeaderValue) return next()
|
|
45
45
|
|
|
46
46
|
return next(
|
|
47
|
-
new
|
|
48
|
-
|
|
47
|
+
new AppError('secureHeader or adminToken is required', {
|
|
48
|
+
backendResponseStatusCode: 401,
|
|
49
49
|
adminAuthRequired: true,
|
|
50
50
|
}),
|
|
51
51
|
)
|
|
@@ -126,7 +126,7 @@ export async function deployHealthCheck(
|
|
|
126
126
|
|
|
127
127
|
const started = Date.now()
|
|
128
128
|
|
|
129
|
-
const { err, fetchResponse } = await fetcher.
|
|
129
|
+
const { err, fetchResponse } = await fetcher.doFetch(url, {
|
|
130
130
|
mode: 'json',
|
|
131
131
|
timeoutSeconds: timeoutSec,
|
|
132
132
|
retry: {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { AppError } from '@naturalcycles/js-lib'
|
|
2
2
|
import { BackendRequestHandler, respondWithError } from '../index'
|
|
3
3
|
|
|
4
4
|
export interface BodyParserTimeoutMiddlewareCfg {
|
|
@@ -10,7 +10,7 @@ export interface BodyParserTimeoutMiddlewareCfg {
|
|
|
10
10
|
/**
|
|
11
11
|
* @default 400
|
|
12
12
|
*/
|
|
13
|
-
|
|
13
|
+
backendResponseStatusCode?: number
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
16
|
* @default 'Timeout reading request input'
|
|
@@ -26,9 +26,9 @@ const code = 'BODY_PARSER_TIMEOUT'
|
|
|
26
26
|
export function bodyParserTimeoutMiddleware(
|
|
27
27
|
cfg: BodyParserTimeoutMiddlewareCfg = {},
|
|
28
28
|
): BackendRequestHandler {
|
|
29
|
-
const { timeoutSeconds,
|
|
29
|
+
const { timeoutSeconds, backendResponseStatusCode, httpStatus } = {
|
|
30
30
|
timeoutSeconds: 10,
|
|
31
|
-
|
|
31
|
+
backendResponseStatusCode: 400,
|
|
32
32
|
httpStatus: 'Timeout reading request input',
|
|
33
33
|
...cfg,
|
|
34
34
|
}
|
|
@@ -44,9 +44,9 @@ export function bodyParserTimeoutMiddleware(
|
|
|
44
44
|
respondWithError(
|
|
45
45
|
req,
|
|
46
46
|
res,
|
|
47
|
-
new
|
|
47
|
+
new AppError(httpStatus, {
|
|
48
48
|
code,
|
|
49
|
-
|
|
49
|
+
backendResponseStatusCode,
|
|
50
50
|
// userFriendly: true, // no, cause this error is not expected
|
|
51
51
|
}),
|
|
52
52
|
)
|
|
@@ -2,10 +2,9 @@ import {
|
|
|
2
2
|
_anyToError,
|
|
3
3
|
_errorToErrorObject,
|
|
4
4
|
_filterUndefinedValues,
|
|
5
|
+
AppError,
|
|
6
|
+
BackendErrorResponseObject,
|
|
5
7
|
ErrorObject,
|
|
6
|
-
HttpError,
|
|
7
|
-
HttpErrorData,
|
|
8
|
-
HttpErrorResponse,
|
|
9
8
|
} from '@naturalcycles/js-lib'
|
|
10
9
|
import { SentrySharedService } from '../sentry/sentry.shared.service'
|
|
11
10
|
import { BackendErrorRequestHandler, BackendRequest, BackendResponse } from './server.model'
|
|
@@ -23,7 +22,7 @@ export interface GenericErrorMiddlewareCfg {
|
|
|
23
22
|
* Generic hook that can be used to **mutate** errors before they are returned to client.
|
|
24
23
|
* This function does not affect data sent to sentry.
|
|
25
24
|
*/
|
|
26
|
-
formatError?: (err: ErrorObject
|
|
25
|
+
formatError?: (err: ErrorObject) => void
|
|
27
26
|
}
|
|
28
27
|
|
|
29
28
|
const { APP_ENV } = process.env
|
|
@@ -82,24 +81,24 @@ export function respondWithError(req: BackendRequest, res: BackendResponse, err:
|
|
|
82
81
|
|
|
83
82
|
if (res.headersSent) return
|
|
84
83
|
|
|
85
|
-
const httpError = _errorToErrorObject
|
|
84
|
+
const httpError = _errorToErrorObject(originalError)
|
|
86
85
|
if (!includeErrorStack) delete httpError.stack
|
|
87
86
|
|
|
88
87
|
httpError.data.errorId = errorId
|
|
89
|
-
httpError.data.
|
|
88
|
+
httpError.data.backendResponseStatusCode ||= 500 // default to 500
|
|
90
89
|
httpError.data.headersSent = headersSent || undefined
|
|
91
90
|
httpError.data.report ||= undefined // set to undefined if false
|
|
92
91
|
_filterUndefinedValues(httpError.data, true)
|
|
93
92
|
|
|
94
93
|
formatError?.(httpError) // Mutates
|
|
95
94
|
|
|
96
|
-
res.status(httpError.data.
|
|
95
|
+
res.status(httpError.data.backendResponseStatusCode).json({
|
|
97
96
|
error: httpError,
|
|
98
|
-
}
|
|
97
|
+
} satisfies BackendErrorResponseObject)
|
|
99
98
|
}
|
|
100
99
|
|
|
101
100
|
function shouldReportToSentry(err: Error): boolean {
|
|
102
|
-
const e = err as
|
|
101
|
+
const e = err as AppError
|
|
103
102
|
|
|
104
103
|
// By default - report
|
|
105
104
|
if (!e?.data) return true
|
|
@@ -112,5 +111,7 @@ function shouldReportToSentry(err: Error): boolean {
|
|
|
112
111
|
// If no httpCode - report
|
|
113
112
|
// if httpCode >= 500 - report
|
|
114
113
|
// Otherwise - report, unless !reportOnly5xx is set
|
|
115
|
-
return
|
|
114
|
+
return (
|
|
115
|
+
!reportOnly5xx || !e.data.backendResponseStatusCode || e.data.backendResponseStatusCode >= 500
|
|
116
|
+
)
|
|
116
117
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { _get, AppError } from '@naturalcycles/js-lib'
|
|
2
2
|
import { AnySchema, getValidationResult, JoiValidationError } from '@naturalcycles/nodejs-lib'
|
|
3
3
|
import { BackendRequestHandler } from './server.model'
|
|
4
4
|
|
|
@@ -37,8 +37,8 @@ export function reqValidation(
|
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
return next(
|
|
40
|
-
new
|
|
41
|
-
|
|
40
|
+
new AppError(error.message, {
|
|
41
|
+
backendResponseStatusCode: 400,
|
|
42
42
|
report,
|
|
43
43
|
...error.data,
|
|
44
44
|
}),
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { _ms,
|
|
1
|
+
import { _ms, AppError, NumberOfSeconds } from '@naturalcycles/js-lib'
|
|
2
2
|
import {
|
|
3
3
|
BackendRequest,
|
|
4
4
|
BackendRequestHandler,
|
|
@@ -12,12 +12,12 @@ export interface RequestTimeoutMiddlewareCfg {
|
|
|
12
12
|
/**
|
|
13
13
|
* @default 120
|
|
14
14
|
*/
|
|
15
|
-
timeoutSeconds?:
|
|
15
|
+
timeoutSeconds?: NumberOfSeconds
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
18
|
* @default 503
|
|
19
19
|
*/
|
|
20
|
-
|
|
20
|
+
backendResponseStatusCode?: number
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
23
|
* @default 'Request timed out'
|
|
@@ -33,7 +33,7 @@ export function requestTimeoutMiddleware(
|
|
|
33
33
|
): BackendRequestHandler {
|
|
34
34
|
const {
|
|
35
35
|
timeoutSeconds: defTimeoutSeconds,
|
|
36
|
-
|
|
36
|
+
backendResponseStatusCode,
|
|
37
37
|
httpErrorMessage,
|
|
38
38
|
} = {
|
|
39
39
|
// Considerations about the default value of the timeout.
|
|
@@ -41,7 +41,7 @@ export function requestTimeoutMiddleware(
|
|
|
41
41
|
// so, cross-service communication has a chance to fail SOONER than server times out,
|
|
42
42
|
// so, proper error from exact service is shown, rather than generic "503 request timed out"
|
|
43
43
|
timeoutSeconds: 120,
|
|
44
|
-
|
|
44
|
+
backendResponseStatusCode: 503,
|
|
45
45
|
httpErrorMessage: 'Request timed out',
|
|
46
46
|
...cfg,
|
|
47
47
|
}
|
|
@@ -62,9 +62,9 @@ export function requestTimeoutMiddleware(
|
|
|
62
62
|
respondWithError(
|
|
63
63
|
req,
|
|
64
64
|
res,
|
|
65
|
-
new
|
|
65
|
+
new AppError(msg, {
|
|
66
66
|
code,
|
|
67
|
-
|
|
67
|
+
backendResponseStatusCode,
|
|
68
68
|
endpoint,
|
|
69
69
|
timeoutSeconds,
|
|
70
70
|
// userFriendly: true, // no, cause this error is not expected
|
|
@@ -82,7 +82,7 @@ export interface CustomRequestTimeoutMiddlewareCfg {
|
|
|
82
82
|
/**
|
|
83
83
|
* @default 120
|
|
84
84
|
*/
|
|
85
|
-
timeoutSeconds?:
|
|
85
|
+
timeoutSeconds?: NumberOfSeconds
|
|
86
86
|
}
|
|
87
87
|
|
|
88
88
|
/**
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { JsonSchema, JsonSchemaBuilder, _get, AppError } from '@naturalcycles/js-lib'
|
|
2
2
|
import { AjvSchema, AjvValidationError } from '@naturalcycles/nodejs-lib'
|
|
3
3
|
import { BackendRequestHandler } from './server.model'
|
|
4
4
|
import { ReqValidationOptions } from './reqValidationMiddleware'
|
|
@@ -54,8 +54,8 @@ function validateObject(
|
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
return next(
|
|
57
|
-
new
|
|
58
|
-
|
|
57
|
+
new AppError(error.message, {
|
|
58
|
+
backendResponseStatusCode: 400,
|
|
59
59
|
report,
|
|
60
60
|
...error.data,
|
|
61
61
|
}),
|
|
@@ -1,10 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
HttpError,
|
|
3
|
-
_get,
|
|
4
|
-
ZodSchema,
|
|
5
|
-
ZodValidationError,
|
|
6
|
-
zSafeValidate,
|
|
7
|
-
} from '@naturalcycles/js-lib'
|
|
1
|
+
import { _get, AppError, ZodSchema, ZodValidationError, zSafeValidate } from '@naturalcycles/js-lib'
|
|
8
2
|
import { BackendRequestHandler } from './server.model'
|
|
9
3
|
import { ReqValidationOptions } from './reqValidationMiddleware'
|
|
10
4
|
|
|
@@ -36,8 +30,8 @@ export function zodReqValidate(
|
|
|
36
30
|
}
|
|
37
31
|
|
|
38
32
|
return next(
|
|
39
|
-
new
|
|
40
|
-
|
|
33
|
+
new AppError(error.message, {
|
|
34
|
+
backendResponseStatusCode: 400,
|
|
41
35
|
report,
|
|
42
36
|
}),
|
|
43
37
|
)
|