@naturalcycles/backend-lib 2.73.2 → 3.2.0
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/admin.mw.d.ts +4 -4
- package/dist/admin/base.admin.service.d.ts +11 -12
- package/dist/db/httpDBRequestHandler.d.ts +2 -2
- package/dist/index.d.ts +26 -24
- package/dist/index.js +29 -50
- package/dist/sentry/sentry.shared.service.d.ts +3 -3
- package/dist/server/appEngineLogMiddleware.d.ts +13 -0
- package/dist/server/{handlers/createGaeLogMiddleware.js → appEngineLogMiddleware.js} +6 -7
- package/dist/server/{handlers/asyncLocalStorage.mw.d.ts → asyncLocalStorageMiddleware.d.ts} +4 -4
- package/dist/server/{handlers/asyncLocalStorage.mw.js → asyncLocalStorageMiddleware.js} +5 -5
- package/dist/server/{handlers/bodyParserTimeout.mw.d.ts → bodyParserTimeoutMiddleware.d.ts} +4 -4
- package/dist/server/{handlers/bodyParserTimeout.mw.js → bodyParserTimeoutMiddleware.js} +10 -7
- package/dist/server/catchWrapper.d.ts +2 -2
- package/dist/server/createDefaultApp.d.ts +2 -2
- package/dist/server/createDefaultApp.js +13 -13
- package/dist/server/createDefaultApp.model.d.ts +8 -8
- package/dist/server/deployInfo.util.d.ts +1 -1
- package/dist/server/genericErrorMiddleware.d.ts +12 -0
- package/dist/server/{handlers/genericErrorHandler.mw.js → genericErrorMiddleware.js} +20 -8
- package/dist/server/getDefaultRouter.d.ts +2 -2
- package/dist/server/methodOverrideMiddleware.d.ts +8 -0
- package/dist/server/{handlers/methodOverride.mw.js → methodOverrideMiddleware.js} +3 -3
- package/dist/server/notFoundMiddleware.d.ts +2 -0
- package/dist/server/notFoundMiddleware.js +10 -0
- package/dist/server/okMiddleware.d.ts +2 -0
- package/dist/server/{handlers/okHandler.mw.js → okMiddleware.js} +3 -3
- package/dist/server/{handlers/reqValidation.mw.d.ts → reqValidationMiddleware.d.ts} +2 -2
- package/dist/server/{handlers/reqValidation.mw.js → reqValidationMiddleware.js} +0 -0
- package/dist/server/request.log.util.d.ts +2 -2
- package/dist/server/request.util.d.ts +10 -2
- package/dist/server/request.util.js +8 -0
- package/dist/server/requestTimeoutMiddleware.d.ts +31 -0
- package/dist/server/requestTimeoutMiddleware.js +68 -0
- package/dist/server/{handlers/safeJsonMiddleware.d.ts → safeJsonMiddleware.d.ts} +2 -2
- package/dist/server/{handlers/safeJsonMiddleware.js → safeJsonMiddleware.js} +0 -0
- package/dist/server/server.model.d.ts +37 -0
- package/dist/server/server.model.js +2 -0
- package/dist/server/serverStatsMiddleware.d.ts +13 -0
- package/dist/server/{handlers/serverStatsMiddleware.js → serverStatsMiddleware.js} +4 -16
- package/dist/server/serverStatusMiddleware.d.ts +3 -0
- package/dist/server/{handlers/statusHandler.js → serverStatusMiddleware.js} +7 -7
- package/dist/server/simpleRequestLoggerMiddleware.d.ts +12 -0
- package/dist/server/{handlers/simpleRequestLogger.mw.js → simpleRequestLoggerMiddleware.js} +5 -5
- package/dist/server/startServer.model.d.ts +2 -2
- package/dist/server/{handlers/validate.mw.d.ts → validateMiddleware.d.ts} +5 -5
- package/dist/server/{handlers/validate.mw.js → validateMiddleware.js} +0 -0
- package/dist/testing/express.test.service.d.ts +6 -6
- package/dist/testing/express.test.service.js +8 -5
- package/package.json +1 -2
- package/src/admin/admin.mw.ts +7 -4
- package/src/admin/base.admin.service.ts +11 -12
- package/src/admin/secureHeader.mw.ts +2 -2
- package/src/db/httpDBRequestHandler.ts +2 -3
- package/src/index.ts +55 -68
- package/src/sentry/sentry.shared.service.ts +3 -4
- package/src/server/{handlers/createGaeLogMiddleware.ts → appEngineLogMiddleware.ts} +6 -29
- package/src/server/{handlers/asyncLocalStorage.mw.ts → asyncLocalStorageMiddleware.ts} +5 -5
- package/src/server/{handlers/bodyParserTimeout.mw.ts → bodyParserTimeoutMiddleware.ts} +14 -13
- package/src/server/catchWrapper.ts +2 -2
- package/src/server/createDefaultApp.model.ts +8 -8
- package/src/server/createDefaultApp.ts +22 -19
- package/src/server/deployInfo.util.ts +1 -1
- package/src/server/{handlers/genericErrorHandler.mw.ts → genericErrorMiddleware.ts} +24 -14
- package/src/server/getDefaultRouter.ts +2 -2
- package/src/server/{handlers/methodOverride.mw.ts → methodOverrideMiddleware.ts} +5 -3
- package/src/server/notFoundMiddleware.ts +8 -0
- package/src/server/okMiddleware.ts +7 -0
- package/src/server/{handlers/reqValidation.mw.ts → reqValidationMiddleware.ts} +2 -2
- package/src/server/request.log.util.ts +2 -2
- package/src/server/request.util.ts +10 -2
- package/src/server/requestTimeoutMiddleware.ts +117 -0
- package/src/server/{handlers/safeJsonMiddleware.ts → safeJsonMiddleware.ts} +3 -3
- package/src/server/server.model.ts +56 -0
- package/src/server/{handlers/serverStatsMiddleware.ts → serverStatsMiddleware.ts} +10 -22
- package/src/server/{handlers/statusHandler.ts → serverStatusMiddleware.ts} +5 -5
- package/src/server/{handlers/simpleRequestLogger.mw.ts → simpleRequestLoggerMiddleware.ts} +8 -7
- package/src/server/startServer.model.ts +2 -2
- package/src/server/{handlers/validate.mw.ts → validateMiddleware.ts} +6 -6
- package/src/testing/express.test.service.ts +12 -8
- package/dist/server/handlers/createGaeLogMiddleware.d.ts +0 -30
- package/dist/server/handlers/genericErrorHandler.mw.d.ts +0 -13
- package/dist/server/handlers/methodOverride.mw.d.ts +0 -8
- package/dist/server/handlers/notFoundHandler.mw.d.ts +0 -2
- package/dist/server/handlers/notFoundHandler.mw.js +0 -9
- package/dist/server/handlers/okHandler.mw.d.ts +0 -2
- package/dist/server/handlers/requestTimeout.mw.d.ts +0 -16
- package/dist/server/handlers/requestTimeout.mw.js +0 -34
- package/dist/server/handlers/sentryErrorHandler.mw.d.ts +0 -10
- package/dist/server/handlers/sentryErrorHandler.mw.js +0 -26
- package/dist/server/handlers/serverStatsMiddleware.d.ts +0 -13
- package/dist/server/handlers/simpleRequestLogger.mw.d.ts +0 -12
- package/dist/server/handlers/statusHandler.d.ts +0 -3
- package/src/server/handlers/notFoundHandler.mw.ts +0 -7
- package/src/server/handlers/okHandler.mw.ts +0 -7
- package/src/server/handlers/requestTimeout.mw.ts +0 -62
- package/src/server/handlers/sentryErrorHandler.mw.ts +0 -34
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { _assert, Admin401ErrorData, Admin403ErrorData, HttpError } from '@naturalcycles/js-lib'
|
|
2
2
|
import { dimGrey, green, red } from '@naturalcycles/nodejs-lib/dist/colors'
|
|
3
|
-
import { Request, RequestHandler } from 'express'
|
|
4
3
|
import type * as FirebaseAdmin from 'firebase-admin'
|
|
5
|
-
import {
|
|
4
|
+
import { BackendRequest, BackendRequestHandler } from '../server/server.model'
|
|
6
5
|
|
|
7
6
|
export interface AdminServiceCfg {
|
|
8
7
|
/**
|
|
@@ -70,7 +69,7 @@ export class BaseAdminService {
|
|
|
70
69
|
* To be extended.
|
|
71
70
|
*/
|
|
72
71
|
protected async onPermissionCheck(
|
|
73
|
-
req:
|
|
72
|
+
req: BackendRequest,
|
|
74
73
|
email: string,
|
|
75
74
|
reqPermissions: string[],
|
|
76
75
|
required: boolean,
|
|
@@ -85,7 +84,7 @@ export class BaseAdminService {
|
|
|
85
84
|
)
|
|
86
85
|
}
|
|
87
86
|
|
|
88
|
-
async getEmailByToken(req:
|
|
87
|
+
async getEmailByToken(req: BackendRequest, adminToken?: string): Promise<string | undefined> {
|
|
89
88
|
if (!adminToken) return
|
|
90
89
|
|
|
91
90
|
try {
|
|
@@ -112,7 +111,7 @@ export class BaseAdminService {
|
|
|
112
111
|
* Current implementation is based on req=Request (from Express).
|
|
113
112
|
* Override if needed.
|
|
114
113
|
*/
|
|
115
|
-
async getAdminToken(req:
|
|
114
|
+
async getAdminToken(req: BackendRequest): Promise<string | undefined> {
|
|
116
115
|
return (
|
|
117
116
|
(req.cookies || {})[this.cfg.adminTokenKey] ||
|
|
118
117
|
req.header(this.cfg.adminTokenKey) ||
|
|
@@ -120,13 +119,13 @@ export class BaseAdminService {
|
|
|
120
119
|
)
|
|
121
120
|
}
|
|
122
121
|
|
|
123
|
-
async isAdmin(req:
|
|
122
|
+
async isAdmin(req: BackendRequest): Promise<boolean> {
|
|
124
123
|
const adminToken = await this.getAdminToken(req)
|
|
125
124
|
const email = await this.getEmailByToken(req, adminToken)
|
|
126
125
|
return !!this.getEmailPermissions(email)
|
|
127
126
|
}
|
|
128
127
|
|
|
129
|
-
async getAdminInfo(req:
|
|
128
|
+
async getAdminInfo(req: BackendRequest): Promise<AdminInfo | undefined> {
|
|
130
129
|
return await this.hasPermissions(req)
|
|
131
130
|
}
|
|
132
131
|
|
|
@@ -140,7 +139,7 @@ export class BaseAdminService {
|
|
|
140
139
|
* Otherwise returns undefined
|
|
141
140
|
*/
|
|
142
141
|
async hasPermissions(
|
|
143
|
-
req:
|
|
142
|
+
req: BackendRequest,
|
|
144
143
|
reqPermissions: string[] = [],
|
|
145
144
|
meta: Record<string, any> = {},
|
|
146
145
|
): Promise<AdminInfo | undefined> {
|
|
@@ -164,7 +163,7 @@ export class BaseAdminService {
|
|
|
164
163
|
}
|
|
165
164
|
|
|
166
165
|
async requirePermissions(
|
|
167
|
-
req:
|
|
166
|
+
req: BackendRequest,
|
|
168
167
|
reqPermissions: string[] = [],
|
|
169
168
|
meta: Record<string, any> = {},
|
|
170
169
|
andComparison: boolean = true,
|
|
@@ -221,7 +220,7 @@ export class BaseAdminService {
|
|
|
221
220
|
|
|
222
221
|
// convenience method
|
|
223
222
|
async hasPermission(
|
|
224
|
-
req:
|
|
223
|
+
req: BackendRequest,
|
|
225
224
|
reqPermission: string,
|
|
226
225
|
meta?: Record<string, any>,
|
|
227
226
|
): Promise<boolean> {
|
|
@@ -229,7 +228,7 @@ export class BaseAdminService {
|
|
|
229
228
|
}
|
|
230
229
|
|
|
231
230
|
async requirePermission(
|
|
232
|
-
req:
|
|
231
|
+
req: BackendRequest,
|
|
233
232
|
reqPermission: string,
|
|
234
233
|
meta?: Record<string, any>,
|
|
235
234
|
): Promise<AdminInfo> {
|
|
@@ -245,7 +244,7 @@ export class BaseAdminService {
|
|
|
245
244
|
*
|
|
246
245
|
* Same endpoint is used to logout, but the `Authentication` header should contain `logout` magic string.
|
|
247
246
|
*/
|
|
248
|
-
getFirebaseAuthLoginHandler():
|
|
247
|
+
getFirebaseAuthLoginHandler(): BackendRequestHandler {
|
|
249
248
|
return async (req, res) => {
|
|
250
249
|
const token = req.header('authentication')
|
|
251
250
|
_assert(token, `401 Unauthenticated`, {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Admin401ErrorData, HttpError } from '@naturalcycles/js-lib'
|
|
2
|
-
import {
|
|
2
|
+
import { BackendRequestHandler } from '../server/server.model'
|
|
3
3
|
import { AdminMiddleware, RequireAdminCfg, requireAdminPermissions } from './admin.mw'
|
|
4
4
|
import { BaseAdminService } from './base.admin.service'
|
|
5
5
|
|
|
@@ -19,7 +19,7 @@ export function createSecureHeaderMiddleware(cfg: SecureHeaderMiddlewareCfg): Ad
|
|
|
19
19
|
function requireSecureHeaderOrAdmin(
|
|
20
20
|
cfg: SecureHeaderMiddlewareCfg,
|
|
21
21
|
reqPermissions?: string[],
|
|
22
|
-
):
|
|
22
|
+
): BackendRequestHandler {
|
|
23
23
|
const requireAdmin = requireAdminPermissions(cfg.adminService, reqPermissions, cfg)
|
|
24
24
|
|
|
25
25
|
return async (req, res, next) => {
|
|
@@ -12,8 +12,7 @@ import {
|
|
|
12
12
|
} from '@naturalcycles/db-lib/dist/validation'
|
|
13
13
|
import { ObjectWithId } from '@naturalcycles/js-lib'
|
|
14
14
|
import { anyObjectSchema, arraySchema, objectSchema, stringSchema } from '@naturalcycles/nodejs-lib'
|
|
15
|
-
import {
|
|
16
|
-
import { getDefaultRouter, reqValidation } from '..'
|
|
15
|
+
import { BackendRouter, getDefaultRouter, reqValidation } from '..'
|
|
17
16
|
|
|
18
17
|
export interface GetByIdsInput {
|
|
19
18
|
table: string
|
|
@@ -52,7 +51,7 @@ const saveBatchInputSchema = objectSchema<SaveBatchInput>({
|
|
|
52
51
|
/**
|
|
53
52
|
* Exposes CommonDB interface from provided CommonDB as HTTP endpoint (Express RequestHandler).
|
|
54
53
|
*/
|
|
55
|
-
export function httpDBRequestHandler(db: CommonDB):
|
|
54
|
+
export function httpDBRequestHandler(db: CommonDB): BackendRouter {
|
|
56
55
|
const router = getDefaultRouter()
|
|
57
56
|
|
|
58
57
|
// resetCache, only applicable to InMemoryDB
|
package/src/index.ts
CHANGED
|
@@ -10,56 +10,60 @@ import { FirebaseSharedService } from './admin/firebase.shared.service'
|
|
|
10
10
|
import { createSecureHeaderMiddleware, SecureHeaderMiddlewareCfg } from './admin/secureHeader.mw'
|
|
11
11
|
import { BaseEnv } from './env/env.model'
|
|
12
12
|
import { EnvSharedService, EnvSharedServiceCfg } from './env/env.shared.service'
|
|
13
|
-
|
|
13
|
+
export * from './gae/appEngine.util'
|
|
14
14
|
import { SentrySharedService, SentrySharedServiceCfg } from './sentry/sentry.shared.service'
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
export * from './server/catchWrapper'
|
|
16
|
+
export * from './server/createDefaultApp'
|
|
17
17
|
import {
|
|
18
18
|
DefaultAppCfg,
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
BackendRequestHandlerCfg,
|
|
20
|
+
BackendRequestHandlerWithPath,
|
|
21
21
|
} from './server/createDefaultApp.model'
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
export * from './server/deployInfo.util'
|
|
23
|
+
export * from './server/getDefaultRouter'
|
|
24
24
|
import {
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
bodyParserTimeoutMiddleware,
|
|
26
|
+
BodyParserTimeoutMiddlewareCfg,
|
|
27
27
|
clearBodyParserTimeout,
|
|
28
|
-
} from './server/
|
|
29
|
-
|
|
28
|
+
} from './server/bodyParserTimeoutMiddleware'
|
|
29
|
+
export * from './server/genericErrorMiddleware'
|
|
30
|
+
export * from './server/serverStatsMiddleware'
|
|
30
31
|
import {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
} from './server/
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
import {
|
|
37
|
-
|
|
38
|
-
import { reqValidation, ReqValidationOptions } from './server/
|
|
32
|
+
methodOverrideMiddleware,
|
|
33
|
+
MethodOverrideMiddlewareCfg,
|
|
34
|
+
} from './server/methodOverrideMiddleware'
|
|
35
|
+
export * from './server/notFoundMiddleware'
|
|
36
|
+
export * from './server/okMiddleware'
|
|
37
|
+
import { RequestTimeoutMiddlewareCfg } from './server/requestTimeoutMiddleware'
|
|
38
|
+
export * from './server/requestTimeoutMiddleware'
|
|
39
|
+
import { reqValidation, ReqValidationOptions } from './server/reqValidationMiddleware'
|
|
39
40
|
import {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
} from './server/
|
|
43
|
-
import {
|
|
44
|
-
|
|
45
|
-
|
|
41
|
+
simpleRequestLoggerMiddleware,
|
|
42
|
+
SimpleRequestLoggerMiddlewareCfg,
|
|
43
|
+
} from './server/simpleRequestLoggerMiddleware'
|
|
44
|
+
import { serverStatusMiddleware, getServerStatusData } from './server/serverStatusMiddleware'
|
|
45
|
+
export * from './server/validateMiddleware'
|
|
46
|
+
export * from './server/request.log.util'
|
|
46
47
|
import { BackendServer, startServer } from './server/startServer'
|
|
47
48
|
import { StartServerCfg, StartServerData } from './server/startServer.model'
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
49
|
+
export * from './server/asyncLocalStorageMiddleware'
|
|
50
|
+
import type {
|
|
51
|
+
BackendRequest,
|
|
52
|
+
BackendRequestHandler,
|
|
53
|
+
BackendResponse,
|
|
54
|
+
BackendErrorRequestHandler,
|
|
55
|
+
BackendRouter,
|
|
56
|
+
BackendApplication,
|
|
57
|
+
} from './server/server.model'
|
|
58
|
+
export * from './server/appEngineLogMiddleware'
|
|
59
|
+
export * from './server/safeJsonMiddleware'
|
|
60
|
+
export * from './server/request.util'
|
|
57
61
|
|
|
58
62
|
export type {
|
|
59
|
-
|
|
63
|
+
MethodOverrideMiddlewareCfg,
|
|
60
64
|
SentrySharedServiceCfg,
|
|
61
|
-
|
|
62
|
-
|
|
65
|
+
BackendRequestHandlerWithPath,
|
|
66
|
+
BackendRequestHandlerCfg,
|
|
63
67
|
DefaultAppCfg,
|
|
64
68
|
StartServerCfg,
|
|
65
69
|
StartServerData,
|
|
@@ -70,11 +74,16 @@ export type {
|
|
|
70
74
|
AdminInfo,
|
|
71
75
|
RequireAdminCfg,
|
|
72
76
|
SecureHeaderMiddlewareCfg,
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
77
|
+
BodyParserTimeoutMiddlewareCfg,
|
|
78
|
+
RequestTimeoutMiddlewareCfg,
|
|
79
|
+
SimpleRequestLoggerMiddlewareCfg,
|
|
76
80
|
ReqValidationOptions,
|
|
77
|
-
|
|
81
|
+
BackendRequest,
|
|
82
|
+
BackendRequestHandler,
|
|
83
|
+
BackendResponse,
|
|
84
|
+
BackendErrorRequestHandler,
|
|
85
|
+
BackendRouter,
|
|
86
|
+
BackendApplication,
|
|
78
87
|
}
|
|
79
88
|
|
|
80
89
|
export {
|
|
@@ -82,39 +91,17 @@ export {
|
|
|
82
91
|
SentrySharedService,
|
|
83
92
|
EnvSharedService,
|
|
84
93
|
reqValidation,
|
|
85
|
-
|
|
86
|
-
genericErrorHandler,
|
|
87
|
-
methodOverride,
|
|
88
|
-
createDefaultApp,
|
|
94
|
+
methodOverrideMiddleware,
|
|
89
95
|
startServer,
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
isGAE,
|
|
93
|
-
statusHandler,
|
|
94
|
-
statusHandlerData,
|
|
95
|
-
okHandler,
|
|
96
|
-
getDeployInfo,
|
|
96
|
+
serverStatusMiddleware,
|
|
97
|
+
getServerStatusData,
|
|
97
98
|
onFinished,
|
|
98
|
-
respondWithError,
|
|
99
|
-
logRequest,
|
|
100
99
|
FirebaseSharedService,
|
|
101
100
|
createAdminMiddleware,
|
|
102
101
|
BaseAdminService,
|
|
103
102
|
loginHtml,
|
|
104
103
|
createSecureHeaderMiddleware,
|
|
105
|
-
|
|
104
|
+
bodyParserTimeoutMiddleware,
|
|
106
105
|
clearBodyParserTimeout,
|
|
107
|
-
|
|
108
|
-
simpleRequestLogger,
|
|
109
|
-
coloredHttpCode,
|
|
110
|
-
validateBody,
|
|
111
|
-
validateParams,
|
|
112
|
-
validateQuery,
|
|
113
|
-
createAsyncLocalStorage,
|
|
114
|
-
getRequest,
|
|
115
|
-
getRequestLogger,
|
|
116
|
-
requestLogger,
|
|
117
|
-
serverStatsHTMLHandler,
|
|
118
|
-
serverStatsMiddleware,
|
|
119
|
-
safeJsonMiddleware,
|
|
106
|
+
simpleRequestLoggerMiddleware,
|
|
120
107
|
}
|
|
@@ -3,8 +3,7 @@ import { inspectAny, inspectAnyStringifyFn } from '@naturalcycles/nodejs-lib'
|
|
|
3
3
|
import { Severity } from '@sentry/node'
|
|
4
4
|
import type { Breadcrumb, NodeOptions } from '@sentry/node'
|
|
5
5
|
import type * as SentryLib from '@sentry/node'
|
|
6
|
-
import {
|
|
7
|
-
import { getRequestLogger } from '../index'
|
|
6
|
+
import { BackendErrorRequestHandler, BackendRequestHandler, getRequestLogger } from '../index'
|
|
8
7
|
|
|
9
8
|
export interface SentrySharedServiceCfg extends NodeOptions {}
|
|
10
9
|
|
|
@@ -51,7 +50,7 @@ export class SentrySharedService {
|
|
|
51
50
|
*
|
|
52
51
|
* UPD: to be tested. Without it - request is not enriched and the error is less useful.
|
|
53
52
|
*/
|
|
54
|
-
getRequestHandler():
|
|
53
|
+
getRequestHandler(): BackendRequestHandler {
|
|
55
54
|
return this.sentry().Handlers.requestHandler()
|
|
56
55
|
}
|
|
57
56
|
|
|
@@ -60,7 +59,7 @@ export class SentrySharedService {
|
|
|
60
59
|
*
|
|
61
60
|
* @deprecated
|
|
62
61
|
*/
|
|
63
|
-
getErrorHandler():
|
|
62
|
+
getErrorHandler(): BackendErrorRequestHandler {
|
|
64
63
|
return this.sentry().Handlers.errorHandler()
|
|
65
64
|
}
|
|
66
65
|
|
|
@@ -1,29 +1,8 @@
|
|
|
1
1
|
import { inspect } from 'util'
|
|
2
2
|
import { dimGrey } from '@naturalcycles/nodejs-lib/dist/colors'
|
|
3
3
|
import { inspectAny } from '@naturalcycles/nodejs-lib'
|
|
4
|
-
import { AnyObject,
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Use this interface instead of express.Request in cases when TypeScript gives an error, because it haven't "included" this very file
|
|
9
|
-
*/
|
|
10
|
-
export interface RequestWithLog extends Request {
|
|
11
|
-
log: CommonLogFunction
|
|
12
|
-
warn: CommonLogFunction
|
|
13
|
-
error: CommonLogFunction
|
|
14
|
-
|
|
15
|
-
requestId?: string
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
declare module 'http' {
|
|
19
|
-
interface IncomingMessage {
|
|
20
|
-
log: CommonLogFunction
|
|
21
|
-
warn: CommonLogFunction
|
|
22
|
-
error: CommonLogFunction
|
|
23
|
-
|
|
24
|
-
requestId?: string
|
|
25
|
-
}
|
|
26
|
-
}
|
|
4
|
+
import { AnyObject, CommonLogger } from '@naturalcycles/js-lib'
|
|
5
|
+
import { BackendRequestHandler } from './server.model'
|
|
27
6
|
|
|
28
7
|
const { GOOGLE_CLOUD_PROJECT, GAE_INSTANCE } = process.env
|
|
29
8
|
const isGAE = !!GAE_INSTANCE
|
|
@@ -71,7 +50,7 @@ function logToDev(requestId: string | null, args: any[]): void {
|
|
|
71
50
|
)
|
|
72
51
|
}
|
|
73
52
|
|
|
74
|
-
export function
|
|
53
|
+
export function appEngineLogMiddleware(): BackendRequestHandler {
|
|
75
54
|
if (!isGAE || !GOOGLE_CLOUD_PROJECT) {
|
|
76
55
|
// Local machine, return "simple" logToDev middleware with request numbering
|
|
77
56
|
return function gaeLogMiddlewareDev(req, res, next) {
|
|
@@ -84,16 +63,14 @@ export function createGAELogMiddleware(): RequestHandler {
|
|
|
84
63
|
|
|
85
64
|
// Otherwise, we're in AppEngine
|
|
86
65
|
|
|
87
|
-
return function
|
|
88
|
-
const meta: AnyObject = {}
|
|
89
|
-
|
|
66
|
+
return function appEngineLogHandler(req, res, next) {
|
|
90
67
|
const traceHeader = req.header('x-cloud-trace-context')
|
|
91
68
|
if (traceHeader) {
|
|
92
69
|
const [trace] = traceHeader.split('/')
|
|
93
|
-
|
|
70
|
+
const meta = {
|
|
94
71
|
'logging.googleapis.com/trace': `projects/${GOOGLE_CLOUD_PROJECT}/traces/${trace}`,
|
|
95
72
|
'appengine.googleapis.com/request_id': req.header('x-appengine-request-log-id'),
|
|
96
|
-
}
|
|
73
|
+
}
|
|
97
74
|
Object.assign(req, {
|
|
98
75
|
log: (...args: any[]) => logToAppEngine({ ...meta, severity: 'INFO' }, args),
|
|
99
76
|
warn: (...args: any[]) => logToAppEngine({ ...meta, severity: 'WARNING' }, args),
|
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
import { AsyncLocalStorage } from 'async_hooks'
|
|
2
2
|
import { _lazyValue, CommonLogger } from '@naturalcycles/js-lib'
|
|
3
|
-
import {
|
|
4
|
-
import { gaeLogger, devLogger } from './
|
|
3
|
+
import { BackendRequest, BackendRequestHandler } from './server.model'
|
|
4
|
+
import { gaeLogger, devLogger } from './appEngineLogMiddleware'
|
|
5
5
|
|
|
6
6
|
const { GAE_INSTANCE } = process.env
|
|
7
7
|
const isGAE = !!GAE_INSTANCE
|
|
8
8
|
|
|
9
9
|
export interface RequestLocalStorage {
|
|
10
|
-
req:
|
|
10
|
+
req: BackendRequest
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
// Singleton, for simplicity
|
|
14
14
|
// Create it lazily (on demand)
|
|
15
15
|
const storage = _lazyValue(() => new AsyncLocalStorage<RequestLocalStorage>())
|
|
16
16
|
|
|
17
|
-
export function
|
|
17
|
+
export function asyncLocalStorageMiddleware(): BackendRequestHandler {
|
|
18
18
|
return (req, res, next) => {
|
|
19
19
|
const store: RequestLocalStorage = {
|
|
20
20
|
req,
|
|
@@ -24,7 +24,7 @@ export function createAsyncLocalStorage(): RequestHandler {
|
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
export function getRequest():
|
|
27
|
+
export function getRequest(): BackendRequest | undefined {
|
|
28
28
|
return storage().getStore()?.req
|
|
29
29
|
}
|
|
30
30
|
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { HttpError } from '@naturalcycles/js-lib'
|
|
2
|
-
import {
|
|
3
|
-
import { respondWithError } from '../../index'
|
|
2
|
+
import { BackendRequestHandler, respondWithError } from '../index'
|
|
4
3
|
|
|
5
|
-
export interface
|
|
4
|
+
export interface BodyParserTimeoutMiddlewareCfg {
|
|
6
5
|
/**
|
|
7
6
|
* @default 10
|
|
8
7
|
*/
|
|
@@ -19,16 +18,14 @@ export interface BodyParserTimeoutCfg {
|
|
|
19
18
|
httpStatus?: string
|
|
20
19
|
}
|
|
21
20
|
|
|
22
|
-
interface RequestWithTimeout extends Request {
|
|
23
|
-
_bodyParserTimeout?: NodeJS.Timeout
|
|
24
|
-
}
|
|
25
|
-
|
|
26
21
|
const code = 'BODY_PARSER_TIMEOUT'
|
|
27
22
|
|
|
28
23
|
/**
|
|
29
24
|
* Should be called BEFORE bodyParser
|
|
30
25
|
*/
|
|
31
|
-
export function
|
|
26
|
+
export function bodyParserTimeoutMiddleware(
|
|
27
|
+
cfg: BodyParserTimeoutMiddlewareCfg = {},
|
|
28
|
+
): BackendRequestHandler {
|
|
32
29
|
const { timeoutSeconds, httpStatusCode, httpStatus } = {
|
|
33
30
|
timeoutSeconds: 10,
|
|
34
31
|
httpStatusCode: 400,
|
|
@@ -38,8 +35,12 @@ export function bodyParserTimeout(cfg: BodyParserTimeoutCfg = {}): RequestHandle
|
|
|
38
35
|
|
|
39
36
|
const timeout = timeoutSeconds * 1000
|
|
40
37
|
|
|
41
|
-
return (req
|
|
42
|
-
|
|
38
|
+
return (req, res, next) => {
|
|
39
|
+
// If requestTimeout was previously set - cancel it first
|
|
40
|
+
// Then set the new requestTimeout and handler
|
|
41
|
+
if (req.bodyParserTimeout) clearTimeout(req.bodyParserTimeout)
|
|
42
|
+
|
|
43
|
+
req.bodyParserTimeout = setTimeout(() => {
|
|
43
44
|
respondWithError(
|
|
44
45
|
req,
|
|
45
46
|
res,
|
|
@@ -58,9 +59,9 @@ export function bodyParserTimeout(cfg: BodyParserTimeoutCfg = {}): RequestHandle
|
|
|
58
59
|
/**
|
|
59
60
|
* Should be called AFTER bodyParser
|
|
60
61
|
*/
|
|
61
|
-
export function clearBodyParserTimeout():
|
|
62
|
-
return (req
|
|
63
|
-
|
|
62
|
+
export function clearBodyParserTimeout(): BackendRequestHandler {
|
|
63
|
+
return (req, res, next) => {
|
|
64
|
+
clearTimeout(req.bodyParserTimeout!)
|
|
64
65
|
next()
|
|
65
66
|
}
|
|
66
67
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { BackendRequestHandler } from './server.model'
|
|
2
2
|
|
|
3
3
|
// https://strongloop.com/strongblog/async-error-handling-expressjs-es7-promises-generators/
|
|
4
4
|
// https://stackoverflow.com/a/43564267/4919972
|
|
5
5
|
export const catchWrapper =
|
|
6
|
-
(fn:
|
|
6
|
+
(fn: BackendRequestHandler): BackendRequestHandler =>
|
|
7
7
|
async (req, res, next) => {
|
|
8
8
|
try {
|
|
9
9
|
// eslint-disable-next-line @typescript-eslint/await-thenable
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import { RequestHandler } from 'express'
|
|
2
1
|
import { SentrySharedService } from '../sentry/sentry.shared.service'
|
|
2
|
+
import { BackendRequestHandler } from './server.model'
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Plain RequestHandler can be provided - then it's mounted to /
|
|
6
6
|
* Otherwise `path` can be provided to specify mounting point.
|
|
7
7
|
*/
|
|
8
|
-
export type
|
|
8
|
+
export type BackendRequestHandlerCfg = BackendRequestHandler | BackendRequestHandlerWithPath
|
|
9
9
|
|
|
10
|
-
export interface
|
|
10
|
+
export interface BackendRequestHandlerWithPath {
|
|
11
11
|
path: string
|
|
12
|
-
handler:
|
|
12
|
+
handler: BackendRequestHandler
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
/**
|
|
@@ -21,9 +21,9 @@ export interface RequestHandlerWithPath {
|
|
|
21
21
|
* 4. postHandlers
|
|
22
22
|
*/
|
|
23
23
|
export interface DefaultAppCfg {
|
|
24
|
-
preHandlers?:
|
|
25
|
-
handlers?:
|
|
26
|
-
resources?:
|
|
27
|
-
postHandlers?:
|
|
24
|
+
preHandlers?: BackendRequestHandlerCfg[]
|
|
25
|
+
handlers?: BackendRequestHandlerCfg[]
|
|
26
|
+
resources?: BackendRequestHandlerCfg[]
|
|
27
|
+
postHandlers?: BackendRequestHandlerCfg[]
|
|
28
28
|
sentryService?: SentrySharedService
|
|
29
29
|
}
|
|
@@ -1,19 +1,22 @@
|
|
|
1
1
|
import cookieParser = require('cookie-parser')
|
|
2
2
|
import cors = require('cors')
|
|
3
|
-
import type { Application } from 'express'
|
|
4
3
|
import express = require('express')
|
|
5
|
-
import { isGAE,
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
4
|
+
import { BackendApplication, isGAE, methodOverrideMiddleware } from '..'
|
|
5
|
+
import {
|
|
6
|
+
DefaultAppCfg,
|
|
7
|
+
BackendRequestHandlerCfg,
|
|
8
|
+
BackendRequestHandlerWithPath,
|
|
9
|
+
} from './createDefaultApp.model'
|
|
10
|
+
import { asyncLocalStorageMiddleware } from './asyncLocalStorageMiddleware'
|
|
11
|
+
import { appEngineLogMiddleware } from './appEngineLogMiddleware'
|
|
12
|
+
import { genericErrorMiddleware } from './genericErrorMiddleware'
|
|
13
|
+
import { notFoundMiddleware } from './notFoundMiddleware'
|
|
14
|
+
import { requestTimeoutMiddleware } from './requestTimeoutMiddleware'
|
|
15
|
+
import { simpleRequestLoggerMiddleware } from './simpleRequestLoggerMiddleware'
|
|
13
16
|
|
|
14
17
|
const isTest = process.env['APP_ENV'] === 'test'
|
|
15
18
|
|
|
16
|
-
export function createDefaultApp(cfg: DefaultAppCfg):
|
|
19
|
+
export function createDefaultApp(cfg: DefaultAppCfg): BackendApplication {
|
|
17
20
|
const { sentryService } = cfg
|
|
18
21
|
|
|
19
22
|
const app = express()
|
|
@@ -25,10 +28,10 @@ export function createDefaultApp(cfg: DefaultAppCfg): Application {
|
|
|
25
28
|
// preHandlers
|
|
26
29
|
useHandlers(app, cfg.preHandlers)
|
|
27
30
|
|
|
28
|
-
app.use(
|
|
31
|
+
app.use(appEngineLogMiddleware())
|
|
29
32
|
|
|
30
33
|
if (!isTest) {
|
|
31
|
-
app.use(
|
|
34
|
+
app.use(asyncLocalStorageMiddleware())
|
|
32
35
|
}
|
|
33
36
|
|
|
34
37
|
// The request handler must be the first middleware on the app
|
|
@@ -38,13 +41,13 @@ export function createDefaultApp(cfg: DefaultAppCfg): Application {
|
|
|
38
41
|
app.use(sentryService.getRequestHandler())
|
|
39
42
|
}
|
|
40
43
|
|
|
41
|
-
app.use(
|
|
42
|
-
app.use(
|
|
44
|
+
app.use(methodOverrideMiddleware())
|
|
45
|
+
app.use(requestTimeoutMiddleware())
|
|
43
46
|
// app.use(serverStatsMiddleware()) // disabled by default
|
|
44
47
|
// app.use(bodyParserTimeout()) // removed by default
|
|
45
48
|
|
|
46
49
|
if (!isGAE() && !isTest) {
|
|
47
|
-
app.use(
|
|
50
|
+
app.use(simpleRequestLoggerMiddleware())
|
|
48
51
|
}
|
|
49
52
|
|
|
50
53
|
// app.use(safeJsonMiddleware()) // optional
|
|
@@ -86,19 +89,19 @@ export function createDefaultApp(cfg: DefaultAppCfg): Application {
|
|
|
86
89
|
useHandlers(app, cfg.postHandlers)
|
|
87
90
|
|
|
88
91
|
// Generic 404 handler
|
|
89
|
-
app.use(
|
|
92
|
+
app.use(notFoundMiddleware())
|
|
90
93
|
|
|
91
94
|
// Generic error handler
|
|
92
95
|
// It handles errors, returns proper status, does sentry.captureException(),
|
|
93
96
|
// assigns err.data.errorId from sentry
|
|
94
|
-
app.use(
|
|
97
|
+
app.use(genericErrorMiddleware({ sentryService }))
|
|
95
98
|
|
|
96
99
|
return app
|
|
97
100
|
}
|
|
98
101
|
|
|
99
|
-
function useHandlers(app:
|
|
102
|
+
function useHandlers(app: BackendApplication, handlers: BackendRequestHandlerCfg[] = []): void {
|
|
100
103
|
handlers
|
|
101
|
-
.map<
|
|
104
|
+
.map<BackendRequestHandlerWithPath>(cfg => {
|
|
102
105
|
if (typeof cfg === 'function') {
|
|
103
106
|
return {
|
|
104
107
|
path: '/',
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as fs from 'fs'
|
|
2
2
|
import { _memoFn } from '@naturalcycles/js-lib'
|
|
3
|
-
import type { DeployInfo } from '../deploy
|
|
3
|
+
import type { DeployInfo } from '../deploy'
|
|
4
4
|
|
|
5
5
|
export const getDeployInfo = _memoFn((projectDir: string): DeployInfo => {
|
|
6
6
|
const deployInfoPath = `${projectDir}/deployInfo.json`
|