badmfck-api-server 2.8.4 → 2.8.5

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.
@@ -5,6 +5,10 @@ import { IBaseEndpoint } from './BaseEndpoint';
5
5
  import { HTTPRequestVO, IError, TransferPacketVO } from './structures/Interfaces';
6
6
  import { Req } from "badmfck-signal";
7
7
  import http from 'http';
8
+ export interface IMonitorUser {
9
+ login: string;
10
+ password: string;
11
+ }
8
12
  export interface APIServiceNetworkLogItem {
9
13
  id: number;
10
14
  created: number;
@@ -30,10 +34,7 @@ export interface APIServiceOptions {
30
34
  interceptor?: IBaseEndpoint;
31
35
  postproducer?: (req: HTTPRequestVO | undefined | null, res: Response, data: TransferPacketVO<any>, requestTime: number, endpoint?: string, log?: APIServiceNetworkLogItem | null) => Promise<TransferPacketVO>;
32
36
  preproducer?: (req: HTTPRequestVO | undefined | null) => any;
33
- monitor?: {
34
- login: string;
35
- password: string;
36
- }[];
37
+ monitor?: IMonitorUser[];
37
38
  appVersion?: string;
38
39
  fileTempDir: string;
39
40
  fileLimit: number;
@@ -47,15 +48,13 @@ export declare const REQ_HTTP_SERVER: Req<void, {
47
48
  http: http.Server;
48
49
  }>;
49
50
  export declare const REQ_INTERNAL_CALL: Req<HTTPRequestVO, IError | any>;
50
- export declare const REQ_MONITOR_USERS: Req<{
51
- authorization: string;
52
- }, any>;
51
+ export declare const REQ_MONITOR_USERS: Req<void, IMonitorUser[]>;
53
52
  export declare function Initializer(services: IBaseService[]): Promise<void>;
54
53
  export declare class APIService extends BaseService {
55
54
  private static nextLogID;
56
55
  private version;
57
56
  private options;
58
- private monitor?;
57
+ private monitor;
59
58
  private monitorIndexFile?;
60
59
  private started;
61
60
  private requestsCount;
@@ -46,6 +46,7 @@ const http_1 = __importDefault(require("http"));
46
46
  const MysqlService_1 = require("./MysqlService");
47
47
  const StatService_1 = require("./StatService");
48
48
  const ExternalServiceEndpoint_1 = require("./external/ExternalServiceEndpoint");
49
+ const MonitorService_1 = require("./MonitorService");
49
50
  function getDefaultOptions() {
50
51
  return {
51
52
  port: 8091,
@@ -74,7 +75,6 @@ exports.REQ_HTTP_SERVER = new badmfck_signal_1.Req(undefined, "REQ_HTTP_SERVER")
74
75
  exports.REQ_INTERNAL_CALL = new badmfck_signal_1.Req(undefined, "REQ_INTERNAL_CALL");
75
76
  exports.REQ_MONITOR_USERS = new badmfck_signal_1.Req(undefined, "REQ_MONITOR_USERS");
76
77
  const activeServices = [];
77
- const entryPoints = [];
78
78
  async function Initializer(services) {
79
79
  services.push(new StatService_1.StatService());
80
80
  for (let i of services) {
@@ -88,9 +88,9 @@ async function Initializer(services) {
88
88
  exports.Initializer = Initializer;
89
89
  class APIService extends BaseService_1.BaseService {
90
90
  static nextLogID = 0;
91
- version = "2.8.4";
91
+ version = "2.8.5";
92
92
  options;
93
- monitor;
93
+ monitor = null;
94
94
  monitorIndexFile;
95
95
  started = new Date();
96
96
  requestsCount = 0;
@@ -105,24 +105,9 @@ class APIService extends BaseService_1.BaseService {
105
105
  if (!this.options.corsHostWhiteList.find(val => val === self))
106
106
  this.options.corsHostWhiteList.push();
107
107
  if (this.options.monitor && this.options.monitor.length > 0) {
108
- this.monitor = new Monitor_1.Monitor();
109
- this.options.endpoints.push(this.monitor);
110
- console.warn("Service Monitor initialized");
111
- console.warn("monitor links:");
112
- for (let i of this.options.monitor) {
113
- const hash = crypto_1.default.createHash("sha256").update(i.login + i.password).digest().toString("hex");
114
- console.warn("Monitor link for: " + i.login + " -> /sm-" + hash);
115
- }
116
- exports.REQ_MONITOR_USERS.listener = async (req) => {
117
- const result = [];
118
- if (this.options.monitor) {
119
- for (let i of this.options.monitor) {
120
- const hash = crypto_1.default.createHash("sha256").update(i.login + i.password).digest().toString("hex");
121
- result.push(hash);
122
- }
123
- }
124
- return result;
125
- };
108
+ exports.REQ_MONITOR_USERS.listener = async () => this.options.monitor ?? [];
109
+ this.monitor = new MonitorService_1.MonitorService();
110
+ this.options.endpoints.push(new Monitor_1.Monitor());
126
111
  }
127
112
  this.options.endpoints.push(new ExternalServiceEndpoint_1.ExternalServiceEndpoint());
128
113
  this.options.endpoints.push(new Liveness_1.Liveness(this.started), new Readiness_1.Readiness(this.started));
@@ -299,8 +284,6 @@ class APIService extends BaseService_1.BaseService {
299
284
  httpRequest.interceptorResult = interceptorResult;
300
285
  }
301
286
  }
302
- if (i === this.monitor)
303
- httpRequest.precheck = { data: this.options.monitor };
304
287
  const precheck = await i.precheck(httpRequest);
305
288
  if (precheck && precheck.error) {
306
289
  this.sendResponse(req.get("Referer") ?? "", res, precheck, tme, ep, log, httpRequest);
@@ -313,8 +296,6 @@ class APIService extends BaseService_1.BaseService {
313
296
  console.error(e);
314
297
  if (this.options.onError)
315
298
  this.options.onError(e);
316
- if (this.monitor)
317
- this.monitor.registrateFatalError(ep);
318
299
  let data = {
319
300
  error: {
320
301
  code: 10002,
@@ -388,8 +369,6 @@ class APIService extends BaseService_1.BaseService {
388
369
  this.options.onNetworkLog(log);
389
370
  return;
390
371
  }
391
- if (req && req.stream) {
392
- }
393
372
  if (this.options.postproducer) {
394
373
  try {
395
374
  data = await this.options.postproducer(req, res, data, requestTime, endpoint, log);
@@ -409,15 +388,11 @@ class APIService extends BaseService_1.BaseService {
409
388
  log.time = data.responseTime;
410
389
  log.referer = ref;
411
390
  }
412
- if (this.monitor)
413
- this.monitor.registrateResponse(data.endpoint, data.responseTime);
414
391
  if (res.destroyed || res.closed) {
415
392
  if (log)
416
393
  log.error = "Connection already closed, can't send response for: " + data.endpoint;
417
394
  if (this.options.onError)
418
395
  this.options.onError("Connection already closed, can't send response: " + data.endpoint, data);
419
- if (this.monitor)
420
- this.monitor.registrateError(data.endpoint);
421
396
  }
422
397
  else {
423
398
  try {
@@ -426,8 +401,6 @@ class APIService extends BaseService_1.BaseService {
426
401
  if (err) {
427
402
  if (log)
428
403
  log.error = "Can't send file: " + data.file;
429
- if (this.monitor && data.endpoint)
430
- this.monitor.registrateAPIError(data.endpoint);
431
404
  this.sendResponse(ref, res, {
432
405
  error: DefaultErrors_1.default.CANT_SEND_FILE,
433
406
  data: null,
@@ -460,8 +433,7 @@ class APIService extends BaseService_1.BaseService {
460
433
  res.send(data);
461
434
  if (log)
462
435
  log.response = this.checkDataLength(data);
463
- if (data.error && this.monitor) {
464
- this.monitor.registrateAPIError(data.endpoint);
436
+ if (data.error) {
465
437
  if (log)
466
438
  log.error = data.error.message;
467
439
  }
@@ -473,8 +445,6 @@ class APIService extends BaseService_1.BaseService {
473
445
  this.options.onError("Can't send response", e);
474
446
  if (log)
475
447
  log.error = "Can't send response";
476
- if (this.monitor)
477
- this.monitor.registrateError(data.endpoint);
478
448
  }
479
449
  }
480
450
  if (log && log.error && this.options.onError) {
@@ -0,0 +1,4 @@
1
+ import { BaseService } from "./BaseService";
2
+ export declare class MonitorService extends BaseService {
3
+ constructor();
4
+ }
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MonitorService = void 0;
4
+ const BaseService_1 = require("./BaseService");
5
+ class MonitorService extends BaseService_1.BaseService {
6
+ constructor() {
7
+ super("MonitorService");
8
+ }
9
+ }
10
+ exports.MonitorService = MonitorService;
@@ -10,39 +10,15 @@ interface IStatObject {
10
10
  fatalErrors: Map<string, number>;
11
11
  apiErrors: Map<string, number>;
12
12
  }
13
- interface ISystemStat {
14
- cpuUsage: any;
15
- memoryTotal: number;
16
- memoryUsage: any;
17
- }
18
13
  export declare const S_MONITOR_REGISTRATE_ACTION: Signal<IUserAction>;
19
14
  export declare class Monitor extends BaseEndpoint {
20
15
  ignoreHttpLogging: boolean;
21
- private httpRequests;
22
- private systemStat;
23
- private userActions;
24
- private registeredActions;
16
+ users: Map<string, IStatObject>;
25
17
  constructor();
26
- registrateAction(data: IUserAction): void;
27
- addSystemStat(): void;
28
- registrateError(endpoint: string): void;
29
- registrateFatalError(endpoint: string): void;
30
- registrateAPIError(endpoint: string): void;
31
- registrateResponse(endpoint: string, responseTime: number): void;
32
- increaseStat(statObject: Map<string, number>, endpoint: string): void;
33
- createStatObj(): IStatObject;
34
- createSystemStatObj(): ISystemStat;
35
- getDateIndex(d: Date): number;
36
- getHourMinuteIndex(d: Date): string;
37
- leadZero(i: number): string;
38
- precheck(req: HTTPRequestVO): Promise<TransferPacketVO<any> | null>;
39
- logs(req: HTTPRequestVO): Promise<TransferPacketVO<any>>;
40
- netlog(req: HTTPRequestVO): Promise<TransferPacketVO<any>>;
41
- metrics(req: HTTPRequestVO): Promise<TransferPacketVO<any>>;
42
- serverStat(req: HTTPRequestVO): Promise<TransferPacketVO<any>>;
43
- mapToKeyValue(map: Map<string, string | number>): {
44
- name: string;
45
- value: string | number;
46
- }[];
18
+ logs(req: HTTPRequestVO): Promise<TransferPacketVO>;
19
+ netlog(req: HTTPRequestVO): Promise<TransferPacketVO>;
20
+ metrics(req: HTTPRequestVO): Promise<TransferPacketVO>;
21
+ serverStat(req: HTTPRequestVO): Promise<TransferPacketVO>;
22
+ checkAuthentication(req: HTTPRequestVO): Promise<boolean>;
47
23
  }
48
24
  export {};
@@ -10,254 +10,69 @@ const BaseEndpoint_1 = require("../BaseEndpoint");
10
10
  const LogService_1 = require("../LogService");
11
11
  const crypto_1 = __importDefault(require("crypto"));
12
12
  const os_1 = __importDefault(require("os"));
13
+ const DefaultErrors_1 = __importDefault(require("../structures/DefaultErrors"));
13
14
  exports.S_MONITOR_REGISTRATE_ACTION = new badmfck_signal_1.Signal();
14
15
  class Monitor extends BaseEndpoint_1.BaseEndpoint {
15
16
  ignoreHttpLogging = true;
16
- httpRequests = new Map();
17
- systemStat = new Map();
18
- userActions = new Map();
19
- registeredActions = 0;
17
+ users = new Map();
20
18
  constructor() {
21
- super("sys-monitor");
22
- exports.S_MONITOR_REGISTRATE_ACTION.subscribe(data => this.registrateAction(data));
19
+ super("--sys-monitor");
23
20
  this.registerEndpoints([
24
21
  { ignoreInterceptor: true, endpoint: "log", handler: this.logs },
25
22
  { ignoreInterceptor: true, endpoint: "netlog", handler: this.netlog },
26
23
  { ignoreInterceptor: true, endpoint: "metrics", handler: this.metrics },
27
- { ignoreInterceptor: true, endpoint: "server/stat", handler: this.serverStat }
24
+ { ignoreInterceptor: true, endpoint: "server", handler: this.serverStat }
28
25
  ]);
29
- setInterval(() => {
30
- this.addSystemStat();
31
- }, 1000 * 60 * 10);
32
- this.addSystemStat();
33
- }
34
- registrateAction(data) {
35
- if (!data.action)
36
- return;
37
- if (typeof data.action !== "string")
38
- return;
39
- if (data.action.length > 20)
40
- data.action = data.action.substring(0, 20);
41
- const d = new Date();
42
- const doy = this.getDateIndex(d);
43
- let day = this.userActions.get(doy);
44
- if (!day) {
45
- day = new Map();
46
- this.userActions.set(doy, day);
47
- if (this.userActions.size > 7) {
48
- for (let i of this.userActions) {
49
- this.userActions.delete(i[0]);
50
- break;
51
- }
52
- }
53
- }
54
- const minute = this.getHourMinuteIndex(d);
55
- let m = day.get(minute);
56
- if (!m) {
57
- m = new Map();
58
- day.set(minute, m);
59
- }
60
- let cnt = m.get(data.action);
61
- if (!cnt)
62
- cnt = 0;
63
- cnt++;
64
- m.set(data.action, cnt);
65
- }
66
- addSystemStat() {
67
- const so = this.createSystemStatObj();
68
- so.memoryUsage = process.memoryUsage();
69
- so.memoryTotal = os_1.default.totalmem();
70
- }
71
- registrateError(endpoint) {
72
- const so = this.createStatObj();
73
- this.increaseStat(so.errors, endpoint);
74
- }
75
- registrateFatalError(endpoint) {
76
- const so = this.createStatObj();
77
- this.increaseStat(so.fatalErrors, endpoint);
78
- }
79
- registrateAPIError(endpoint) {
80
- const so = this.createStatObj();
81
- this.increaseStat(so.apiErrors, endpoint);
82
- }
83
- registrateResponse(endpoint, responseTime) {
84
- const so = this.createStatObj();
85
- this.increaseStat(so.requests, endpoint);
86
- }
87
- increaseStat(statObject, endpoint) {
88
- let reqep = statObject.get(endpoint);
89
- if (!reqep)
90
- reqep = 0;
91
- reqep += 1;
92
- statObject.set(endpoint, reqep);
93
- this.registeredActions++;
94
- }
95
- createStatObj() {
96
- const d = new Date();
97
- const dtm = this.getDateIndex(d);
98
- let day = this.httpRequests.get(dtm);
99
- if (!day) {
100
- day = new Map();
101
- this.httpRequests.set(dtm, day);
102
- if (this.httpRequests.size > 7) {
103
- for (let i of this.httpRequests) {
104
- this.httpRequests.delete(i[0]);
105
- break;
106
- }
107
- }
108
- }
109
- const hourMinute = this.getHourMinuteIndex(d);
110
- let hm = day.get(hourMinute);
111
- if (!hm) {
112
- hm = {
113
- errors: new Map(),
114
- requests: new Map(),
115
- fatalErrors: new Map(),
116
- apiErrors: new Map()
117
- };
118
- day.set(hourMinute, hm);
119
- }
120
- return hm;
121
- }
122
- createSystemStatObj() {
123
- const d = new Date();
124
- const dtm = this.getDateIndex(d);
125
- let day = this.systemStat.get(dtm);
126
- if (!day) {
127
- day = new Map();
128
- this.systemStat.set(dtm, day);
129
- if (this.systemStat.size > 7) {
130
- for (let i of this.systemStat) {
131
- this.systemStat.delete(i[0]);
132
- break;
133
- }
134
- }
135
- }
136
- const hourMinute = this.getHourMinuteIndex(d);
137
- let hm = day.get(hourMinute);
138
- if (!hm) {
139
- hm = {
140
- cpuUsage: 0,
141
- memoryTotal: 0,
142
- memoryUsage: {}
143
- };
144
- day.set(hourMinute, hm);
145
- }
146
- return hm;
147
- }
148
- getDateIndex(d) {
149
- const dtm = d;
150
- return parseInt(dtm.getFullYear().toString().substring(2) + this.leadZero(dtm.getMonth() + 1) + this.leadZero(dtm.getDate()));
151
- }
152
- getHourMinuteIndex(d) {
153
- const dtm = d;
154
- return this.leadZero(dtm.getHours()) + this.leadZero(dtm.getMinutes());
155
- }
156
- leadZero(i) {
157
- if (i > 9)
158
- return i + "";
159
- return "0" + i;
160
- }
161
- async precheck(req) {
162
- if (!req.headers)
163
- return { error: { code: 10001, message: "No authorization", httpStatus: 400 } };
164
- if (!req.headers['authorization'])
165
- return { error: { code: 10002, message: "No authorization found", httpStatus: 400 } };
166
- const auth = req.headers['authorization'];
167
- if (!req.precheck || !Array.isArray(req.precheck.data)) {
168
- return { error: { code: 10003, message: "No authorization records found", httpStatus: 400 } };
169
- }
170
- let authorized = false;
171
- for (let i of req.precheck.data) {
172
- let expectationStr = "";
173
- expectationStr += req.endpoint;
174
- expectationStr += JSON.stringify(i);
175
- expectationStr += JSON.stringify(req.method);
176
- expectationStr += JSON.stringify(req.data);
177
- expectationStr += JSON.stringify(req.params);
178
- const expectation = "sha256 " + crypto_1.default.createHash("sha256").update(expectationStr).digest().toString("hex");
179
- if (auth === expectation) {
180
- authorized = true;
181
- break;
182
- }
183
- }
184
- if (!authorized) {
185
- console.error("Wrong token: " + auth);
186
- return { error: { code: 10004, message: "Unauthorized access", httpStatus: 401 } };
187
- }
188
- return null;
189
26
  }
190
27
  async logs(req) {
191
- const log = await LogService_1.REQ_LOG.request(null);
192
- return { data: log };
28
+ this.checkAuthentication(req);
29
+ const services = await LogService_1.REQ_LOG_UNIQUE_SERVICES.request();
30
+ const log = await LogService_1.REQ_LOG.request({
31
+ lastID: req.data.lastID ?? 0
32
+ });
33
+ return {
34
+ data: {
35
+ services,
36
+ log
37
+ }
38
+ };
193
39
  }
194
40
  async netlog(req) {
195
- const log = await APIService_1.REQ_HTTP_LOG.request();
196
- return { data: log };
41
+ this.checkAuthentication(req);
42
+ return {};
197
43
  }
198
44
  async metrics(req) {
199
- const date = new Date();
200
- const di = this.getDateIndex(date);
201
- const stat = this.httpRequests.get(di);
202
- let result = [];
203
- if (stat) {
204
- for (let minutes of stat) {
205
- result.push({
206
- m: minutes[0],
207
- ae: this.mapToKeyValue(minutes[1].apiErrors),
208
- e: this.mapToKeyValue(minutes[1].errors),
209
- r: this.mapToKeyValue(minutes[1].requests),
210
- fe: this.mapToKeyValue(minutes[1].fatalErrors),
211
- });
212
- }
213
- }
214
- const ua = this.userActions.get(di);
215
- let uaResult = [];
216
- if (ua) {
217
- for (let i of ua) {
218
- const minutes = i[1];
219
- const mArray = [];
220
- for (let j of i[0]) {
221
- mArray.push({
222
- action: j[0],
223
- count: j[1]
224
- });
225
- }
226
- result.push({
227
- m: i[0],
228
- data: mArray
229
- });
230
- }
231
- }
232
- return { data: { stat: result, date: +date, userActions: uaResult } };
45
+ this.checkAuthentication(req);
46
+ return {};
233
47
  }
234
48
  async serverStat(req) {
235
- const date = new Date();
236
- const di = this.getDateIndex(date);
237
- const stat = this.systemStat.get(di);
238
- if (!stat)
239
- return { data: [] };
240
- let result = [];
241
- for (let minutes of stat) {
242
- result.push({
243
- m: minutes[0],
244
- mt: minutes[1].memoryTotal,
245
- mu: minutes[1].memoryUsage,
246
- cu: minutes[1].cpuUsage,
247
- });
49
+ this.checkAuthentication(req);
50
+ let stat = {
51
+ cpuUsage: os_1.default.cpus(),
52
+ memoryTotal: os_1.default.totalmem(),
53
+ memoryUsage: os_1.default.freemem()
54
+ };
55
+ return { data: stat };
56
+ }
57
+ async checkAuthentication(req) {
58
+ const users = await APIService_1.REQ_MONITOR_USERS.request();
59
+ const header = req.headers['authorization'];
60
+ if (!header)
61
+ throw { ...DefaultErrors_1.default.UNAUTHORIZED, details: "No authorization header found" };
62
+ for (let i of users) {
63
+ let hashStr = "";
64
+ hashStr += req.endpoint;
65
+ hashStr += JSON.stringify(i);
66
+ hashStr += JSON.stringify(req.method.toUpperCase());
67
+ hashStr += JSON.stringify(req.data ?? {});
68
+ hashStr += JSON.stringify(req.params ?? {});
69
+ let token = null;
70
+ token = "sha256 " + crypto_1.default.createHash('sha256').update(hashStr).digest('hex');
71
+ if (token == header) {
72
+ return true;
73
+ }
248
74
  }
249
- return { data: {
250
- registeredActions: this.registeredActions,
251
- uptime: process.uptime(),
252
- osUptime: os_1.default.uptime(),
253
- stat: result
254
- } };
255
- }
256
- mapToKeyValue(map) {
257
- const res = [];
258
- for (let i of map)
259
- res.push({ name: i[0], value: i[1] });
260
- return res;
75
+ throw { ...DefaultErrors_1.default.UNAUTHORIZED, details: "Invalid token" };
261
76
  }
262
77
  }
263
78
  exports.Monitor = Monitor;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "badmfck-api-server",
3
- "version": "2.8.4",
3
+ "version": "2.8.5",
4
4
  "description": "Simple API http server based on express",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",