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.
- package/dist/apiServer/APIService.d.ts +5 -0
- package/dist/apiServer/APIService.js +26 -1
- package/dist/apiServer/BaseEndpoint.d.ts +2 -0
- package/dist/apiServer/BaseEndpoint.js +1 -0
- package/dist/apiServer/monitor/Monitor.d.ts +32 -0
- package/dist/apiServer/monitor/Monitor.js +146 -0
- package/dist/apiServer/structures/Interfaces.d.ts +1 -0
- package/package.json +1 -1
|
@@ -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.
|
|
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;
|