@catbee/utils 2.1.1 → 2.2.1
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/README.md +1 -1
- package/config/index.cjs +5 -6
- package/config/index.mjs +5 -6
- package/healthz-server/index.cjs +64 -14
- package/healthz-server/index.d.ts +65 -31
- package/healthz-server/index.mjs +64 -14
- package/package.json +1 -1
- package/server/index.cjs +244 -137
- package/server/index.d.ts +70 -32
- package/server/index.mjs +247 -140
- package/types/index.d.ts +142 -47
package/server/index.mjs
CHANGED
|
@@ -26,17 +26,18 @@ import express from 'express';
|
|
|
26
26
|
import http from 'http';
|
|
27
27
|
import https from 'https';
|
|
28
28
|
import { HttpStatusCodes } from '@catbee/utils/http-status-codes';
|
|
29
|
-
import { createFinalErrorResponse
|
|
29
|
+
import { createFinalErrorResponse } from '@catbee/utils/response';
|
|
30
30
|
import { requestId, setupRequestContext, timeout, responseTime, errorHandler } from '@catbee/utils/middleware';
|
|
31
31
|
import { Env } from '@catbee/utils/env';
|
|
32
32
|
import { getLogger } from '@catbee/utils/logger';
|
|
33
|
-
import { ServiceUnavailableException,
|
|
33
|
+
import { ServiceUnavailableException, NotFoundException } from '@catbee/utils/exception';
|
|
34
34
|
import { getCatbeeServerGlobalConfig } from '@catbee/utils/config';
|
|
35
|
-
import {
|
|
35
|
+
import { isPlainObject, deepClone, deepObjMerge } from '@catbee/utils/object';
|
|
36
36
|
import { fileExists, readFile, readFileSync } from '@catbee/utils/fs';
|
|
37
37
|
import { isPort, isHostname } from '@catbee/utils/validation';
|
|
38
38
|
import { optionalRequire } from '@catbee/utils/async';
|
|
39
39
|
import { uuid } from '@catbee/utils/id';
|
|
40
|
+
import { HealthzServer } from '@catbee/utils/healthz-server';
|
|
40
41
|
|
|
41
42
|
var __defProp = Object.defineProperty;
|
|
42
43
|
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
@@ -299,36 +300,53 @@ var ServerConfigBuilder = class {
|
|
|
299
300
|
return this.setEnabled("requestLogging", false);
|
|
300
301
|
}
|
|
301
302
|
/**
|
|
302
|
-
* Configures server
|
|
303
|
+
* Configures the dedicated Healthz probe HTTP server for Kubernetes.
|
|
303
304
|
*
|
|
304
|
-
* @param opts -
|
|
305
|
+
* @param opts - Healthz server configuration options or boolean toggle
|
|
305
306
|
* @returns The builder instance for chaining
|
|
306
|
-
* @default - { path: '/healthz', detailed: true, withGlobalPrefix: false }
|
|
307
307
|
*
|
|
308
308
|
* @example
|
|
309
309
|
* ```typescript
|
|
310
|
-
* builder.
|
|
311
|
-
*
|
|
312
|
-
*
|
|
310
|
+
* builder.withHealthzServer({
|
|
311
|
+
* port: 8282,
|
|
312
|
+
* shutdownDelayMs: 5000,
|
|
313
|
+
* readinessChecks: [
|
|
314
|
+
* { name: 'db', check: () => checkDb() }
|
|
315
|
+
* ]
|
|
313
316
|
* })
|
|
314
317
|
* ```
|
|
315
318
|
*/
|
|
316
|
-
|
|
317
|
-
|
|
319
|
+
withHealthzServer(opts) {
|
|
320
|
+
if (typeof opts === "boolean") {
|
|
321
|
+
this.config.healthzServer = opts;
|
|
322
|
+
} else {
|
|
323
|
+
const current = isPlainObject(this.config.healthzServer) ? deepClone(this.config.healthzServer) : {};
|
|
324
|
+
this.config.healthzServer = deepObjMerge({}, current, {
|
|
325
|
+
enable: true,
|
|
326
|
+
...opts
|
|
327
|
+
});
|
|
328
|
+
}
|
|
318
329
|
return this;
|
|
319
330
|
}
|
|
320
331
|
/**
|
|
321
|
-
* Enables
|
|
322
|
-
*
|
|
332
|
+
* Enables the dedicated Healthz probe HTTP server.
|
|
333
|
+
*
|
|
334
|
+
* @param opts - Optional Healthz server configuration options
|
|
323
335
|
* @returns The builder instance for chaining
|
|
336
|
+
*/
|
|
337
|
+
enableHealthzServer(opts = {}) {
|
|
338
|
+
return this.withHealthzServer({
|
|
339
|
+
...opts,
|
|
340
|
+
enable: true
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* Disables the dedicated Healthz probe HTTP server.
|
|
324
345
|
*
|
|
325
|
-
* @
|
|
326
|
-
* ```typescript
|
|
327
|
-
* builder.disableHealthCheck()
|
|
328
|
-
* ```
|
|
346
|
+
* @returns The builder instance for chaining
|
|
329
347
|
*/
|
|
330
|
-
|
|
331
|
-
return this.
|
|
348
|
+
disableHealthzServer() {
|
|
349
|
+
return this.withHealthzServer(false);
|
|
332
350
|
}
|
|
333
351
|
/**
|
|
334
352
|
* Configures OpenAPI/Swagger documentation for the API.
|
|
@@ -718,11 +736,12 @@ var ExpressServer = class {
|
|
|
718
736
|
gracefulShutdownRegistered = false;
|
|
719
737
|
/** Map of registered signal listeners for clean teardown */
|
|
720
738
|
signalListeners = /* @__PURE__ */ new Map();
|
|
721
|
-
/**
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
739
|
+
/** Running address info for the Healthz probe server */
|
|
740
|
+
healthzAddress;
|
|
741
|
+
/** Named checks queued for Healthz liveness probe */
|
|
742
|
+
healthzChecks = [];
|
|
743
|
+
/** Named checks queued for Healthz readiness probe */
|
|
744
|
+
healthzReadinessChecks = [];
|
|
726
745
|
/** Promise that resolves when initialization (middleware + routes) is complete */
|
|
727
746
|
initPromise;
|
|
728
747
|
/** In-flight start promise to protect against concurrent start() calls */
|
|
@@ -757,8 +776,19 @@ var ExpressServer = class {
|
|
|
757
776
|
getLogger().error(msg);
|
|
758
777
|
throw new Error(msg);
|
|
759
778
|
}
|
|
760
|
-
if (config
|
|
761
|
-
this.
|
|
779
|
+
if (typeof this.config.healthzServer === "boolean") {
|
|
780
|
+
this.config.healthzServer = {
|
|
781
|
+
...HealthzServer.getDefaultConfig(),
|
|
782
|
+
enable: this.config.healthzServer
|
|
783
|
+
};
|
|
784
|
+
}
|
|
785
|
+
if (this.config.healthzServer && typeof this.config.healthzServer === "object") {
|
|
786
|
+
if (this.config.healthzServer.checks) {
|
|
787
|
+
this.healthzChecks.push(...this.config.healthzServer.checks);
|
|
788
|
+
}
|
|
789
|
+
if (this.config.healthzServer.readinessChecks) {
|
|
790
|
+
this.healthzReadinessChecks.push(...this.config.healthzServer.readinessChecks);
|
|
791
|
+
}
|
|
762
792
|
}
|
|
763
793
|
this.globalPrefix = this.normalizePath(this.config.globalPrefix ?? "", false);
|
|
764
794
|
this.hooks = hooks;
|
|
@@ -1106,10 +1136,6 @@ var ExpressServer = class {
|
|
|
1106
1136
|
* 4. Error handler
|
|
1107
1137
|
*/
|
|
1108
1138
|
async setupRoutes() {
|
|
1109
|
-
const healthCheckPath = this.normalizePath(this.config.healthCheck?.path || "/healthz", this.config.healthCheck?.withGlobalPrefix);
|
|
1110
|
-
this.app.get(healthCheckPath, async (_req, res) => {
|
|
1111
|
-
return this.handleHealthCheckRequest(res);
|
|
1112
|
-
});
|
|
1113
1139
|
this.app.use(this.globalPrefix, this.rootRouter);
|
|
1114
1140
|
await this.runHook("afterRoutes", this.app);
|
|
1115
1141
|
this.app.use((req, res) => {
|
|
@@ -1133,60 +1159,25 @@ var ExpressServer = class {
|
|
|
1133
1159
|
});
|
|
1134
1160
|
}
|
|
1135
1161
|
/**
|
|
1136
|
-
*
|
|
1162
|
+
* Whether the Healthz probe server is enabled.
|
|
1137
1163
|
*/
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
return res.status(HttpStatusCodes.OK).json(new SuccessResponse("OK"));
|
|
1142
|
-
}
|
|
1143
|
-
const results = await this.executeHealthChecks();
|
|
1144
|
-
const allOk = results.every((r) => r.status);
|
|
1145
|
-
const status = allOk ? HttpStatusCodes.OK : HttpStatusCodes.SERVICE_UNAVAILABLE;
|
|
1146
|
-
const response = new SuccessResponse(allOk ? "OK" : "Service unavailable");
|
|
1147
|
-
if (!allOk) response.error = true;
|
|
1148
|
-
if (this.config.healthCheck?.detailed) response.data = {
|
|
1149
|
-
checks: results
|
|
1150
|
-
};
|
|
1151
|
-
return res.status(status).json(response);
|
|
1152
|
-
} catch {
|
|
1153
|
-
return res.status(HttpStatusCodes.INTERNAL_SERVER_ERROR).json(new InternalServerErrorException("Health check failed"));
|
|
1164
|
+
isHealthzServerEnabled() {
|
|
1165
|
+
if (typeof this.config.healthzServer === "boolean") {
|
|
1166
|
+
return this.config.healthzServer;
|
|
1154
1167
|
}
|
|
1168
|
+
return this.config.healthzServer?.enable === true;
|
|
1155
1169
|
}
|
|
1156
1170
|
/**
|
|
1157
|
-
*
|
|
1171
|
+
* Get the graceful shutdown delay in milliseconds configured for HealthzServer.
|
|
1158
1172
|
*/
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
try {
|
|
1162
|
-
const status = await Promise.resolve(check());
|
|
1163
|
-
return {
|
|
1164
|
-
name,
|
|
1165
|
-
status,
|
|
1166
|
-
error: null
|
|
1167
|
-
};
|
|
1168
|
-
} catch (error) {
|
|
1169
|
-
return {
|
|
1170
|
-
name,
|
|
1171
|
-
status: false,
|
|
1172
|
-
error: error.message
|
|
1173
|
-
};
|
|
1174
|
-
}
|
|
1175
|
-
}));
|
|
1176
|
-
return checkResults.map((result) => {
|
|
1177
|
-
if (result.status === "fulfilled") return result.value;
|
|
1178
|
-
return {
|
|
1179
|
-
name: "unknown",
|
|
1180
|
-
status: false,
|
|
1181
|
-
error: result.reason
|
|
1182
|
-
};
|
|
1183
|
-
});
|
|
1173
|
+
getHealthzShutdownDelay() {
|
|
1174
|
+
return typeof this.config.healthzServer === "object" ? this.config.healthzServer.shutdownDelayMs ?? 0 : 0;
|
|
1184
1175
|
}
|
|
1185
1176
|
/**
|
|
1186
|
-
* Register a new health check function for monitoring service dependencies.
|
|
1177
|
+
* Register a new health check function for monitoring service dependencies on the Healthz probe server.
|
|
1187
1178
|
*
|
|
1188
|
-
*
|
|
1189
|
-
*
|
|
1179
|
+
* By default, external dependencies (DB, Redis, etc.) are registered as `readiness` checks.
|
|
1180
|
+
* Can also be registered as `liveness` or `both`.
|
|
1190
1181
|
*
|
|
1191
1182
|
* Examples:
|
|
1192
1183
|
* - Database connectivity
|
|
@@ -1195,33 +1186,76 @@ var ExpressServer = class {
|
|
|
1195
1186
|
* - Memory/CPU usage checks
|
|
1196
1187
|
*
|
|
1197
1188
|
* @param name Unique identifier for the check (used in detailed responses)
|
|
1198
|
-
* @param check Function returning boolean or Promise<boolean> indicating health
|
|
1189
|
+
* @param check Function returning boolean or Promise<boolean> indicating health (supports optional AbortSignal)
|
|
1190
|
+
* @param options Target probe type ('readiness' | 'liveness' | 'both') or options object
|
|
1199
1191
|
* @returns This instance for method chaining
|
|
1200
1192
|
*/
|
|
1201
|
-
registerHealthCheck(name, check) {
|
|
1202
|
-
|
|
1193
|
+
registerHealthCheck(name, check, options) {
|
|
1194
|
+
const probeType = typeof options === "string" ? options : options?.type ?? "readiness";
|
|
1195
|
+
const namedCheck = {
|
|
1203
1196
|
name,
|
|
1204
1197
|
check
|
|
1205
|
-
}
|
|
1198
|
+
};
|
|
1199
|
+
if (probeType === "liveness" || probeType === "both") {
|
|
1200
|
+
this.healthzChecks.push(namedCheck);
|
|
1201
|
+
}
|
|
1202
|
+
if (probeType === "readiness" || probeType === "both") {
|
|
1203
|
+
this.healthzReadinessChecks.push(namedCheck);
|
|
1204
|
+
}
|
|
1205
|
+
HealthzServer.registerCheck(namedCheck, probeType);
|
|
1206
1206
|
return this;
|
|
1207
1207
|
}
|
|
1208
1208
|
/**
|
|
1209
|
-
*
|
|
1210
|
-
* Useful for readiness probes in deployment tooling.
|
|
1209
|
+
* Mark the service as ready / not-ready for traffic on the Healthz probe server.
|
|
1211
1210
|
*
|
|
1212
|
-
* @
|
|
1211
|
+
* @param ready Whether the service is ready to receive traffic
|
|
1212
|
+
* @returns This instance for method chaining
|
|
1213
1213
|
*/
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1214
|
+
setReady(ready) {
|
|
1215
|
+
HealthzServer.setReady(ready);
|
|
1216
|
+
return this;
|
|
1217
|
+
}
|
|
1218
|
+
/**
|
|
1219
|
+
* Whether the service is currently marked as ready for traffic on the Healthz probe server.
|
|
1220
|
+
*/
|
|
1221
|
+
isReady() {
|
|
1222
|
+
return HealthzServer.isReady();
|
|
1223
|
+
}
|
|
1224
|
+
/**
|
|
1225
|
+
* Mark application startup as completed on the Healthz probe server (switches `/startupz` to 200).
|
|
1226
|
+
*
|
|
1227
|
+
* @returns This instance for method chaining
|
|
1228
|
+
*/
|
|
1229
|
+
markStartupComplete() {
|
|
1230
|
+
HealthzServer.markStartupComplete();
|
|
1231
|
+
return this;
|
|
1232
|
+
}
|
|
1233
|
+
/**
|
|
1234
|
+
* Whether application startup has completed on the Healthz probe server.
|
|
1235
|
+
*/
|
|
1236
|
+
isStartupComplete() {
|
|
1237
|
+
return HealthzServer.isStartupComplete();
|
|
1238
|
+
}
|
|
1239
|
+
/**
|
|
1240
|
+
* Get the running HealthzServer instance (if started).
|
|
1241
|
+
*/
|
|
1242
|
+
getHealthzServer() {
|
|
1243
|
+
return HealthzServer.getInstance();
|
|
1244
|
+
}
|
|
1245
|
+
/**
|
|
1246
|
+
* Get the address info of the running HealthzServer (if started).
|
|
1247
|
+
*/
|
|
1248
|
+
getHealthzAddress() {
|
|
1249
|
+
return this.healthzAddress;
|
|
1250
|
+
}
|
|
1251
|
+
/**
|
|
1252
|
+
* Returns whether the service is currently marked as ready for traffic on the Healthz probe server.
|
|
1253
|
+
* Useful for readiness checks in deployment tooling.
|
|
1254
|
+
*
|
|
1255
|
+
* @returns `true` when ready, otherwise `false`.
|
|
1256
|
+
*/
|
|
1257
|
+
ready() {
|
|
1258
|
+
return HealthzServer.isReady();
|
|
1225
1259
|
}
|
|
1226
1260
|
/**
|
|
1227
1261
|
* Get the underlying Express application instance.
|
|
@@ -1278,48 +1312,105 @@ var ExpressServer = class {
|
|
|
1278
1312
|
*/
|
|
1279
1313
|
async doStart() {
|
|
1280
1314
|
await this.initPromise;
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1315
|
+
if (this.isHealthzServerEnabled()) {
|
|
1316
|
+
const healthzConfig = {
|
|
1317
|
+
...typeof this.config.healthzServer === "object" ? this.config.healthzServer : {},
|
|
1318
|
+
handleSignals: false,
|
|
1319
|
+
checks: [
|
|
1320
|
+
...this.healthzChecks
|
|
1321
|
+
],
|
|
1322
|
+
readinessChecks: [
|
|
1323
|
+
...this.healthzReadinessChecks
|
|
1324
|
+
]
|
|
1325
|
+
};
|
|
1326
|
+
const addr = await HealthzServer.start(healthzConfig);
|
|
1327
|
+
if (!addr) {
|
|
1328
|
+
throw new Error("Healthz probe server failed to start (already running in this process)");
|
|
1329
|
+
}
|
|
1330
|
+
this.healthzAddress = addr;
|
|
1331
|
+
}
|
|
1332
|
+
try {
|
|
1333
|
+
await this.runHook("beforeStart", this.app);
|
|
1334
|
+
const server = this.createServerInstance();
|
|
1335
|
+
this.server = server;
|
|
1336
|
+
return await new Promise((resolve, reject) => {
|
|
1337
|
+
let isListening = false;
|
|
1338
|
+
server.on("error", async (err) => {
|
|
1339
|
+
if (!isListening) {
|
|
1340
|
+
getLogger().error({
|
|
1341
|
+
err
|
|
1342
|
+
}, "Server failed to start");
|
|
1343
|
+
try {
|
|
1344
|
+
server.removeAllListeners();
|
|
1345
|
+
server.close();
|
|
1346
|
+
if (HealthzServer.isRunning()) {
|
|
1347
|
+
await HealthzServer.stop().catch(() => {
|
|
1348
|
+
});
|
|
1349
|
+
}
|
|
1350
|
+
} catch {
|
|
1351
|
+
}
|
|
1352
|
+
this.server = null;
|
|
1353
|
+
this.healthzAddress = null;
|
|
1354
|
+
this.connections.clear();
|
|
1355
|
+
reject(err);
|
|
1356
|
+
} else {
|
|
1357
|
+
getLogger().error({
|
|
1358
|
+
err
|
|
1359
|
+
}, "Server runtime error");
|
|
1295
1360
|
}
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1361
|
+
});
|
|
1362
|
+
this.setupConnectionTracking();
|
|
1363
|
+
Promise.resolve(this.runHook("onServerCreated", server)).then(() => {
|
|
1364
|
+
const onListening = /* @__PURE__ */ __name(async () => {
|
|
1365
|
+
try {
|
|
1366
|
+
this.logServerStartInfo();
|
|
1367
|
+
await this.runHook("afterStart", server);
|
|
1368
|
+
if (this.isHealthzServerEnabled()) {
|
|
1369
|
+
HealthzServer.markStartupComplete();
|
|
1370
|
+
HealthzServer.setReady(true);
|
|
1371
|
+
}
|
|
1372
|
+
isListening = true;
|
|
1373
|
+
resolve(server);
|
|
1374
|
+
} catch (err) {
|
|
1375
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
1376
|
+
getLogger().error({
|
|
1377
|
+
err: error
|
|
1378
|
+
}, "Server startup failed");
|
|
1379
|
+
if (HealthzServer.isRunning()) {
|
|
1380
|
+
await HealthzServer.stop().catch(() => {
|
|
1381
|
+
});
|
|
1382
|
+
}
|
|
1383
|
+
try {
|
|
1384
|
+
server.removeAllListeners();
|
|
1385
|
+
server.close();
|
|
1386
|
+
} catch {
|
|
1387
|
+
}
|
|
1388
|
+
this.server = null;
|
|
1389
|
+
this.healthzAddress = null;
|
|
1390
|
+
this.connections.clear();
|
|
1391
|
+
reject(error);
|
|
1392
|
+
}
|
|
1393
|
+
}, "onListening");
|
|
1394
|
+
const listenArgs = [
|
|
1395
|
+
this.config.port,
|
|
1396
|
+
this.config.host,
|
|
1397
|
+
onListening
|
|
1398
|
+
];
|
|
1399
|
+
server.listen(...listenArgs);
|
|
1400
|
+
}).catch((err) => {
|
|
1401
|
+
server.emit("error", err instanceof Error ? err : new Error(String(err)));
|
|
1402
|
+
});
|
|
1321
1403
|
});
|
|
1322
|
-
})
|
|
1404
|
+
} catch (err) {
|
|
1405
|
+
if (HealthzServer.isRunning()) {
|
|
1406
|
+
await HealthzServer.stop().catch(() => {
|
|
1407
|
+
});
|
|
1408
|
+
}
|
|
1409
|
+
this.healthzAddress = null;
|
|
1410
|
+
this.server = null;
|
|
1411
|
+
this.connections.clear();
|
|
1412
|
+
throw err;
|
|
1413
|
+
}
|
|
1323
1414
|
}
|
|
1324
1415
|
/**
|
|
1325
1416
|
* Create HTTP or HTTPS server instance (without listening).
|
|
@@ -1359,8 +1450,8 @@ var ExpressServer = class {
|
|
|
1359
1450
|
const host = this.formatHostForUrl(this.config.host || "localhost");
|
|
1360
1451
|
const url = `${protocol}://${host}:${port}`;
|
|
1361
1452
|
getLogger().info(`Server running on ${url}`);
|
|
1362
|
-
if (this.
|
|
1363
|
-
getLogger().info(`
|
|
1453
|
+
if (this.healthzAddress) {
|
|
1454
|
+
getLogger().info(`Healthz probe server running on http://${this.healthzAddress.address}:${this.healthzAddress.port}`);
|
|
1364
1455
|
}
|
|
1365
1456
|
if (this.config.openApi?.enable) {
|
|
1366
1457
|
getLogger().info(`API docs available at ${url}${this.normalizePath(this.config.openApi.mountPath, this.config.openApi.withGlobalPrefix)}`);
|
|
@@ -1383,7 +1474,7 @@ var ExpressServer = class {
|
|
|
1383
1474
|
* - Monitoring systems are notified
|
|
1384
1475
|
*/
|
|
1385
1476
|
async stop(force = false) {
|
|
1386
|
-
if (!this.server) {
|
|
1477
|
+
if (!this.server && !HealthzServer.isRunning()) {
|
|
1387
1478
|
getLogger().warn("Stop called but server is not running");
|
|
1388
1479
|
return;
|
|
1389
1480
|
}
|
|
@@ -1392,10 +1483,26 @@ var ExpressServer = class {
|
|
|
1392
1483
|
return;
|
|
1393
1484
|
}
|
|
1394
1485
|
this.isShuttingDown = true;
|
|
1395
|
-
|
|
1486
|
+
if (HealthzServer.isRunning()) {
|
|
1487
|
+
HealthzServer.setReady(false);
|
|
1488
|
+
}
|
|
1489
|
+
if (this.server) {
|
|
1490
|
+
await this.runHook("beforeStop", this.server);
|
|
1491
|
+
}
|
|
1396
1492
|
try {
|
|
1397
|
-
|
|
1493
|
+
const shutdownDelay = this.getHealthzShutdownDelay();
|
|
1494
|
+
if (shutdownDelay > 0 && !force && HealthzServer.isRunning()) {
|
|
1495
|
+
getLogger().info(`Waiting ${shutdownDelay}ms for load balancer to drain traffic...`);
|
|
1496
|
+
await new Promise((resolve) => setTimeout(resolve, shutdownDelay));
|
|
1497
|
+
}
|
|
1498
|
+
if (this.server) {
|
|
1499
|
+
await this.gracefulShutdown(force);
|
|
1500
|
+
}
|
|
1398
1501
|
} finally {
|
|
1502
|
+
if (HealthzServer.isRunning()) {
|
|
1503
|
+
await HealthzServer.stop();
|
|
1504
|
+
this.healthzAddress = null;
|
|
1505
|
+
}
|
|
1399
1506
|
this.isShuttingDown = false;
|
|
1400
1507
|
}
|
|
1401
1508
|
}
|
|
@@ -1484,7 +1591,7 @@ var ExpressServer = class {
|
|
|
1484
1591
|
getLogger().info(`Received ${signal}, initiating graceful shutdown...`);
|
|
1485
1592
|
try {
|
|
1486
1593
|
this.disableGracefulShutdown();
|
|
1487
|
-
await this.stop(
|
|
1594
|
+
await this.stop(false);
|
|
1488
1595
|
process.exit(0);
|
|
1489
1596
|
} catch (err) {
|
|
1490
1597
|
getLogger().fatal({
|