badmfck-api-server 4.1.26 → 4.1.27

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.
@@ -37,6 +37,7 @@ export interface IEndpointHandler {
37
37
  resultModel?: any;
38
38
  }
39
39
  export declare class BaseEndpoint implements IBaseEndpoint {
40
+ static allInstances: BaseEndpoint[];
40
41
  private inializedEndpointNames;
41
42
  description: string;
42
43
  endpoints?: IEndpointHandler[];
@@ -44,6 +45,8 @@ export declare class BaseEndpoint implements IBaseEndpoint {
44
45
  ignoreInDocumentation: boolean;
45
46
  private static entrypoint;
46
47
  endpoint: string;
48
+ createdAt: number;
49
+ initializedAt: number | null;
47
50
  protected streamOptions: Record<string, any>;
48
51
  static setEntryPoint: (ep: string) => void;
49
52
  static getEntryPoint: () => string;
@@ -51,6 +54,29 @@ export declare class BaseEndpoint implements IBaseEndpoint {
51
54
  registerEndpoints(endpoints: IEndpointHandler[]): void;
52
55
  init(): Promise<void>;
53
56
  getInitalizedEndpointNames(): String[];
57
+ getInventory(): {
58
+ className: string;
59
+ basePath: string;
60
+ ignoreHttpLogging: boolean;
61
+ ignoreInDocumentation: boolean;
62
+ description: string;
63
+ createdAt: number;
64
+ initializedAt: number | null;
65
+ handlerCount: number;
66
+ handlers: {
67
+ path: string;
68
+ methods: string[];
69
+ ignoreInterceptor: boolean;
70
+ ignoreInDocumentation: boolean;
71
+ ignoreHttpLogging: boolean;
72
+ independed: boolean;
73
+ asStream: boolean;
74
+ hasValidationModel: boolean;
75
+ hasResultModel: boolean;
76
+ description?: string | undefined;
77
+ title?: string | undefined;
78
+ }[];
79
+ };
54
80
  __precheck(req: HTTPRequestVO): Promise<TransferPacketVO<any> | null>;
55
81
  __execute(req: HTTPRequestVO): Promise<TransferPacketVO<any>>;
56
82
  protected validateStructure(structure: any, req: HTTPRequestVO): Promise<void>;
@@ -10,6 +10,7 @@ const DefaultErrors_1 = __importDefault(require("./structures/DefaultErrors"));
10
10
  const stream_1 = require("stream");
11
11
  const httpMethods = ["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS", "HEAD", "TRACE", "CONNECT"];
12
12
  class BaseEndpoint {
13
+ static allInstances = [];
13
14
  inializedEndpointNames = [];
14
15
  description = "no description";
15
16
  endpoints;
@@ -17,6 +18,8 @@ class BaseEndpoint {
17
18
  ignoreInDocumentation = false;
18
19
  static entrypoint = "/";
19
20
  endpoint = "";
21
+ createdAt = Date.now();
22
+ initializedAt = null;
20
23
  streamOptions = {};
21
24
  static setEntryPoint = (ep) => {
22
25
  this.entrypoint = ep;
@@ -35,6 +38,7 @@ class BaseEndpoint {
35
38
  if (!endpoint.endsWith("/"))
36
39
  endpoint = endpoint + "/";
37
40
  this.endpoint = endpoint;
41
+ BaseEndpoint.allInstances.push(this);
38
42
  }
39
43
  registerEndpoints(endpoints) {
40
44
  for (let i of endpoints) {
@@ -63,11 +67,65 @@ class BaseEndpoint {
63
67
  (0, LogService_1.logInfo)("${BaseEndpoint.js}", "endpoint: " + i.endpoint + " initalized, ignoreInterceptor: " + (i.ignoreInterceptor ?? "false"));
64
68
  this.inializedEndpointNames.push(i.endpoint + " ignore interceptor: " + (i.ignoreInterceptor ?? "false"));
65
69
  }
70
+ this.initializedAt = Date.now();
66
71
  }
67
72
  ;
68
73
  getInitalizedEndpointNames() {
69
74
  return this.inializedEndpointNames;
70
75
  }
76
+ getInventory() {
77
+ const handlers = [];
78
+ for (const i of this.endpoints ?? []) {
79
+ let methods = [];
80
+ let asStream = i.asStream === true;
81
+ let hasVm = i.validationModel !== undefined;
82
+ let hasRm = i.resultModel !== undefined;
83
+ if (typeof i.handler === "function") {
84
+ methods = ["ANY"];
85
+ }
86
+ else if (i.handler && typeof i.handler === "object") {
87
+ for (const key of Object.keys(i.handler)) {
88
+ const h = i.handler[key];
89
+ if (typeof h === "function") {
90
+ methods.push(key);
91
+ }
92
+ else if (h && typeof h === "object" && typeof h.controller === "function") {
93
+ methods.push(key);
94
+ if (h.asStream)
95
+ asStream = true;
96
+ if (h.validationModel !== undefined)
97
+ hasVm = true;
98
+ if (h.resultModel !== undefined)
99
+ hasRm = true;
100
+ }
101
+ }
102
+ }
103
+ handlers.push({
104
+ path: (BaseEndpoint.entrypoint + i.endpoint).replaceAll("//", "/"),
105
+ methods,
106
+ ignoreInterceptor: i.ignoreInterceptor === true,
107
+ ignoreInDocumentation: i.ignoreInDocumentation === true,
108
+ ignoreHttpLogging: i.ignoreHttpLogging === true,
109
+ independed: i.independed === true,
110
+ asStream,
111
+ hasValidationModel: hasVm,
112
+ hasResultModel: hasRm,
113
+ description: i.description,
114
+ title: i.title,
115
+ });
116
+ }
117
+ return {
118
+ className: this.constructor.name,
119
+ basePath: this.endpoint,
120
+ ignoreHttpLogging: this.ignoreHttpLogging,
121
+ ignoreInDocumentation: this.ignoreInDocumentation,
122
+ description: this.description,
123
+ createdAt: this.createdAt,
124
+ initializedAt: this.initializedAt,
125
+ handlerCount: handlers.length,
126
+ handlers,
127
+ };
128
+ }
71
129
  async __precheck(req) { return null; }
72
130
  async __execute(req) {
73
131
  if (this.endpoints && this.endpoints.length > 0) {
@@ -4,7 +4,11 @@ export interface IBaseService {
4
4
  getName: () => string;
5
5
  }
6
6
  export declare class BaseService implements IBaseService {
7
+ static allInstances: BaseService[];
7
8
  protected name: string;
9
+ createdAt: number;
10
+ initializedAt: number | null;
11
+ applicationReadyAt: number | null;
8
12
  constructor(name: string);
9
13
  init(): Promise<void>;
10
14
  finishService(): Promise<void>;
@@ -11,14 +11,20 @@ process.on('SIGTERM', async () => {
11
11
  process.exit(0);
12
12
  });
13
13
  class BaseService {
14
+ static allInstances = [];
14
15
  name = "BaseService";
16
+ createdAt = Date.now();
17
+ initializedAt = null;
18
+ applicationReadyAt = null;
15
19
  constructor(name) {
16
20
  this.name = name;
17
21
  this.finishService = this.finishService.bind(this);
22
+ BaseService.allInstances.push(this);
18
23
  }
19
24
  async init() {
20
25
  console.log("Service: " + this.name + " initialized");
21
26
  finalize.push(this.finishService);
27
+ this.initializedAt = Date.now();
22
28
  }
23
29
  async finishService() {
24
30
  console.log("finishService -> " + (this.name ?? "no-name"));
@@ -26,6 +32,8 @@ class BaseService {
26
32
  getName() {
27
33
  return this.name;
28
34
  }
29
- applicationReady() { }
35
+ applicationReady() {
36
+ this.applicationReadyAt = Date.now();
37
+ }
30
38
  }
31
39
  exports.BaseService = BaseService;
@@ -62,4 +62,9 @@ export declare class LogService extends BaseService {
62
62
  createDate(d?: Date): string;
63
63
  leadZero(num: number): string;
64
64
  createText(data: any, level: LOG_LEVEL): string;
65
+ private static readonly SANITIZERS;
66
+ static sanitize(text: string): string;
67
+ private static readonly PAN_CANDIDATE_REGEX;
68
+ private static isLuhnValid;
69
+ static maskPAN(text: string): string;
65
70
  }
@@ -244,7 +244,54 @@ class LogService extends BaseService_1.BaseService {
244
244
  }
245
245
  if (res.length > this.options.textLimit && level === LOG_LEVEL.INFO)
246
246
  res = res.substring(0, this.options.textLimit) + "... (total: " + res.length + ")";
247
+ res = LogService.sanitize(res);
247
248
  return res;
248
249
  }
250
+ static SANITIZERS = [
251
+ [/-----BEGIN [A-Z0-9 ]+-----[\s\S]+?-----END [A-Z0-9 ]+-----/g, "<REDACTED PEM>"],
252
+ [/eyJ[A-Za-z0-9_-]{5,}\.eyJ[A-Za-z0-9_-]{5,}\.[A-Za-z0-9_-]+/g, "<REDACTED JWT>"],
253
+ [/(Bearer|Basic|Digest)\s+[A-Za-z0-9\-._~+\/=]+/gi, "$1 <REDACTED>"],
254
+ [/%B\d{13,19}\^[^?]{2,50}\^\d{4,}[^?]*\?/g, "<REDACTED TRACK1>"],
255
+ [/;\d{13,19}=\d{4,}[^?]*\?/g, "<REDACTED TRACK2>"],
256
+ [/("(?:password|passwd|pwd|secret|api[_-]?key|api[_-]?secret|client[_-]?secret|access[_-]?key|private[_-]?key|access[_-]?token|refresh[_-]?token|id[_-]?token|token|authorization|cookie|set[_-]?cookie|session[_-]?id|sess[_-]?id|sid|cvv2?|cvc2?|cvn|cav2?|cid|security[_-]?code|card[_-]?verification[_-]?(?:value|code)?|pin|pin[_-]?block|encrypted[_-]?pin|iso[_-]?9564|exp|expiry|expiration|expiration[_-]?date|exp[_-]?date|exp[_-]?month|exp[_-]?year|cardholder[_-]?name|holder[_-]?name|card[_-]?holder)"\s*:\s*)"[^"]*"/gi, '$1"<REDACTED>"'],
257
+ [/("(?:cvv2?|cvc2?|pin|exp[_-]?month|exp[_-]?year|service[_-]?code)"\s*:\s*)(-?\d+)/gi, '$1"<REDACTED>"'],
258
+ ];
259
+ static sanitize(text) {
260
+ if (!text || text.length < 3)
261
+ return text;
262
+ let out = text;
263
+ for (const [re, repl] of LogService.SANITIZERS)
264
+ out = out.replace(re, repl);
265
+ return LogService.maskPAN(out);
266
+ }
267
+ static PAN_CANDIDATE_REGEX = /(?<!\d)(?:\d[ -]?){12,18}\d(?!\d)/g;
268
+ static isLuhnValid(digits) {
269
+ if (digits.length < 13 || digits.length > 19)
270
+ return false;
271
+ let sum = 0;
272
+ let alt = false;
273
+ for (let i = digits.length - 1; i >= 0; i--) {
274
+ const d = digits.charCodeAt(i) - 48;
275
+ if (d < 0 || d > 9)
276
+ return false;
277
+ let x = d;
278
+ if (alt) {
279
+ x *= 2;
280
+ if (x > 9)
281
+ x -= 9;
282
+ }
283
+ sum += x;
284
+ alt = !alt;
285
+ }
286
+ return sum % 10 === 0;
287
+ }
288
+ static maskPAN(text) {
289
+ if (!text || text.length < 13)
290
+ return text;
291
+ return text.replace(LogService.PAN_CANDIDATE_REGEX, (match) => {
292
+ const digits = match.replace(/\D/g, "");
293
+ return LogService.isLuhnValid(digits) ? "xxxx...xxxx" : match;
294
+ });
295
+ }
249
296
  }
250
297
  exports.LogService = LogService;
@@ -26,6 +26,47 @@ export type THttpOutgoingLogItem = {
26
26
  response: any;
27
27
  error?: string;
28
28
  };
29
+ export type THttpHostStats = {
30
+ host: string;
31
+ count: number;
32
+ errors: number;
33
+ timeouts: number;
34
+ retries: number;
35
+ avgMs: number;
36
+ minMs: number;
37
+ maxMs: number;
38
+ totalTimeMs: number;
39
+ inFlight: number;
40
+ byStatus: {
41
+ s2xx: number;
42
+ s3xx: number;
43
+ s4xx: number;
44
+ s5xx: number;
45
+ other: number;
46
+ networkErrors: number;
47
+ };
48
+ lastRequestAt: number;
49
+ };
50
+ export type THttpStatusDistribution = {
51
+ s2xx: number;
52
+ s3xx: number;
53
+ s4xx: number;
54
+ s5xx: number;
55
+ other: number;
56
+ networkErrors: number;
57
+ timeouts: number;
58
+ };
59
+ export type THttpStatsSnapshot = {
60
+ totalRequests: number;
61
+ inFlight: {
62
+ current: number;
63
+ peak: number;
64
+ byHost: Record<string, number>;
65
+ };
66
+ statusDistribution: THttpStatusDistribution;
67
+ perHost: THttpHostStats[];
68
+ topSlow: THttpOutgoingLogItem[];
69
+ };
29
70
  export type TRetryPolicy = {
30
71
  enabled?: boolean;
31
72
  maxAttempts?: number;
@@ -90,6 +131,21 @@ export declare class Http {
90
131
  private static _truncate;
91
132
  private static _pushOutgoingLog;
92
133
  static getOutgoingLogSnapshot(): THttpOutgoingLogItem[];
134
+ private static _totalRequests;
135
+ private static _inFlight;
136
+ private static _peakInFlight;
137
+ private static _inFlightByHost;
138
+ private static _statusDist;
139
+ private static _perHost;
140
+ private static _topSlow;
141
+ private static _topSlowLimit;
142
+ private static _hostFromUrl;
143
+ private static _isTimeoutError;
144
+ private static _incInFlight;
145
+ private static _decInFlight;
146
+ private static _updateStats;
147
+ private static _pushToTopSlow;
148
+ static getHttpStats(): THttpStatsSnapshot;
93
149
  private static ensureInit;
94
150
  static configure(partial: Partial<THttpConfig>): void;
95
151
  static createAgent(opts?: ConstructorParameters<typeof Agent>[0]): Agent;
@@ -118,10 +118,149 @@ class Http {
118
118
  this._outgoingLog.shift();
119
119
  if (this._nextOutgoingLogID > Number.MAX_SAFE_INTEGER - 1000)
120
120
  this._nextOutgoingLogID = 0;
121
+ return entry;
121
122
  }
122
123
  static getOutgoingLogSnapshot() {
123
124
  return this._outgoingLog.slice();
124
125
  }
126
+ static _totalRequests = 0;
127
+ static _inFlight = 0;
128
+ static _peakInFlight = 0;
129
+ static _inFlightByHost = new Map();
130
+ static _statusDist = {
131
+ s2xx: 0, s3xx: 0, s4xx: 0, s5xx: 0, other: 0, networkErrors: 0, timeouts: 0,
132
+ };
133
+ static _perHost = new Map();
134
+ static _topSlow = [];
135
+ static _topSlowLimit = 20;
136
+ static _hostFromUrl(url) {
137
+ try {
138
+ return new URL(url).host || "unknown";
139
+ }
140
+ catch {
141
+ return "unknown";
142
+ }
143
+ }
144
+ static _isTimeoutError(err) {
145
+ if (!err || typeof err !== "object")
146
+ return false;
147
+ const e = err;
148
+ if (e.name === "AbortError")
149
+ return true;
150
+ if (typeof e.code === "string" && e.code.includes("TIMEOUT"))
151
+ return true;
152
+ return false;
153
+ }
154
+ static _incInFlight(host) {
155
+ this._inFlight++;
156
+ if (this._inFlight > this._peakInFlight)
157
+ this._peakInFlight = this._inFlight;
158
+ this._inFlightByHost.set(host, (this._inFlightByHost.get(host) ?? 0) + 1);
159
+ }
160
+ static _decInFlight(host) {
161
+ this._inFlight = Math.max(0, this._inFlight - 1);
162
+ const prev = this._inFlightByHost.get(host) ?? 0;
163
+ const next = Math.max(0, prev - 1);
164
+ if (next === 0)
165
+ this._inFlightByHost.delete(host);
166
+ else
167
+ this._inFlightByHost.set(host, next);
168
+ }
169
+ static _updateStats(host, entry, isTimeout) {
170
+ this._totalRequests++;
171
+ if (typeof entry.status === "number") {
172
+ const s = entry.status;
173
+ if (s >= 200 && s < 300)
174
+ this._statusDist.s2xx++;
175
+ else if (s >= 300 && s < 400)
176
+ this._statusDist.s3xx++;
177
+ else if (s >= 400 && s < 500)
178
+ this._statusDist.s4xx++;
179
+ else if (s >= 500 && s < 600)
180
+ this._statusDist.s5xx++;
181
+ else
182
+ this._statusDist.other++;
183
+ }
184
+ else if (entry.error) {
185
+ this._statusDist.networkErrors++;
186
+ if (isTimeout)
187
+ this._statusDist.timeouts++;
188
+ }
189
+ let h = this._perHost.get(host);
190
+ if (!h) {
191
+ h = {
192
+ host,
193
+ count: 0, errors: 0, timeouts: 0, retries: 0,
194
+ avgMs: 0, minMs: Infinity, maxMs: 0, totalTimeMs: 0,
195
+ inFlight: 0,
196
+ byStatus: { s2xx: 0, s3xx: 0, s4xx: 0, s5xx: 0, other: 0, networkErrors: 0 },
197
+ lastRequestAt: 0,
198
+ };
199
+ this._perHost.set(host, h);
200
+ }
201
+ h.count++;
202
+ h.totalTimeMs += entry.time;
203
+ h.avgMs = Math.round((h.totalTimeMs / h.count) * 10) / 10;
204
+ if (entry.time < h.minMs)
205
+ h.minMs = entry.time;
206
+ if (entry.time > h.maxMs)
207
+ h.maxMs = entry.time;
208
+ if (!entry.ok)
209
+ h.errors++;
210
+ if (isTimeout)
211
+ h.timeouts++;
212
+ h.retries += Math.max(0, entry.attempts - 1);
213
+ h.lastRequestAt = Date.now();
214
+ if (typeof entry.status === "number") {
215
+ const s = entry.status;
216
+ if (s >= 200 && s < 300)
217
+ h.byStatus.s2xx++;
218
+ else if (s >= 300 && s < 400)
219
+ h.byStatus.s3xx++;
220
+ else if (s >= 400 && s < 500)
221
+ h.byStatus.s4xx++;
222
+ else if (s >= 500 && s < 600)
223
+ h.byStatus.s5xx++;
224
+ else
225
+ h.byStatus.other++;
226
+ }
227
+ else if (entry.error) {
228
+ h.byStatus.networkErrors++;
229
+ }
230
+ }
231
+ static _pushToTopSlow(entry) {
232
+ if (this._topSlow.length < this._topSlowLimit) {
233
+ this._topSlow.push(entry);
234
+ this._topSlow.sort((a, b) => b.time - a.time);
235
+ return;
236
+ }
237
+ const slowest_of_top = this._topSlow[this._topSlow.length - 1];
238
+ if (entry.time > slowest_of_top.time) {
239
+ this._topSlow.push(entry);
240
+ this._topSlow.sort((a, b) => b.time - a.time);
241
+ this._topSlow.pop();
242
+ }
243
+ }
244
+ static getHttpStats() {
245
+ const perHost = Array.from(this._perHost.values())
246
+ .map(h => ({
247
+ ...h,
248
+ minMs: Number.isFinite(h.minMs) ? h.minMs : 0,
249
+ inFlight: this._inFlightByHost.get(h.host) ?? 0,
250
+ }))
251
+ .sort((a, b) => b.count - a.count);
252
+ return {
253
+ totalRequests: this._totalRequests,
254
+ inFlight: {
255
+ current: this._inFlight,
256
+ peak: this._peakInFlight,
257
+ byHost: Object.fromEntries(this._inFlightByHost),
258
+ },
259
+ statusDistribution: { ...this._statusDist },
260
+ perHost,
261
+ topSlow: this._topSlow.slice(),
262
+ };
263
+ }
125
264
  static ensureInit() {
126
265
  if (this._inited)
127
266
  return;
@@ -299,8 +438,11 @@ class Http {
299
438
  const responseType = opts.responseType ?? "json";
300
439
  const throwOnJsonParseError = opts.throwOnJsonParseError ?? false;
301
440
  let attemptsMade = 0;
441
+ const host = Http._hostFromUrl(url);
442
+ Http._incInFlight(host);
302
443
  const finish = (result, networkErr) => {
303
- Http._pushOutgoingLog({
444
+ Http._decInFlight(host);
445
+ const entry = Http._pushOutgoingLog({
304
446
  created: overallStarted,
305
447
  time: Date.now() - overallStarted,
306
448
  method: opts.method,
@@ -312,6 +454,9 @@ class Http {
312
454
  response: Http._truncate(result.ok ? result.data : result.error),
313
455
  error: networkErr ? String(networkErr?.message ?? networkErr) : undefined,
314
456
  });
457
+ const isTimeout = Http._isTimeoutError(networkErr);
458
+ Http._updateStats(host, entry, isTimeout);
459
+ Http._pushToTopSlow(entry);
315
460
  return result;
316
461
  };
317
462
  for (let attempt = 1; attempt <= retryCfg.maxAttempts; attempt++) {
@@ -23,6 +23,8 @@ export declare class Monitor extends BaseEndpoint {
23
23
  outgoingNetlog(req: HTTPRequestVO): Promise<TransferPacketVO>;
24
24
  sysinfo(req: HTTPRequestVO): Promise<TransferPacketVO>;
25
25
  procinfo(req: HTTPRequestVO): Promise<TransferPacketVO>;
26
+ httpstats(req: HTTPRequestVO): Promise<TransferPacketVO>;
27
+ inventory(req: HTTPRequestVO): Promise<TransferPacketVO>;
26
28
  metrics(req: HTTPRequestVO): Promise<TransferPacketVO>;
27
29
  connections(req: HTTPRequestVO): Promise<TransferPacketVO>;
28
30
  db(req: HTTPRequestVO): Promise<TransferPacketVO>;
@@ -7,6 +7,7 @@ exports.Monitor = exports.S_MONITOR_REGISTRATE_ACTION = void 0;
7
7
  const badmfck_signal_1 = require("badmfck-signal");
8
8
  const APIService_1 = require("../APIService");
9
9
  const BaseEndpoint_1 = require("../BaseEndpoint");
10
+ const BaseService_1 = require("../BaseService");
10
11
  const LogService_1 = require("../LogService");
11
12
  const crypto_1 = __importDefault(require("crypto"));
12
13
  const DefaultErrors_1 = __importDefault(require("../structures/DefaultErrors"));
@@ -44,6 +45,8 @@ class Monitor extends BaseEndpoint_1.BaseEndpoint {
44
45
  { ignoreInterceptor: true, endpoint: "outgoingnetlog", handler: this.outgoingNetlog },
45
46
  { ignoreInterceptor: true, endpoint: "sysinfo", handler: this.sysinfo },
46
47
  { ignoreInterceptor: true, endpoint: "procinfo", handler: this.procinfo },
48
+ { ignoreInterceptor: true, endpoint: "inventory", handler: this.inventory },
49
+ { ignoreInterceptor: true, endpoint: "httpstats", handler: this.httpstats },
47
50
  { ignoreInterceptor: true, endpoint: "metrics", handler: this.metrics, validationModel: {
48
51
  range: "",
49
52
  $__range_values: ["week", "day", "hour"]
@@ -137,6 +140,36 @@ class Monitor extends BaseEndpoint_1.BaseEndpoint {
137
140
  await this.checkAuthentication(req);
138
141
  return { data: await ProcessMonitor_1.ProcessMonitor.getSnapshot() };
139
142
  }
143
+ async httpstats(req) {
144
+ await this.checkAuthentication(req);
145
+ return { data: Http_1.Http.getHttpStats() };
146
+ }
147
+ async inventory(req) {
148
+ await this.checkAuthentication(req);
149
+ const now = Date.now();
150
+ const services = BaseService_1.BaseService.allInstances.map(s => ({
151
+ className: s.constructor.name,
152
+ name: s.getName(),
153
+ initialized: s.initializedAt !== null,
154
+ createdAt: s.createdAt,
155
+ initializedAt: s.initializedAt,
156
+ applicationReadyAt: s.applicationReadyAt,
157
+ uptimeSec: s.initializedAt ? Math.round((now - s.initializedAt) / 1000) : null,
158
+ }));
159
+ const endpoints = BaseEndpoint_1.BaseEndpoint.allInstances.map(e => e.getInventory());
160
+ return {
161
+ data: {
162
+ services,
163
+ endpoints,
164
+ counts: {
165
+ services: services.length,
166
+ endpoints: endpoints.length,
167
+ totalHandlers: endpoints.reduce((sum, e) => sum + e.handlerCount, 0),
168
+ },
169
+ entrypoint: BaseEndpoint_1.BaseEndpoint.getEntryPoint(),
170
+ }
171
+ };
172
+ }
140
173
  async metrics(req) {
141
174
  await this.checkAuthentication(req);
142
175
  const result = await MonitorService_1.REQ_STAT.request(req.data);