badmfck-api-server 1.5.2 → 1.5.3

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.
@@ -25,6 +25,10 @@ export interface APIServiceOptions {
25
25
  onError?: ((...rest: any[]) => void) | null;
26
26
  isProductionEnvironment: boolean;
27
27
  interceptor?: IBaseEndpoint;
28
+ monitor?: {
29
+ login: string;
30
+ password: string;
31
+ }[];
28
32
  }
29
33
  export declare function getDefaultOptions(): APIServiceOptions;
30
34
  export declare const REQ_CREATE_NET_LOG: Req<void, APIServiceNetworkLogItem>;
@@ -34,6 +38,7 @@ export declare class APIService extends BaseService {
34
38
  private static nextLogID;
35
39
  private version;
36
40
  private options;
41
+ private monitor?;
37
42
  netLog: APIServiceNetworkLogItem[];
38
43
  constructor(options?: APIServiceOptions | null);
39
44
  init(): Promise<void>;
@@ -11,6 +11,7 @@ const BaseEndpoint_1 = require("./BaseEndpoint");
11
11
  const DefaultErrors_1 = __importDefault(require("./structures/DefaultErrors"));
12
12
  const badmfck_signal_1 = require("badmfck-signal");
13
13
  const LogService_1 = require("./LogService");
14
+ const Monitor_1 = require("./monitor/Monitor");
14
15
  function getDefaultOptions() {
15
16
  return {
16
17
  port: 8091,
@@ -43,8 +44,9 @@ async function Initializer(services) {
43
44
  exports.Initializer = Initializer;
44
45
  class APIService extends BaseService_1.BaseService {
45
46
  static nextLogID = 0;
46
- version = "1.5.2";
47
+ version = "1.5.3";
47
48
  options;
49
+ monitor;
48
50
  netLog = [];
49
51
  constructor(options) {
50
52
  super('HTTP Service');
@@ -54,6 +56,11 @@ class APIService extends BaseService_1.BaseService {
54
56
  const self = "http://localhost:" + this.options.port;
55
57
  if (!this.options.corsHostWhiteList.find(val => val === self))
56
58
  this.options.corsHostWhiteList.push();
59
+ if (this.options.monitor && this.options.monitor.length > 0) {
60
+ this.monitor = new Monitor_1.Monitor();
61
+ this.options.endpoints.push(this.monitor);
62
+ console.log("Service Monitor initialized");
63
+ }
57
64
  }
58
65
  async init() {
59
66
  exports.REQ_HTTP_LOG.listener = async (ignore) => this.netLog;
@@ -148,12 +155,22 @@ class APIService extends BaseService_1.BaseService {
148
155
  httpRequest.interceptorResult = interceptorResult;
149
156
  }
150
157
  }
158
+ if (i === this.monitor)
159
+ httpRequest.precheck = { data: this.options.monitor };
160
+ const precheck = await i.precheck(httpRequest);
161
+ if (precheck && precheck.error) {
162
+ this.sendResponse(res, precheck, tme, ep, log);
163
+ return;
164
+ }
165
+ httpRequest.precheck = precheck;
151
166
  result = await i.execute(httpRequest);
152
167
  }
153
168
  catch (e) {
154
169
  console.error(e);
155
170
  if (this.options.onError)
156
171
  this.options.onError(e);
172
+ if (this.monitor)
173
+ this.monitor.registrateFatalError(ep);
157
174
  this.sendResponse(res, {
158
175
  httpStatus: 500,
159
176
  error: {
@@ -186,6 +203,8 @@ class APIService extends BaseService_1.BaseService {
186
203
  data.responseTime = (+new Date()) - requestTime;
187
204
  data.version = this.version;
188
205
  data.endpoint = endpoint ?? "no_endpoint";
206
+ if (this.monitor)
207
+ this.monitor.registrateResponse(data.endpoint, data.responseTime);
189
208
  if (log)
190
209
  log.time = data.responseTime;
191
210
  if (res.destroyed || res.closed) {
@@ -193,6 +212,8 @@ class APIService extends BaseService_1.BaseService {
193
212
  log.error = "Connection already closed, can't send response";
194
213
  if (this.options.onError)
195
214
  this.options.onError("Connection already closed, can't send response", data);
215
+ if (this.monitor)
216
+ this.monitor.registrateError(data.endpoint);
196
217
  }
197
218
  else {
198
219
  try {
@@ -219,6 +240,8 @@ class APIService extends BaseService_1.BaseService {
219
240
  res.send(data);
220
241
  if (log)
221
242
  log.response = data;
243
+ if (data.error && this.monitor)
244
+ this.monitor.registrateAPIError(data.endpoint);
222
245
  }
223
246
  }
224
247
  }
@@ -227,6 +250,8 @@ class APIService extends BaseService_1.BaseService {
227
250
  this.options.onError("Can't send response", e);
228
251
  if (log)
229
252
  log.error = "Can't send response";
253
+ if (this.monitor)
254
+ this.monitor.registrateError(data.endpoint);
230
255
  }
231
256
  }
232
257
  if (this.options.onNetworkLog && log)
@@ -2,6 +2,7 @@ import { HTTPRequestVO, TransferPacketVO } from "./structures/Interfaces";
2
2
  export interface IBaseEndpoint {
3
3
  endpoints?: IEndpointHandler[];
4
4
  execute: (req: HTTPRequestVO) => Promise<TransferPacketVO<any>>;
5
+ precheck: (req: HTTPRequestVO) => Promise<TransferPacketVO<any> | null>;
5
6
  init: () => Promise<void>;
6
7
  ignoreHttpLogging: boolean;
7
8
  }
@@ -22,5 +23,6 @@ export declare class BaseEndpoint implements IBaseEndpoint {
22
23
  constructor(endpoint: string);
23
24
  registerEndpoints(endpoints: IEndpointHandler[]): void;
24
25
  init(): Promise<void>;
26
+ precheck(req: HTTPRequestVO): Promise<TransferPacketVO<any> | null>;
25
27
  execute(req: HTTPRequestVO): Promise<TransferPacketVO<any>>;
26
28
  }
@@ -56,6 +56,7 @@ class BaseEndpoint {
56
56
  (0, LogService_1.logInfo)("${BaseEndpoint.js}", "endpoint: " + i.endpoint + " initalized, ignoreInterceptor: " + (i.ignoreInterceptor ?? "false"));
57
57
  }
58
58
  ;
59
+ async precheck(req) { return null; }
59
60
  async execute(req) {
60
61
  if (this.endpoints && this.endpoints.length > 0) {
61
62
  for (let i of this.endpoints) {
@@ -0,0 +1,32 @@
1
+ import { BaseEndpoint } from "../BaseEndpoint";
2
+ import { HTTPRequestVO, TransferPacketVO } from "../structures/Interfaces";
3
+ interface IStatObject {
4
+ requests: Map<string, number>;
5
+ errors: Map<string, number>;
6
+ fatalErrors: Map<string, number>;
7
+ apiErrors: Map<string, number>;
8
+ }
9
+ export declare class Monitor extends BaseEndpoint {
10
+ ignoreHttpLogging: boolean;
11
+ private startedAt;
12
+ private httpRequests;
13
+ constructor();
14
+ registrateError(endpoint: string): void;
15
+ registrateFatalError(endpoint: string): void;
16
+ registrateAPIError(endpoint: string): void;
17
+ registrateResponse(endpoint: string, responseTime: number): void;
18
+ increaseStat(statObject: Map<string, number>, endpoint: string): void;
19
+ createStatObj(): IStatObject;
20
+ getDateIndex(d: Date): number;
21
+ getHourMinuteIndex(d: Date): string;
22
+ leadZero(i: number): string;
23
+ precheck(req: HTTPRequestVO): Promise<TransferPacketVO<any> | null>;
24
+ logs(req: HTTPRequestVO): Promise<TransferPacketVO<any>>;
25
+ netlog(req: HTTPRequestVO): Promise<TransferPacketVO<any>>;
26
+ metrics(req: HTTPRequestVO): Promise<TransferPacketVO<any>>;
27
+ mapToKeyValue(map: Map<string, number>): {
28
+ name: string;
29
+ value: number;
30
+ }[];
31
+ }
32
+ export {};
@@ -0,0 +1,146 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.Monitor = void 0;
7
+ const APIService_1 = require("../APIService");
8
+ const BaseEndpoint_1 = require("../BaseEndpoint");
9
+ const LogService_1 = require("../LogService");
10
+ const crypto_1 = __importDefault(require("crypto"));
11
+ class Monitor extends BaseEndpoint_1.BaseEndpoint {
12
+ ignoreHttpLogging = true;
13
+ startedAt = new Date();
14
+ httpRequests = new Map();
15
+ constructor() {
16
+ super("sys-monitor");
17
+ this.registerEndpoints([
18
+ { ignoreInterceptor: true, endpoint: "log", handler: this.logs },
19
+ { ignoreInterceptor: true, endpoint: "netlog", handler: this.netlog },
20
+ { ignoreInterceptor: true, endpoint: "metrics", handler: this.metrics }
21
+ ]);
22
+ }
23
+ registrateError(endpoint) {
24
+ const so = this.createStatObj();
25
+ this.increaseStat(so.errors, endpoint);
26
+ }
27
+ registrateFatalError(endpoint) {
28
+ const so = this.createStatObj();
29
+ this.increaseStat(so.fatalErrors, endpoint);
30
+ }
31
+ registrateAPIError(endpoint) {
32
+ const so = this.createStatObj();
33
+ this.increaseStat(so.apiErrors, endpoint);
34
+ }
35
+ registrateResponse(endpoint, responseTime) {
36
+ const so = this.createStatObj();
37
+ this.increaseStat(so.requests, endpoint);
38
+ }
39
+ increaseStat(statObject, endpoint) {
40
+ let reqep = statObject.get(endpoint);
41
+ if (!reqep)
42
+ reqep = 0;
43
+ reqep += 1;
44
+ statObject.set(endpoint, reqep);
45
+ }
46
+ createStatObj() {
47
+ const d = new Date();
48
+ const dtm = this.getDateIndex(d);
49
+ let day = this.httpRequests.get(dtm);
50
+ if (!day) {
51
+ day = new Map();
52
+ this.httpRequests.set(dtm, day);
53
+ if (this.httpRequests.size > 14) {
54
+ for (let i of this.httpRequests) {
55
+ this.httpRequests.delete(i[0]);
56
+ break;
57
+ }
58
+ }
59
+ }
60
+ const hourMinute = this.getHourMinuteIndex(d);
61
+ let hm = day.get(hourMinute);
62
+ if (!hm) {
63
+ hm = {
64
+ errors: new Map(),
65
+ requests: new Map(),
66
+ fatalErrors: new Map(),
67
+ apiErrors: new Map()
68
+ };
69
+ }
70
+ return hm;
71
+ }
72
+ getDateIndex(d) {
73
+ const dtm = d;
74
+ return parseInt(dtm.getFullYear().toString().substring(2) + this.leadZero(dtm.getMonth() + 1) + this.leadZero(dtm.getDate()));
75
+ }
76
+ getHourMinuteIndex(d) {
77
+ const dtm = d;
78
+ return this.leadZero(dtm.getHours()) + this.leadZero(dtm.getMinutes());
79
+ }
80
+ leadZero(i) {
81
+ if (i > 9)
82
+ return i + "";
83
+ return "0" + i;
84
+ }
85
+ async precheck(req) {
86
+ if (!req.headers)
87
+ return { error: { code: 10001, message: "No authorization", httpStatus: 400 } };
88
+ if (!req.headers['authorization'])
89
+ return { error: { code: 10002, message: "No authorization found", httpStatus: 400 } };
90
+ const auth = req.headers['authorization'];
91
+ if (!Array.isArray(req.precheck)) {
92
+ return { error: { code: 10003, message: "No authorization records found", httpStatus: 400 } };
93
+ }
94
+ let authorized = false;
95
+ for (let i of req.precheck) {
96
+ let expectationStr = "";
97
+ expectationStr += JSON.stringify(i);
98
+ expectationStr += JSON.stringify(req.method);
99
+ expectationStr += JSON.stringify(req.data);
100
+ expectationStr += JSON.stringify(req.params);
101
+ const expectation = "sha256 " + crypto_1.default.createHash("sha256").update(expectationStr).digest().toString("hex");
102
+ if (auth === expectation) {
103
+ authorized = true;
104
+ break;
105
+ }
106
+ }
107
+ if (!authorized) {
108
+ console.error("Wrong token: " + auth);
109
+ return { error: { code: 10004, message: "Unauthorized access", httpStatus: 401 } };
110
+ }
111
+ return null;
112
+ }
113
+ async logs(req) {
114
+ const log = await LogService_1.REQ_LOG.request(null);
115
+ return { data: log };
116
+ }
117
+ async netlog(req) {
118
+ const log = await APIService_1.REQ_HTTP_LOG.request();
119
+ return { data: log };
120
+ }
121
+ async metrics(req) {
122
+ const date = new Date();
123
+ const di = this.getDateIndex(date);
124
+ const stat = this.httpRequests.get(di);
125
+ if (!stat)
126
+ return { data: [] };
127
+ let result = [];
128
+ for (let minutes of stat) {
129
+ result.push({
130
+ m: minutes[0],
131
+ ae: this.mapToKeyValue(minutes[1].apiErrors),
132
+ e: this.mapToKeyValue(minutes[1].errors),
133
+ r: this.mapToKeyValue(minutes[1].requests),
134
+ fe: this.mapToKeyValue(minutes[1].fatalErrors),
135
+ });
136
+ }
137
+ return { data: { stat: result, date: +date } };
138
+ }
139
+ mapToKeyValue(map) {
140
+ const res = [];
141
+ for (let i of map)
142
+ res.push({ name: i[0], value: i[1] });
143
+ return res;
144
+ }
145
+ }
146
+ exports.Monitor = Monitor;
@@ -21,6 +21,7 @@ export interface HTTPRequestVO {
21
21
  headers: any;
22
22
  endpoint: string;
23
23
  interceptorResult?: TransferPacketVO<any>;
24
+ precheck?: TransferPacketVO<any> | null;
24
25
  }
25
26
  export interface IError {
26
27
  code: number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "badmfck-api-server",
3
- "version": "1.5.2",
3
+ "version": "1.5.3",
4
4
  "description": "Simple API http server based on express",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",