@naturalcycles/backend-lib 6.1.0 → 6.3.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/deploy/deployHealthCheck.d.ts +1 -1
- package/dist/deploy/deployHealthCheck.js +2 -2
- package/dist/server/createDefaultApp.js +4 -3
- package/dist/server/genericErrorMiddleware.js +2 -2
- package/dist/server/serverStatusMiddleware.js +6 -1
- package/dist/server/startServer.js +16 -10
- package/dist/server/startServer.model.d.ts +5 -0
- package/package.json +6 -3
- package/src/deploy/deployHealthCheck.ts +3 -3
- package/src/server/createDefaultApp.ts +4 -3
- package/src/server/genericErrorMiddleware.ts +2 -2
- package/src/server/serverStatusMiddleware.ts +7 -1
- package/src/server/startServer.model.ts +6 -0
- package/src/server/startServer.ts +21 -11
|
@@ -13,7 +13,7 @@ exports.deployHealthCheckYargsOptions = {
|
|
|
13
13
|
},
|
|
14
14
|
thresholdUnhealthy: {
|
|
15
15
|
type: 'number',
|
|
16
|
-
default:
|
|
16
|
+
default: 3,
|
|
17
17
|
},
|
|
18
18
|
maxTries: {
|
|
19
19
|
type: 'number',
|
|
@@ -57,7 +57,7 @@ const inspectOpt = {
|
|
|
57
57
|
* Fails after maxTries.
|
|
58
58
|
*/
|
|
59
59
|
async function deployHealthCheck(url, opt = {}) {
|
|
60
|
-
const { thresholdHealthy = 5, thresholdUnhealthy =
|
|
60
|
+
const { thresholdHealthy = 5, thresholdUnhealthy = 3, maxTries = 30, timeoutSec = 30, intervalSec = 1, logOnFailure = true, logOnSuccess, gaeProject, gaeService, gaeVersion, } = opt;
|
|
61
61
|
let attempt = 0;
|
|
62
62
|
let countHealthy = 0;
|
|
63
63
|
let countUnhealthy = 0;
|
|
@@ -75,9 +75,10 @@ function createDefaultApp(cfg) {
|
|
|
75
75
|
useHandlers(app, cfg.postHandlers);
|
|
76
76
|
// Generic 404 handler
|
|
77
77
|
app.use((0, notFoundMiddleware_1.notFoundMiddleware)());
|
|
78
|
-
//
|
|
79
|
-
//
|
|
80
|
-
//
|
|
78
|
+
// currently disabled as not necessary (because genericErrorMiddleware already reports to sentry)
|
|
79
|
+
// if (sentryService) {
|
|
80
|
+
// sentryService.sentry.setupExpressErrorHandler(app)
|
|
81
|
+
// }
|
|
81
82
|
// Generic error handler
|
|
82
83
|
// It handles errors, returns proper status, does sentry.captureException(),
|
|
83
84
|
// assigns err.data.errorId from sentry
|
|
@@ -36,7 +36,7 @@ function genericErrorMiddleware(cfg = {}) {
|
|
|
36
36
|
function respondWithError(req, res, err) {
|
|
37
37
|
const { headersSent } = res;
|
|
38
38
|
if (headersSent) {
|
|
39
|
-
req.error(`after headersSent
|
|
39
|
+
req.error(`error after headersSent:`, err);
|
|
40
40
|
}
|
|
41
41
|
else {
|
|
42
42
|
req.error(err);
|
|
@@ -46,7 +46,7 @@ function respondWithError(req, res, err) {
|
|
|
46
46
|
if (sentryService && shouldReportToSentry(originalError)) {
|
|
47
47
|
errorId = sentryService.captureException(originalError, false);
|
|
48
48
|
}
|
|
49
|
-
if (
|
|
49
|
+
if (headersSent)
|
|
50
50
|
return;
|
|
51
51
|
const httpError = (0, js_lib_1._errorLikeToErrorObject)(originalError);
|
|
52
52
|
if (!includeErrorStack)
|
|
@@ -17,6 +17,7 @@ function getServerStatusData(projectDir = process.cwd(), extra) {
|
|
|
17
17
|
const t = (0, js_lib_1.localTime)(ts);
|
|
18
18
|
const deployBuildTime = t.toPretty();
|
|
19
19
|
const buildInfo = [t.toStringCompact(), gitBranch, gitRev].filter(Boolean).join('_');
|
|
20
|
+
const { arch, platform } = process;
|
|
20
21
|
return (0, js_lib_1._filterNullishValues)({
|
|
21
22
|
started: getStartedStr(),
|
|
22
23
|
deployBuildTime,
|
|
@@ -25,9 +26,13 @@ function getServerStatusData(projectDir = process.cwd(), extra) {
|
|
|
25
26
|
GAE_APPLICATION,
|
|
26
27
|
GAE_SERVICE,
|
|
27
28
|
GAE_VERSION,
|
|
29
|
+
processInfo: {
|
|
30
|
+
arch,
|
|
31
|
+
platform,
|
|
32
|
+
},
|
|
28
33
|
mem: (0, nodejs_lib_1.memoryUsageFull)(),
|
|
29
34
|
cpuAvg: nodejs_lib_1.processSharedUtil.cpuAvg(),
|
|
30
|
-
|
|
35
|
+
cpuInfo: nodejs_lib_1.processSharedUtil.cpuInfo(),
|
|
31
36
|
versions,
|
|
32
37
|
NODE_OPTIONS,
|
|
33
38
|
...extra,
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.BackendServer = void 0;
|
|
4
4
|
exports.startServer = startServer;
|
|
5
5
|
const tslib_1 = require("tslib");
|
|
6
|
+
const node_os_1 = tslib_1.__importDefault(require("node:os"));
|
|
6
7
|
const js_lib_1 = require("@naturalcycles/js-lib");
|
|
7
8
|
const nodejs_lib_1 = require("@naturalcycles/nodejs-lib");
|
|
8
9
|
const index_1 = require("../index");
|
|
@@ -12,16 +13,18 @@ class BackendServer {
|
|
|
12
13
|
this.cfg = cfg;
|
|
13
14
|
}
|
|
14
15
|
async start() {
|
|
15
|
-
const { port: cfgPort, expressApp = (0, index_1.createDefaultApp)(this.cfg) } = this.cfg;
|
|
16
|
+
const { port: cfgPort, expressApp = (0, index_1.createDefaultApp)(this.cfg), registerUncaughtExceptionHandlers = true, } = this.cfg;
|
|
16
17
|
// 1. Register error handlers, etc.
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
18
|
+
if (registerUncaughtExceptionHandlers) {
|
|
19
|
+
process.on('uncaughtException', err => {
|
|
20
|
+
console.error('BackendServer uncaughtException:', err);
|
|
21
|
+
this.cfg.sentryService?.captureException(err, false);
|
|
22
|
+
});
|
|
23
|
+
process.on('unhandledRejection', err => {
|
|
24
|
+
console.error('BackendServer unhandledRejection:', err);
|
|
25
|
+
this.cfg.sentryService?.captureException(err, false);
|
|
26
|
+
});
|
|
27
|
+
}
|
|
25
28
|
process.once('SIGINT', () => this.stop('SIGINT'));
|
|
26
29
|
process.once('SIGTERM', () => this.stop('SIGTERM'));
|
|
27
30
|
// sentryService.install()
|
|
@@ -46,7 +49,10 @@ class BackendServer {
|
|
|
46
49
|
address = `http://${addr.address}:${port}`;
|
|
47
50
|
}
|
|
48
51
|
}
|
|
49
|
-
|
|
52
|
+
const cpus = node_os_1.default.cpus().length;
|
|
53
|
+
const availableParallelism = node_os_1.default.availableParallelism?.();
|
|
54
|
+
const { version, platform, arch } = process;
|
|
55
|
+
console.log((0, nodejs_lib_1.dimGrey)(`node ${version} ${platform} ${arch}, NODE_OPTIONS: ${NODE_OPTIONS || 'undefined'}, APP_ENV: ${APP_ENV || 'undefined'}, cpus: ${cpus}, availableParallelism: ${availableParallelism}`));
|
|
50
56
|
console.log(`serverStarted on ${(0, nodejs_lib_1.white)(address)} in ${(0, nodejs_lib_1.dimGrey)((0, js_lib_1._ms)(process.uptime() * 1000))}`);
|
|
51
57
|
return {
|
|
52
58
|
port,
|
|
@@ -21,6 +21,11 @@ export interface StartServerCfg extends DefaultAppCfg {
|
|
|
21
21
|
*/
|
|
22
22
|
forceShutdownTimeout?: number;
|
|
23
23
|
sentryService?: SentrySharedService;
|
|
24
|
+
/**
|
|
25
|
+
* Defaults to true.
|
|
26
|
+
* Set to false if you already have your handlers elsewhere and don't need them here.
|
|
27
|
+
*/
|
|
28
|
+
registerUncaughtExceptionHandlers?: boolean;
|
|
24
29
|
}
|
|
25
30
|
export interface StartServerData {
|
|
26
31
|
port: number;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@naturalcycles/backend-lib",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.3.0",
|
|
4
4
|
"scripts": {
|
|
5
5
|
"prepare": "husky",
|
|
6
6
|
"build": "dev-lib build",
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"deploy-health-check-debug2": "yarn tsn ./src/bin/deploy-health-check.ts --url https://api-master2.naturalcycles.com --thresholdUnhealthy 5"
|
|
19
19
|
},
|
|
20
20
|
"peerDependencies": {
|
|
21
|
-
"@sentry/node": "^
|
|
21
|
+
"@sentry/node": "^9"
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
24
|
"@naturalcycles/db-lib": "^9",
|
|
@@ -43,13 +43,16 @@
|
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"@naturalcycles/bench-lib": "^3",
|
|
45
45
|
"@naturalcycles/dev-lib": "^15",
|
|
46
|
-
"@sentry/node": "^
|
|
46
|
+
"@sentry/node": "^9",
|
|
47
47
|
"@types/ejs": "^3",
|
|
48
48
|
"@types/node": "^22",
|
|
49
49
|
"@types/yargs": "^16",
|
|
50
50
|
"fastify": "^5",
|
|
51
51
|
"jest": "^29"
|
|
52
52
|
},
|
|
53
|
+
"resolutions": {
|
|
54
|
+
"long": "5.2.4"
|
|
55
|
+
},
|
|
53
56
|
"files": [
|
|
54
57
|
"dist",
|
|
55
58
|
"cfg",
|
|
@@ -30,7 +30,7 @@ export const deployHealthCheckYargsOptions = {
|
|
|
30
30
|
},
|
|
31
31
|
thresholdUnhealthy: {
|
|
32
32
|
type: 'number',
|
|
33
|
-
default:
|
|
33
|
+
default: 3,
|
|
34
34
|
},
|
|
35
35
|
maxTries: {
|
|
36
36
|
type: 'number',
|
|
@@ -81,8 +81,8 @@ export async function deployHealthCheck(
|
|
|
81
81
|
): Promise<void> {
|
|
82
82
|
const {
|
|
83
83
|
thresholdHealthy = 5,
|
|
84
|
-
thresholdUnhealthy =
|
|
85
|
-
maxTries =
|
|
84
|
+
thresholdUnhealthy = 3,
|
|
85
|
+
maxTries = 30,
|
|
86
86
|
timeoutSec = 30,
|
|
87
87
|
intervalSec = 1,
|
|
88
88
|
logOnFailure = true,
|
|
@@ -110,9 +110,10 @@ export function createDefaultApp(cfg: DefaultAppCfg): BackendApplication {
|
|
|
110
110
|
// Generic 404 handler
|
|
111
111
|
app.use(notFoundMiddleware())
|
|
112
112
|
|
|
113
|
-
//
|
|
114
|
-
//
|
|
115
|
-
//
|
|
113
|
+
// currently disabled as not necessary (because genericErrorMiddleware already reports to sentry)
|
|
114
|
+
// if (sentryService) {
|
|
115
|
+
// sentryService.sentry.setupExpressErrorHandler(app)
|
|
116
|
+
// }
|
|
116
117
|
|
|
117
118
|
// Generic error handler
|
|
118
119
|
// It handles errors, returns proper status, does sentry.captureException(),
|
|
@@ -66,7 +66,7 @@ export function respondWithError(req: BackendRequest, res: BackendResponse, err:
|
|
|
66
66
|
const { headersSent } = res
|
|
67
67
|
|
|
68
68
|
if (headersSent) {
|
|
69
|
-
req.error(`after headersSent
|
|
69
|
+
req.error(`error after headersSent:`, err)
|
|
70
70
|
} else {
|
|
71
71
|
req.error(err)
|
|
72
72
|
}
|
|
@@ -79,7 +79,7 @@ export function respondWithError(req: BackendRequest, res: BackendResponse, err:
|
|
|
79
79
|
errorId = sentryService.captureException(originalError, false)
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
-
if (
|
|
82
|
+
if (headersSent) return
|
|
83
83
|
|
|
84
84
|
const httpError = _errorLikeToErrorObject(originalError)
|
|
85
85
|
if (!includeErrorStack) delete httpError.stack
|
|
@@ -21,6 +21,8 @@ export function getServerStatusData(
|
|
|
21
21
|
const deployBuildTime = t.toPretty()
|
|
22
22
|
const buildInfo = [t.toStringCompact(), gitBranch, gitRev].filter(Boolean).join('_')
|
|
23
23
|
|
|
24
|
+
const { arch, platform } = process
|
|
25
|
+
|
|
24
26
|
return _filterNullishValues({
|
|
25
27
|
started: getStartedStr(),
|
|
26
28
|
deployBuildTime,
|
|
@@ -29,9 +31,13 @@ export function getServerStatusData(
|
|
|
29
31
|
GAE_APPLICATION,
|
|
30
32
|
GAE_SERVICE,
|
|
31
33
|
GAE_VERSION,
|
|
34
|
+
processInfo: {
|
|
35
|
+
arch,
|
|
36
|
+
platform,
|
|
37
|
+
},
|
|
32
38
|
mem: memoryUsageFull(),
|
|
33
39
|
cpuAvg: processSharedUtil.cpuAvg(),
|
|
34
|
-
|
|
40
|
+
cpuInfo: processSharedUtil.cpuInfo(),
|
|
35
41
|
versions,
|
|
36
42
|
NODE_OPTIONS,
|
|
37
43
|
...extra,
|
|
@@ -26,6 +26,12 @@ export interface StartServerCfg extends DefaultAppCfg {
|
|
|
26
26
|
forceShutdownTimeout?: number
|
|
27
27
|
|
|
28
28
|
sentryService?: SentrySharedService
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Defaults to true.
|
|
32
|
+
* Set to false if you already have your handlers elsewhere and don't need them here.
|
|
33
|
+
*/
|
|
34
|
+
registerUncaughtExceptionHandlers?: boolean
|
|
29
35
|
}
|
|
30
36
|
|
|
31
37
|
export interface StartServerData {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Server } from 'node:http'
|
|
2
|
+
import os from 'node:os'
|
|
2
3
|
import { _Memo, _ms } from '@naturalcycles/js-lib'
|
|
3
4
|
import { boldGrey, dimGrey, white } from '@naturalcycles/nodejs-lib'
|
|
4
5
|
import { createDefaultApp } from '../index'
|
|
@@ -12,18 +13,24 @@ export class BackendServer {
|
|
|
12
13
|
server?: Server
|
|
13
14
|
|
|
14
15
|
async start(): Promise<StartServerData> {
|
|
15
|
-
const {
|
|
16
|
+
const {
|
|
17
|
+
port: cfgPort,
|
|
18
|
+
expressApp = createDefaultApp(this.cfg),
|
|
19
|
+
registerUncaughtExceptionHandlers = true,
|
|
20
|
+
} = this.cfg
|
|
16
21
|
|
|
17
22
|
// 1. Register error handlers, etc.
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
23
|
+
if (registerUncaughtExceptionHandlers) {
|
|
24
|
+
process.on('uncaughtException', err => {
|
|
25
|
+
console.error('BackendServer uncaughtException:', err)
|
|
26
|
+
this.cfg.sentryService?.captureException(err, false)
|
|
27
|
+
})
|
|
22
28
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
29
|
+
process.on('unhandledRejection', err => {
|
|
30
|
+
console.error('BackendServer unhandledRejection:', err)
|
|
31
|
+
this.cfg.sentryService?.captureException(err, false)
|
|
32
|
+
})
|
|
33
|
+
}
|
|
27
34
|
|
|
28
35
|
process.once('SIGINT', () => this.stop('SIGINT'))
|
|
29
36
|
process.once('SIGTERM', () => this.stop('SIGTERM'))
|
|
@@ -54,11 +61,14 @@ export class BackendServer {
|
|
|
54
61
|
}
|
|
55
62
|
}
|
|
56
63
|
|
|
64
|
+
const cpus = os.cpus().length
|
|
65
|
+
const availableParallelism = os.availableParallelism?.()
|
|
66
|
+
const { version, platform, arch } = process
|
|
57
67
|
console.log(
|
|
58
68
|
dimGrey(
|
|
59
|
-
`node ${
|
|
69
|
+
`node ${version} ${platform} ${arch}, NODE_OPTIONS: ${NODE_OPTIONS || 'undefined'}, APP_ENV: ${
|
|
60
70
|
APP_ENV || 'undefined'
|
|
61
|
-
}`,
|
|
71
|
+
}, cpus: ${cpus}, availableParallelism: ${availableParallelism}`,
|
|
62
72
|
),
|
|
63
73
|
)
|
|
64
74
|
console.log(`serverStarted on ${white(address)} in ${dimGrey(_ms(process.uptime() * 1000))}`)
|