badmfck-api-server 3.9.98 → 3.9.99

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.
@@ -93,7 +93,7 @@ async function Initializer(services) {
93
93
  }
94
94
  exports.Initializer = Initializer;
95
95
  class APIService extends BaseService_1.BaseService {
96
- version = "3.9.98";
96
+ version = "3.9.99";
97
97
  options;
98
98
  monitor = null;
99
99
  started = new Date();
@@ -430,6 +430,7 @@ class APIService extends BaseService_1.BaseService {
430
430
  this.addNetlog(data, req, requestTime, 0);
431
431
  return;
432
432
  }
433
+ MonitorService_1.S_STAT_REGISTRATE_REQUEST.invoke(data);
433
434
  if (this.options.postproducer) {
434
435
  try {
435
436
  data = await this.options.postproducer(req, res, data, requestTime, endpoint);
@@ -445,7 +446,6 @@ class APIService extends BaseService_1.BaseService {
445
446
  data.endpoint = endpoint ?? "no_endpoint";
446
447
  if (this.options.appVersion)
447
448
  data.version = this.options.appVersion;
448
- MonitorService_1.S_STAT_REGISTRATE_REQUEST.invoke(data);
449
449
  if (res.socket?.destroyed || res.writableEnded || res.headersSent || (res.destroyed !== undefined && res.destroyed)) {
450
450
  (0, LogService_1.logAPI)("Connection already closed, can't send response for: " + data.endpoint);
451
451
  }
@@ -1,3 +1,4 @@
1
+ /// <reference types="node" />
1
2
  import Signal, { Req } from "badmfck-signal";
2
3
  import { BaseService } from "./BaseService";
3
4
  import { TransferPacketVO } from "./structures/Interfaces";
@@ -5,55 +6,48 @@ export interface ICustomEvent {
5
6
  event: string;
6
7
  data?: any;
7
8
  }
8
- export interface IEPStatReqFilter {
9
- range?: "minute" | "hour" | "day";
10
- from?: string;
11
- to?: string;
12
- ep?: string;
13
- project?: string;
14
- event?: string;
9
+ export interface IStat {
10
+ timestamp: number;
11
+ slot: IMonitorSlot;
15
12
  }
16
13
  export declare const S_STAT_REGISTRATE_REQUEST: Signal<TransferPacketVO<any>>;
17
14
  export declare const S_STAT_REGISTRATE_CUSTOM_EVENT: Signal<ICustomEvent>;
18
15
  export declare const S_STAT_REGISTRATE_SERVICE: Signal<string>;
19
- export declare const REQ_EP_STAT: Req<void, Record<string, IMonitorDaySlot[]>>;
20
- export declare const REQ_MONITOR_CPU_STAT: Req<void, any>;
21
- type IMonitorMinutesSlot = {
22
- count: number;
23
- yyyymmddhhmm: number;
24
- };
25
- type IMonitorHoursSlot = {
26
- count: number;
27
- yyyymmddhh: number;
28
- minutes: IMonitorMinutesSlot[];
29
- };
30
- type IMonitorDaySlot = {
31
- ep: string;
32
- yyyymmdd: number;
33
- count: number;
34
- hours: IMonitorHoursSlot[];
16
+ export declare const S_STAT_REGISTRATE_HTTP_REQUEST: Signal<{
17
+ url: string;
18
+ }>;
19
+ export declare const REQ_STAT: Req<{
20
+ range: "week" | "day" | "hour";
21
+ }, IStat[]>;
22
+ type IMonitorSlot = {
23
+ yyyymmddhhii: number;
24
+ cpu: number;
25
+ memory: {
26
+ rss: number;
27
+ heapUsed: number;
28
+ heapTotal: number;
29
+ external: number;
30
+ };
31
+ externalCalls?: number;
32
+ successEp: Record<string, number>;
33
+ failEp: Record<string, number>;
34
+ http: Record<string, number>;
35
35
  };
36
36
  export declare class MonitorService extends BaseService {
37
- private prevCpu;
38
- private prevHrTime;
39
- readonly intervalMs = 10000;
40
- readonly maxSlots: number;
41
- private history;
42
- stat: Map<string, IMonitorDaySlot[]>;
37
+ slots: Map<number, Map<number, IMonitorSlot>>;
38
+ intervalMs: number;
39
+ lastCpuUsage: NodeJS.CpuUsage | null;
40
+ lastTimestamp: number | null;
43
41
  constructor();
44
42
  init(): Promise<void>;
45
- getEPStat(): Promise<Record<string, IMonitorDaySlot[]>>;
43
+ getEPStat(req: {
44
+ range: "week" | "day" | "hour";
45
+ }): Promise<IStat[]>;
46
+ private aggregate;
47
+ private slotToTimestamp;
46
48
  onStatRegistrate(data: TransferPacketVO): void;
47
- private collectUsage;
48
- getUsage(): {
49
- timestamp: number;
50
- cpu: number;
51
- memory: {
52
- rss: number;
53
- heapUsed: number;
54
- heapTotal: number;
55
- external: number;
56
- };
57
- }[];
49
+ collectUsage(): Promise<void>;
50
+ private onHttpRequestRegistrate;
51
+ private getSlot;
58
52
  }
59
53
  export {};
@@ -22,103 +22,161 @@ var __importStar = (this && this.__importStar) || function (mod) {
22
22
  __setModuleDefault(result, mod);
23
23
  return result;
24
24
  };
25
+ var __importDefault = (this && this.__importDefault) || function (mod) {
26
+ return (mod && mod.__esModule) ? mod : { "default": mod };
27
+ };
25
28
  Object.defineProperty(exports, "__esModule", { value: true });
26
- exports.MonitorService = exports.REQ_MONITOR_CPU_STAT = exports.REQ_EP_STAT = exports.S_STAT_REGISTRATE_SERVICE = exports.S_STAT_REGISTRATE_CUSTOM_EVENT = exports.S_STAT_REGISTRATE_REQUEST = void 0;
29
+ exports.MonitorService = exports.REQ_STAT = exports.S_STAT_REGISTRATE_HTTP_REQUEST = exports.S_STAT_REGISTRATE_SERVICE = exports.S_STAT_REGISTRATE_CUSTOM_EVENT = exports.S_STAT_REGISTRATE_REQUEST = void 0;
27
30
  const badmfck_signal_1 = __importStar(require("badmfck-signal"));
28
31
  const BaseService_1 = require("./BaseService");
29
32
  const YYYYMMDDHH_1 = require("./helper/YYYYMMDDHH");
33
+ const os_1 = __importDefault(require("os"));
30
34
  exports.S_STAT_REGISTRATE_REQUEST = new badmfck_signal_1.default();
31
35
  exports.S_STAT_REGISTRATE_CUSTOM_EVENT = new badmfck_signal_1.default();
32
36
  exports.S_STAT_REGISTRATE_SERVICE = new badmfck_signal_1.default();
33
- exports.REQ_EP_STAT = new badmfck_signal_1.Req(undefined, "REQ_EP_STAT");
34
- exports.REQ_MONITOR_CPU_STAT = new badmfck_signal_1.Req(undefined, "REQ_MONITOR_CPU_STAT");
37
+ exports.S_STAT_REGISTRATE_HTTP_REQUEST = new badmfck_signal_1.default();
38
+ exports.REQ_STAT = new badmfck_signal_1.Req(undefined, "REQ_STAT");
35
39
  class MonitorService extends BaseService_1.BaseService {
36
- prevCpu = process.cpuUsage();
37
- prevHrTime = process.hrtime();
38
- intervalMs = 10_000;
39
- maxSlots = (5 * 60 * 1000) / this.intervalMs;
40
- history = [];
41
- stat = new Map();
40
+ slots = new Map();
41
+ intervalMs = 59_000;
42
+ lastCpuUsage = null;
43
+ lastTimestamp = null;
42
44
  constructor() {
43
45
  super("MonitorService");
44
- exports.S_STAT_REGISTRATE_REQUEST.subscribe((req) => this.onStatRegistrate(req));
45
- exports.REQ_EP_STAT.listener = async (req) => this.getEPStat();
46
- exports.REQ_MONITOR_CPU_STAT.listener = async () => this.getUsage();
47
46
  setInterval(() => this.collectUsage(), this.intervalMs);
48
47
  }
49
48
  async init() {
50
49
  super.init();
50
+ exports.REQ_STAT.listener = async (req) => this.getEPStat(req);
51
+ exports.S_STAT_REGISTRATE_HTTP_REQUEST.subscribe((data) => this.onHttpRequestRegistrate(data));
52
+ exports.S_STAT_REGISTRATE_REQUEST.subscribe((data) => this.onStatRegistrate(data));
53
+ }
54
+ async getEPStat(req) {
55
+ const allSlots = [];
56
+ const sortedDayKeys = Array.from(this.slots.keys()).sort((a, b) => a - b);
57
+ for (const dayKey of sortedDayKeys) {
58
+ const dayMap = this.slots.get(dayKey);
59
+ const sortedMinKeys = Array.from(dayMap.keys()).sort((a, b) => a - b);
60
+ for (const minKey of sortedMinKeys) {
61
+ allSlots.push(dayMap.get(minKey));
62
+ }
63
+ }
64
+ const steps = { hour: 1, day: 10, week: 30 };
65
+ const step = steps[req.range] || 1;
66
+ const limits = { hour: 60, day: 1440, week: 7200 };
67
+ const limitedSlots = allSlots.slice(-(limits[req.range] || 60));
68
+ return this.aggregate(limitedSlots, step);
51
69
  }
52
- async getEPStat() {
53
- const result = {};
54
- for (const [ep, slots] of this.stat.entries())
55
- result[ep] = slots;
70
+ aggregate(allSlots, step) {
71
+ const result = [];
72
+ for (let i = 0; i < allSlots.length; i += step) {
73
+ const chunk = allSlots.slice(i, i + step);
74
+ const first = chunk[0];
75
+ const agg = {
76
+ yyyymmddhhii: first.yyyymmddhhii,
77
+ cpu: 0,
78
+ memory: { rss: 0, heapUsed: 0, heapTotal: 0, external: 0 },
79
+ successEp: {},
80
+ failEp: {},
81
+ http: {}
82
+ };
83
+ chunk.forEach(s => {
84
+ agg.cpu += s.cpu;
85
+ agg.memory.rss += s.memory.rss;
86
+ agg.memory.heapUsed += s.memory.heapUsed;
87
+ for (const [path, count] of Object.entries(s.successEp)) {
88
+ agg.successEp[path] = (agg.successEp[path] || 0) + count;
89
+ }
90
+ for (const [path, count] of Object.entries(s.failEp)) {
91
+ agg.failEp[path] = (agg.failEp[path] || 0) + count;
92
+ }
93
+ for (const [url, count] of Object.entries(s.http)) {
94
+ agg.http[url] = (agg.http[url] || 0) + count;
95
+ }
96
+ });
97
+ const len = chunk.length;
98
+ agg.cpu = Number((agg.cpu / len).toFixed(2));
99
+ agg.memory.rss = Math.round(agg.memory.rss / len);
100
+ agg.memory.heapUsed = Math.round(agg.memory.heapUsed / len);
101
+ result.push({
102
+ timestamp: this.slotToTimestamp(agg.yyyymmddhhii),
103
+ slot: agg
104
+ });
105
+ }
56
106
  return result;
57
107
  }
108
+ slotToTimestamp(val) {
109
+ const s = val.toString();
110
+ return new Date(+s.substring(0, 4), +s.substring(4, 6) - 1, +s.substring(6, 8), +s.substring(8, 10), +s.substring(10, 12)).getTime();
111
+ }
58
112
  onStatRegistrate(data) {
59
113
  const date = new Date();
60
114
  const yyyymmdd = parseInt((0, YYYYMMDDHH_1.YYYYMMDDHH)(date, "yyyymmdd"));
61
- const yyyymmddhh = parseInt((0, YYYYMMDDHH_1.YYYYMMDDHH)(date, "yyyymmddhh"));
62
115
  const yyyymmddhhmm = parseInt((0, YYYYMMDDHH_1.YYYYMMDDHH)(date, "yyyymmddhhmm"));
63
116
  const ep = data.endpoint || "unknown";
64
- let epSlot = this.stat.get(ep);
65
- if (!epSlot) {
66
- epSlot = [];
67
- this.stat.set(ep, epSlot);
68
- }
69
- let slot = epSlot.find((s) => s.yyyymmdd === yyyymmdd && s.ep === ep);
70
- if (!slot) {
71
- slot = { ep, yyyymmdd: yyyymmdd, count: 0, hours: [] };
72
- epSlot.push(slot);
117
+ const slot = this.getSlot(yyyymmddhhmm);
118
+ if (!data.error)
119
+ slot.successEp[ep] = (slot.successEp[ep] || 0) + 1;
120
+ else
121
+ slot.failEp[ep] = (slot.failEp[ep] || 0) + 1;
122
+ }
123
+ async collectUsage() {
124
+ const usage = process.cpuUsage();
125
+ const now = Date.now();
126
+ const yyyymmddhhmm = parseInt((0, YYYYMMDDHH_1.YYYYMMDDHH)(new Date(now), "yyyymmddhhmm"));
127
+ const slot = this.getSlot(yyyymmddhhmm);
128
+ if (this.lastCpuUsage && this.lastTimestamp) {
129
+ const diff = usage.user - this.lastCpuUsage.user;
130
+ const sysdiff = usage.system - this.lastCpuUsage.system;
131
+ const timeDiffMs = now - this.lastTimestamp;
132
+ slot.cpu = Number(((diff + sysdiff) / 1000 / timeDiffMs / os_1.default.cpus().length * 100).toFixed(2));
73
133
  }
74
- slot.count++;
75
- let slotHour = slot.hours.find((h) => h.yyyymmddhh === yyyymmddhh);
76
- if (!slotHour) {
77
- slotHour = {
78
- count: 0,
79
- yyyymmddhh,
80
- minutes: [],
81
- };
82
- slot.hours.push(slotHour);
134
+ this.lastCpuUsage = usage;
135
+ this.lastTimestamp = now;
136
+ const mem = process.memoryUsage();
137
+ slot.memory = {
138
+ rss: Math.round(mem.rss / 1024 / 1024),
139
+ heapUsed: Math.round(mem.heapUsed / 1024 / 1024),
140
+ heapTotal: Math.round(mem.heapTotal / 1024 / 1024),
141
+ external: Math.round(mem.external / 1024 / 1024),
142
+ };
143
+ }
144
+ onHttpRequestRegistrate(data) {
145
+ const date = new Date();
146
+ const yyyymmddhhmm = parseInt((0, YYYYMMDDHH_1.YYYYMMDDHH)(date, "yyyymmddhhmm"));
147
+ const slot = this.getSlot(yyyymmddhhmm);
148
+ slot.http[data.url] = (slot.http[data.url] || 0) + 1;
149
+ }
150
+ getSlot(yyyymmddhhii) {
151
+ const yyymmdd = Math.floor(yyyymmddhhii / 100);
152
+ let daySlots = this.slots.get(yyymmdd);
153
+ if (!daySlots) {
154
+ daySlots = new Map();
155
+ this.slots.set(yyymmdd, daySlots);
156
+ if (this.slots.size > 7) {
157
+ let key = this.slots.keys().next().value;
158
+ if (key)
159
+ this.slots.delete(key);
160
+ }
83
161
  }
84
- slotHour.count++;
85
- let slotMinute = slotHour.minutes.find((m) => m.yyyymmddhhmm === yyyymmddhhmm);
86
- if (!slotMinute) {
87
- slotMinute = {
88
- count: 0,
89
- yyyymmddhhmm,
162
+ let slot = daySlots.get(yyyymmddhhii);
163
+ if (!slot) {
164
+ slot = {
165
+ yyyymmddhhii,
166
+ cpu: 0,
167
+ memory: {
168
+ rss: 0,
169
+ heapUsed: 0,
170
+ heapTotal: 0,
171
+ external: 0,
172
+ },
173
+ successEp: {},
174
+ failEp: {},
175
+ http: {}
90
176
  };
91
- slotHour.minutes.push(slotMinute);
177
+ daySlots.set(yyyymmddhhii, slot);
92
178
  }
93
- slotMinute.count++;
94
- if (epSlot.length > 7)
95
- epSlot.unshift();
96
- }
97
- collectUsage() {
98
- const currentCpu = process.cpuUsage(this.prevCpu);
99
- const currentHrTime = process.hrtime(this.prevHrTime);
100
- this.prevCpu = process.cpuUsage();
101
- this.prevHrTime = process.hrtime();
102
- const elapsedMicros = currentHrTime[0] * 1e6 + currentHrTime[1] / 1e3;
103
- const totalCpu = currentCpu.user + currentCpu.system;
104
- const cpuPercent = (totalCpu / elapsedMicros) * 100;
105
- const mem = process.memoryUsage();
106
- this.history.push({
107
- timestamp: Date.now(),
108
- cpu: cpuPercent,
109
- memory: {
110
- rss: mem.rss,
111
- heapUsed: mem.heapUsed,
112
- heapTotal: mem.heapTotal,
113
- external: mem.external,
114
- },
115
- });
116
- if (this.history.length > this.maxSlots) {
117
- this.history.shift();
118
- }
119
- }
120
- getUsage() {
121
- return this.history;
179
+ return slot;
122
180
  }
123
181
  }
124
182
  exports.MonitorService = MonitorService;
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.Http = void 0;
7
7
  const undici_1 = require("undici");
8
8
  const node_diagnostics_channel_1 = __importDefault(require("node:diagnostics_channel"));
9
+ const MonitorService_1 = require("../MonitorService");
9
10
  function sleep(ms) {
10
11
  return new Promise((r) => setTimeout(r, ms));
11
12
  }
@@ -227,6 +228,7 @@ class Http {
227
228
  };
228
229
  }
229
230
  static async request(opts) {
231
+ MonitorService_1.S_STAT_REGISTRATE_HTTP_REQUEST.invoke({ url: opts.url });
230
232
  this.ensureInit();
231
233
  const cfg = this.config;
232
234
  const retryCfg = {
@@ -5,19 +5,12 @@ import { HTTPRequestVO, TransferPacketVO } from "../structures/Interfaces";
5
5
  export interface IUserAction {
6
6
  action: string;
7
7
  }
8
- interface IStatObject {
9
- requests: Map<string, number>;
10
- errors: Map<string, number>;
11
- fatalErrors: Map<string, number>;
12
- apiErrors: Map<string, number>;
13
- }
14
8
  export declare const S_MONITOR_REGISTRATE_ACTION: Signal<IUserAction>;
15
9
  export declare class Monitor extends BaseEndpoint {
16
10
  ignoreHttpLogging: boolean;
17
11
  ignoreInDocumentation: boolean;
18
12
  indexHtmlFile: string | null;
19
13
  apiServiceOptions: APIServiceOptions;
20
- users: Map<string, IStatObject>;
21
14
  fileParsed: boolean;
22
15
  lastTimeCacheChecked: number;
23
16
  constructor(options: APIServiceOptions);
@@ -28,7 +21,5 @@ export declare class Monitor extends BaseEndpoint {
28
21
  logs(req: HTTPRequestVO): Promise<TransferPacketVO>;
29
22
  netlog(req: HTTPRequestVO): Promise<TransferPacketVO>;
30
23
  metrics(req: HTTPRequestVO): Promise<TransferPacketVO>;
31
- serverStat(req: HTTPRequestVO): Promise<TransferPacketVO>;
32
24
  checkAuthentication(req: HTTPRequestVO): Promise<void>;
33
25
  }
34
- export {};
@@ -10,10 +10,10 @@ const BaseEndpoint_1 = require("../BaseEndpoint");
10
10
  const LogService_1 = require("../LogService");
11
11
  const crypto_1 = __importDefault(require("crypto"));
12
12
  const DefaultErrors_1 = __importDefault(require("../structures/DefaultErrors"));
13
- const MonitorService_1 = require("../MonitorService");
14
13
  const fs_1 = __importDefault(require("fs"));
15
14
  const path_1 = __importDefault(require("path"));
16
15
  const Http_1 = require("../http/Http");
16
+ const MonitorService_1 = require("../MonitorService");
17
17
  exports.S_MONITOR_REGISTRATE_ACTION = new badmfck_signal_1.Signal();
18
18
  const logMap = {
19
19
  ALL: 10,
@@ -29,7 +29,6 @@ class Monitor extends BaseEndpoint_1.BaseEndpoint {
29
29
  ignoreInDocumentation = true;
30
30
  indexHtmlFile = null;
31
31
  apiServiceOptions;
32
- users = new Map();
33
32
  fileParsed = false;
34
33
  lastTimeCacheChecked = 0;
35
34
  constructor(options) {
@@ -39,8 +38,10 @@ class Monitor extends BaseEndpoint_1.BaseEndpoint {
39
38
  { ignoreInterceptor: true, endpoint: "html", handler: this.html },
40
39
  { ignoreInterceptor: true, endpoint: "log", handler: this.logs },
41
40
  { ignoreInterceptor: true, endpoint: "netlog", handler: this.netlog },
42
- { ignoreInterceptor: true, endpoint: "metrics", handler: this.metrics },
43
- { ignoreInterceptor: true, endpoint: "server", handler: this.serverStat },
41
+ { ignoreInterceptor: true, endpoint: "metrics", handler: this.metrics, validationModel: {
42
+ range: "",
43
+ $__range_values: ["week", "day", "hour"]
44
+ } },
44
45
  { ignoreInterceptor: true, endpoint: "controllers", handler: this.controllers },
45
46
  { ignoreInterceptor: true, endpoint: "changeLogLevel", handler: this.changeLogLevel, validationModel: {
46
47
  level: Object.keys(logMap).join(",")
@@ -125,13 +126,9 @@ class Monitor extends BaseEndpoint_1.BaseEndpoint {
125
126
  }
126
127
  async metrics(req) {
127
128
  await this.checkAuthentication(req);
128
- const result = await MonitorService_1.REQ_EP_STAT.request(req.data);
129
+ const result = await MonitorService_1.REQ_STAT.request(req.data);
129
130
  return { data: result };
130
131
  }
131
- async serverStat(req) {
132
- await this.checkAuthentication(req);
133
- return { data: MonitorService_1.REQ_MONITOR_CPU_STAT.request() };
134
- }
135
132
  async checkAuthentication(req) {
136
133
  const users = await APIService_1.REQ_MONITOR_USERS.request();
137
134
  if (!users || users.length == 0)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "badmfck-api-server",
3
- "version": "3.9.98",
3
+ "version": "3.9.99",
4
4
  "description": "Simple API http server based on express",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",