badmfck-api-server 3.9.91 → 3.9.94

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.
@@ -23,7 +23,7 @@ export interface APIServiceNetworkLogItem {
23
23
  error?: string | null;
24
24
  }
25
25
  export interface IInterceptor<T> {
26
- intercept(req: HTTPRequestVO<any>): Promise<T | IError>;
26
+ intercept(req: HTTPRequestVO<any>, internalCallParams: any): Promise<T | IError>;
27
27
  }
28
28
  export interface APIServiceOptions<TInterceptor = unknown> {
29
29
  port: number;
@@ -58,7 +58,7 @@ export declare const REQ_INTERNAL_CALL: Req<HTTPRequestVO, IError | any>;
58
58
  export declare const REQ_MONITOR_USERS: Req<void, IMonitorUser[]>;
59
59
  export declare const REQ_DOC_USERS: Req<void, IMonitorUser[]>;
60
60
  export declare function Initializer(services: IBaseService[]): Promise<void>;
61
- export declare class APIService extends BaseService {
61
+ export declare class APIService<TInterceptorResult = any, TInternalCallParams = any> extends BaseService {
62
62
  private version;
63
63
  private options;
64
64
  private monitor;
@@ -29,7 +29,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
29
29
  exports.APIService = exports.Initializer = exports.REQ_DOC_USERS = exports.REQ_MONITOR_USERS = exports.REQ_INTERNAL_CALL = exports.REQ_HTTP_SERVER = exports.REQ_HTTP_REQUESTS_COUNT = exports.REQ_HTTP_LOG = exports.getDefaultOptions = void 0;
30
30
  const express_1 = __importDefault(require("express"));
31
31
  const BaseService_1 = require("./BaseService");
32
- const cors_1 = __importDefault(require("cors"));
33
32
  const BaseEndpoint_1 = require("./BaseEndpoint");
34
33
  const DefaultErrors_1 = __importStar(require("./structures/DefaultErrors"));
35
34
  const badmfck_signal_1 = require("badmfck-signal");
@@ -50,7 +49,7 @@ function defaultOptions() {
50
49
  return {
51
50
  port: 8080,
52
51
  baseEndPoint: "/api/",
53
- corsHostWhiteList: ["localhost:3000"],
52
+ corsHostWhiteList: ["http://localhost:3000"],
54
53
  endpoints: [],
55
54
  jsonLimit: "10mb",
56
55
  projectName: "Application project",
@@ -77,7 +76,7 @@ exports.REQ_HTTP_REQUESTS_COUNT = new badmfck_signal_1.Req(undefined, "REQ_HTTP_
77
76
  exports.REQ_HTTP_SERVER = new badmfck_signal_1.Req(undefined, "REQ_HTTP_SERVER");
78
77
  exports.REQ_INTERNAL_CALL = new badmfck_signal_1.Req(undefined, "REQ_INTERNAL_CALL");
79
78
  exports.REQ_MONITOR_USERS = new badmfck_signal_1.Req(undefined, "REQ_MONITOR_USERS");
80
- exports.REQ_DOC_USERS = new badmfck_signal_1.Req(undefined, "REQ_MONITOR_USERS");
79
+ exports.REQ_DOC_USERS = new badmfck_signal_1.Req(undefined, "REQ_DOC_USERS");
81
80
  const activeServices = [];
82
81
  async function Initializer(services) {
83
82
  services.push(new StatService_1.StatService());
@@ -92,7 +91,7 @@ async function Initializer(services) {
92
91
  }
93
92
  exports.Initializer = Initializer;
94
93
  class APIService extends BaseService_1.BaseService {
95
- version = "3.9.9";
94
+ version = "3.9.94";
96
95
  options;
97
96
  monitor = null;
98
97
  started = new Date();
@@ -105,7 +104,18 @@ class APIService extends BaseService_1.BaseService {
105
104
  this.options.corsHostWhiteList = [];
106
105
  const self = "http://localhost:" + this.options.port;
107
106
  if (!this.options.corsHostWhiteList.find(val => val === self))
108
- this.options.corsHostWhiteList.push();
107
+ this.options.corsHostWhiteList.push(self);
108
+ const list = [];
109
+ for (let h of this.options.corsHostWhiteList) {
110
+ h = h.replace(/\/$/, "");
111
+ if (!/^https?:\/\//i.test(h)) {
112
+ list.push(`http://${h}`, `https://${h}`);
113
+ }
114
+ else {
115
+ list.push(h);
116
+ }
117
+ }
118
+ this.options.corsHostWhiteList = Array.from(new Set(list));
109
119
  exports.REQ_MONITOR_USERS.listener = async () => this.options.access.monitor ?? [];
110
120
  exports.REQ_DOC_USERS.listener = async () => this.options.access.documentation ?? [];
111
121
  this.options.endpoints.push(new Monitor_1.Monitor(this.options));
@@ -142,8 +152,8 @@ class APIService extends BaseService_1.BaseService {
142
152
  exports.REQ_HTTP_SERVER.listener = async (_) => { return { express: app, http: server }; };
143
153
  if (this.options.isProductionEnvironment)
144
154
  app.set("env", 'production');
145
- app.use(express_1.default.json({ limit: '10mb' }));
146
- app.use(express_1.default.urlencoded({ limit: '10mb', extended: true }));
155
+ app.use(express_1.default.json({ limit: this.options.jsonLimit ?? '10mb' }));
156
+ app.use(express_1.default.urlencoded({ limit: this.options.jsonLimit ?? '10mb', extended: true }));
147
157
  app.use((0, express_fileupload_1.default)({
148
158
  limitHandler: (req, res, next) => {
149
159
  this.sendResponse(req.get("Referer") ?? "", res, {
@@ -161,6 +171,7 @@ class APIService extends BaseService_1.BaseService {
161
171
  response: undefined,
162
172
  files: undefined
163
173
  });
174
+ return;
164
175
  },
165
176
  limits: { fileSize: this.options.fileLimit },
166
177
  useTempFiles: true,
@@ -168,7 +179,7 @@ class APIService extends BaseService_1.BaseService {
168
179
  tempFileDir: this.options.fileTempDir,
169
180
  abortOnLimit: true
170
181
  }));
171
- app.use(async (err, req, resp, next) => {
182
+ app.use((err, req, resp, next) => {
172
183
  if (!err) {
173
184
  next();
174
185
  return;
@@ -193,15 +204,35 @@ class APIService extends BaseService_1.BaseService {
193
204
  response: undefined,
194
205
  files: undefined
195
206
  });
207
+ return;
208
+ });
209
+ const corsSet = new Set(this.options.corsHostWhiteList.map(x => String(x).replace(/\/$/, "")));
210
+ app.use((req, res, next) => {
211
+ const originHeader = req.headers.origin;
212
+ if (!originHeader)
213
+ return next();
214
+ let originNorm;
215
+ try {
216
+ originNorm = new URL(String(originHeader)).origin;
217
+ }
218
+ catch {
219
+ res.status(403).send({
220
+ error: { ...DefaultErrors_1.default.FORBIDDEN, details: "Invalid Origin header" },
221
+ data: null,
222
+ httpStatus: 403,
223
+ });
224
+ return;
225
+ }
226
+ if (!corsSet.has(originNorm)) {
227
+ res.status(403).send({
228
+ error: { ...DefaultErrors_1.default.FORBIDDEN, details: `Origin not allowed: ${originNorm}` },
229
+ data: null,
230
+ httpStatus: 403,
231
+ });
232
+ return;
233
+ }
234
+ return next();
196
235
  });
197
- const corsOptions = {
198
- origin: (origin, callback) => {
199
- const originIsWhitelisted = this.options.corsHostWhiteList.includes(origin);
200
- callback(null, originIsWhitelisted);
201
- },
202
- credentials: true
203
- };
204
- app.use((0, cors_1.default)(corsOptions));
205
236
  BaseEndpoint_1.BaseEndpoint.setEntryPoint(this.options.baseEndPoint);
206
237
  for (let i of this.options.endpoints) {
207
238
  await i.init();
@@ -214,17 +245,17 @@ class APIService extends BaseService_1.BaseService {
214
245
  this.requestsCount++;
215
246
  const tme = +new Date();
216
247
  const execute = async () => {
217
- const body = req.body;
248
+ const body = { ...(req.body ?? {}) };
218
249
  if (req.query && typeof req.query === "object") {
219
- for (let i in req.query) {
220
- body[i] = req.query[i];
221
- }
250
+ const keys = Object.keys(req.query);
251
+ for (let k of keys)
252
+ body[k] = req.query[k];
222
253
  }
223
254
  let httpRequest = {
224
255
  raw: req,
225
256
  response: res,
226
257
  method: req.method,
227
- data: req.body,
258
+ data: body,
228
259
  params: req.params,
229
260
  headers: req.headers,
230
261
  endpoint: ep,
@@ -273,7 +304,7 @@ class APIService extends BaseService_1.BaseService {
273
304
  if (!ignoreInterceptor) {
274
305
  let interceptorResult;
275
306
  if (this.options.interceptor) {
276
- interceptorResult = await this.options.interceptor.intercept(httpRequest);
307
+ interceptorResult = await this.options.interceptor.intercept(httpRequest, j.internalCallParams);
277
308
  if (DefaultErrors_1.ErrorUtils.isError(interceptorResult) && !allowInterceptorError) {
278
309
  this.sendResponse(req.get("Referer") ?? "", res, interceptorResult, tme, ep, httpRequest);
279
310
  return;
@@ -324,7 +355,7 @@ class APIService extends BaseService_1.BaseService {
324
355
  }
325
356
  this.sendResponse(req.get("Referer") ?? "", res, result, tme, ep, httpRequest);
326
357
  };
327
- execute();
358
+ return execute();
328
359
  });
329
360
  }
330
361
  }
@@ -359,15 +390,17 @@ class APIService extends BaseService_1.BaseService {
359
390
  endpoint: req?.endpoint,
360
391
  headers: req?.headers,
361
392
  method: req?.method,
362
- data: req?.data,
393
+ data: this.checkDataLength(req?.data),
363
394
  params: req?.params,
364
395
  interceptorResult: req?.interceptorResult
365
396
  },
366
- response: data
397
+ response: this.checkDataLength(data),
367
398
  };
368
399
  this.netLog.push(logItem);
369
400
  if (this.netLog.length > 100)
370
401
  this.netLog.shift();
402
+ if (nextLogID > Number.MAX_SAFE_INTEGER - 1000)
403
+ nextLogID = 0;
371
404
  }
372
405
  async sendResponse(ref, res, data, requestTime, endpoint, req) {
373
406
  if (data.blockResponse) {
@@ -392,7 +425,7 @@ class APIService extends BaseService_1.BaseService {
392
425
  if (this.options.appVersion)
393
426
  data.version = this.options.appVersion;
394
427
  MonitorService_1.S_STAT_REGISTRATE_REQUEST.invoke(data);
395
- if (res.destroyed || res.closed) {
428
+ if (res.socket?.destroyed || res.writableEnded || res.headersSent || (res.destroyed !== undefined && res.destroyed)) {
396
429
  (0, LogService_1.logAPI)("Connection already closed, can't send response for: " + data.endpoint);
397
430
  }
398
431
  else {
@@ -401,11 +434,17 @@ class APIService extends BaseService_1.BaseService {
401
434
  res.sendFile(data.file, err => {
402
435
  if (err) {
403
436
  (0, LogService_1.logError)("Can't send file: " + data.file);
404
- this.sendResponse(ref, res, {
405
- error: DefaultErrors_1.default.CANT_SEND_FILE,
406
- data: null,
407
- httpStatus: 500
408
- }, requestTime, data.endpoint, req);
437
+ if (!res.headersSent && !res.writableEnded) {
438
+ res.status(500).send({ error: DefaultErrors_1.default.CANT_SEND_FILE, data: null });
439
+ return;
440
+ }
441
+ else {
442
+ try {
443
+ res.destroy?.(err);
444
+ }
445
+ catch { }
446
+ return;
447
+ }
409
448
  }
410
449
  else {
411
450
  if (req)
@@ -447,27 +486,18 @@ class APIService extends BaseService_1.BaseService {
447
486
  this.addNetlog(data, req, requestTime, 0);
448
487
  }
449
488
  checkDataLength(data, result, lvl) {
450
- if (!lvl)
451
- lvl = 0;
452
- if (typeof data !== "object") {
453
- if (typeof data === "string" && data.length > 1024)
454
- return data.substring(0, 1000) + "... (total:" + data.length + ")";
455
- return data;
489
+ let json = "";
490
+ try {
491
+ json = JSON.stringify(data, (k, v) => {
492
+ if (typeof v === 'string' && v.length > 255)
493
+ return v.slice(0, 255) + `...(${v.length})`;
494
+ return v;
495
+ });
456
496
  }
457
- if (typeof data === "object") {
458
- if (!result)
459
- result = {};
460
- let arrcnt = 100;
461
- for (let i in data) {
462
- result[i] = this.checkDataLength(data[i], result[i], lvl + 1);
463
- arrcnt--;
464
- if (arrcnt <= 0) {
465
- result["..."] = "... total: " + Array.isArray(data) ? data.length : Object.keys(data).length;
466
- break;
467
- }
468
- }
497
+ catch (e) {
498
+ json = "[unserializable data]";
469
499
  }
470
- return result ?? null;
500
+ return json.length > 500 ? json.slice(0, 500) + `...(total ${json.length})` : json;
471
501
  }
472
502
  }
473
503
  exports.APIService = APIService;
@@ -11,6 +11,7 @@ type HTTPMethod = typeof httpMethods[number];
11
11
  export type IHandler = Record<HTTPMethod, ((req: HTTPRequestVO) => Promise<TransferPacketVO<any>>) | {
12
12
  controller: (req: HTTPRequestVO) => Promise<TransferPacketVO<any>>;
13
13
  ignoreInterceptor?: boolean;
14
+ internalCallParams?: any;
14
15
  allowInterceptorError?: boolean;
15
16
  validationModel?: any;
16
17
  resultModel?: any;
@@ -24,6 +25,7 @@ export interface IEndpointHandler {
24
25
  endpoint: string;
25
26
  handler?: ((req: HTTPRequestVO) => Promise<TransferPacketVO<any>>) | IHandler;
26
27
  ignoreInterceptor?: boolean;
28
+ internalCallParams?: any;
27
29
  ignoreInDocumentation?: boolean;
28
30
  ignoreHttpLogging?: boolean;
29
31
  independed?: boolean;
@@ -42,6 +44,7 @@ export declare class BaseEndpoint implements IBaseEndpoint {
42
44
  ignoreInDocumentation: boolean;
43
45
  private static entrypoint;
44
46
  endpoint: string;
47
+ protected streamOptions: Record<string, any>;
45
48
  static setEntryPoint: (ep: string) => void;
46
49
  static getEntryPoint: () => string;
47
50
  constructor(endpoint: string);
@@ -51,6 +54,11 @@ export declare class BaseEndpoint implements IBaseEndpoint {
51
54
  __precheck(req: HTTPRequestVO): Promise<TransferPacketVO<any> | null>;
52
55
  __execute(req: HTTPRequestVO): Promise<TransferPacketVO<any>>;
53
56
  protected validateStructure(structure: any, req: HTTPRequestVO): Promise<void>;
54
- protected createStream(req: HTTPRequestVO): HTTPRequestVO;
57
+ protected createStream(req: HTTPRequestVO, opts: {
58
+ contentType?: string;
59
+ headers?: {
60
+ [key: string]: string;
61
+ };
62
+ }): HTTPRequestVO;
55
63
  }
56
64
  export {};
@@ -17,6 +17,7 @@ class BaseEndpoint {
17
17
  ignoreInDocumentation = false;
18
18
  static entrypoint = "/";
19
19
  endpoint = "";
20
+ streamOptions = {};
20
21
  static setEntryPoint = (ep) => {
21
22
  this.entrypoint = ep;
22
23
  if (!this.entrypoint.endsWith("/")) {
@@ -73,43 +74,65 @@ class BaseEndpoint {
73
74
  for (let i of this.endpoints) {
74
75
  let targetEP = BaseEndpoint.entrypoint + i.endpoint;
75
76
  targetEP = targetEP.replaceAll("//", "/");
76
- if (targetEP === req.endpoint) {
77
- if (i.handler && typeof i.handler === "function") {
78
- if (req.params && req.params.validationModel && req.params.validationModel === "1")
79
- return { data: i.validationModel };
80
- if (i.validationModel)
81
- await this.validateStructure(i.validationModel, req);
82
- return i.handler.call(this, req);
77
+ if (targetEP !== req.endpoint)
78
+ continue;
79
+ const httpMethod = req.method.toUpperCase();
80
+ const resolveAsStream = () => {
81
+ if (i.asStream)
82
+ return true;
83
+ if (i.handler && typeof i.handler === "object") {
84
+ const h = i.handler[httpMethod];
85
+ if (h && typeof h === "object" && "asStream" in h && h.asStream)
86
+ return true;
83
87
  }
84
- const httpMethod = req.method.toUpperCase();
85
- if (i.handler && i.handler[httpMethod] && typeof i.handler[httpMethod] === "function") {
86
- if (req.params && req.params.validationModel && req.params.validationModel === "1")
87
- return { data: i.validationModel };
88
- if (i.validationModel)
89
- await this.validateStructure(i.validationModel, req);
90
- if (i.asStream)
91
- req = this.createStream(req);
92
- return i.handler[httpMethod].call(this, req);
93
- }
94
- if (i.handler
95
- && i.handler[httpMethod]
96
- && typeof i.handler[httpMethod] === "object"
97
- && "controller" in i.handler[httpMethod]
98
- && typeof i.handler[httpMethod].controller === "function") {
99
- const vmodel = i.handler[httpMethod].validationModel;
100
- if (req.params && req.params.validationModel && req.params.validationModel === "1")
101
- return { data: vmodel };
102
- if (vmodel)
103
- await this.validateStructure(vmodel, req);
104
- if (i.asStream)
105
- req = this.createStream(req);
106
- return i.handler[httpMethod].controller.call(this, req);
107
- }
108
- return {
109
- error: { ...DefaultErrors_1.default.METHOD_NOT_ALLOWED, details: "No handler for " + httpMethod },
110
- data: null
111
- };
88
+ return false;
89
+ };
90
+ const asStream = resolveAsStream();
91
+ if (i.handler && typeof i.handler === "function") {
92
+ if (req.data && req.data.validationModel && req.data.validationModel === "1")
93
+ return { data: i.validationModel };
94
+ if (i.validationModel)
95
+ await this.validateStructure(i.validationModel, req);
96
+ if (asStream)
97
+ req = this.createStream(req, this.streamOptions[i.endpoint] ?? {});
98
+ const result = await i.handler.call(this, req);
99
+ if (asStream)
100
+ return { blockResponse: true, data: null };
101
+ return result;
102
+ }
103
+ if (i.handler && i.handler[httpMethod] && typeof i.handler[httpMethod] === "function") {
104
+ if (req.data && req.data.validationModel && req.data.validationModel === "1")
105
+ return { data: i.validationModel };
106
+ if (i.validationModel)
107
+ await this.validateStructure(i.validationModel, req);
108
+ if (asStream)
109
+ req = this.createStream(req, this.streamOptions[i.endpoint] ?? {});
110
+ const result = await i.handler[httpMethod].call(this, req);
111
+ if (asStream)
112
+ return { blockResponse: true, data: null };
113
+ return result;
114
+ }
115
+ if (i.handler
116
+ && i.handler[httpMethod]
117
+ && typeof i.handler[httpMethod] === "object"
118
+ && "controller" in i.handler[httpMethod]
119
+ && typeof i.handler[httpMethod].controller === "function") {
120
+ const vmodel = i.handler[httpMethod].validationModel;
121
+ if (req.data && req.data.validationModel && req.data.validationModel === "1")
122
+ return { data: vmodel };
123
+ if (vmodel)
124
+ await this.validateStructure(vmodel, req);
125
+ if (asStream)
126
+ req = this.createStream(req, this.streamOptions[i.endpoint] ?? {});
127
+ const result = await i.handler[httpMethod].controller.call(this, req);
128
+ if (asStream)
129
+ return { blockResponse: true, data: null };
130
+ return result;
112
131
  }
132
+ return {
133
+ error: { ...DefaultErrors_1.default.METHOD_NOT_ALLOWED, details: "No handler for " + httpMethod },
134
+ data: null
135
+ };
113
136
  }
114
137
  }
115
138
  (0, LogService_1.logWarn)("${BaseEndpoint.js}", "Unhandled entrypoint: " + this.endpoint);
@@ -128,17 +151,101 @@ class BaseEndpoint {
128
151
  throw { ...DefaultErrors_1.default.WRONG_PARAMS, details: report };
129
152
  Validator_1.Validator.filterStructure(structure, req.data);
130
153
  }
131
- createStream(req) {
154
+ createStream(req, opts) {
132
155
  const res = req.response;
133
- res.setHeader('Content-Type', 'text/plain');
134
- res.setHeader('Transfer-Encoding', 'chunked');
135
- const stream = new stream_1.Readable();
136
- stream.pipe(res);
156
+ if (res.headersSent)
157
+ throw { ...DefaultErrors_1.default.INTERNAL_SERVER_ERROR, details: "Headers already sent, can't create stream" };
158
+ res.statusCode = 200;
159
+ res.setHeader("Content-Type", opts?.contentType ?? "application/octet-stream");
160
+ res.setHeader("Transfer-Encoding", "chunked");
161
+ res.setHeader("Cache-Control", "no-cache");
162
+ if (opts?.headers) {
163
+ for (let h in opts.headers) {
164
+ res.setHeader(h, opts.headers[h]);
165
+ }
166
+ }
167
+ const stream = new stream_1.Readable({
168
+ read() {
169
+ }
170
+ });
171
+ let finished = false;
172
+ let timer;
173
+ const cleanup = () => {
174
+ if (finished)
175
+ return;
176
+ finished = true;
177
+ if (timer)
178
+ clearTimeout(timer);
179
+ try {
180
+ if (!stream.destroyed) {
181
+ stream.destroy();
182
+ }
183
+ }
184
+ catch (e) {
185
+ console.error("Error during stream cleanup:", e);
186
+ }
187
+ try {
188
+ if (!res.writableEnded)
189
+ res.end();
190
+ }
191
+ catch { }
192
+ };
193
+ const endOk = () => {
194
+ if (finished)
195
+ return;
196
+ finished = true;
197
+ if (timer)
198
+ clearTimeout(timer);
199
+ try {
200
+ if (!stream.destroyed)
201
+ stream.push(null);
202
+ }
203
+ catch (e) {
204
+ cleanup();
205
+ }
206
+ };
207
+ res.on('close', () => {
208
+ cleanup();
209
+ });
210
+ const setupTimer = () => {
211
+ if (finished)
212
+ return;
213
+ if (timer)
214
+ clearTimeout(timer);
215
+ timer = setTimeout(() => cleanup(), 60_000);
216
+ };
217
+ setupTimer();
218
+ const origPush = stream.push.bind(stream);
219
+ stream.push = (chunk, encoding) => {
220
+ if (chunk === null)
221
+ return origPush(null);
222
+ if (finished)
223
+ return false;
224
+ setupTimer();
225
+ return origPush(chunk, encoding);
226
+ };
227
+ req.raw?.on?.("aborted", () => {
228
+ cleanup();
229
+ try {
230
+ res.destroy?.();
231
+ }
232
+ catch { }
233
+ });
234
+ req.raw?.on?.("close", cleanup);
137
235
  stream.on('error', (err) => {
138
- console.error('Stream error:', err);
139
- res.status(500).send('Stream error');
236
+ console.error("Stream error:", err);
237
+ cleanup();
238
+ try {
239
+ res.destroy?.(err);
240
+ }
241
+ catch {
242
+ res.end();
243
+ }
140
244
  });
245
+ res.on("finish", cleanup);
246
+ stream.pipe(res);
141
247
  req.stream = stream;
248
+ req.endStream = (force) => force ? cleanup() : endOk();
142
249
  return req;
143
250
  }
144
251
  }
@@ -1,32 +1,6 @@
1
1
  "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
2
  Object.defineProperty(exports, "__esModule", { value: true });
6
3
  exports.LocalRequest = void 0;
7
- const axios_1 = __importDefault(require("axios"));
8
4
  const LocalRequest = async (ep, data, method, headers) => {
9
- let m = "get";
10
- if (method)
11
- m = method.toLowerCase();
12
- if (data && !method)
13
- m = "post";
14
- if (ep.startsWith("/"))
15
- ep = ep.substring(1);
16
- let url = "http://localhost:8091/" + ep;
17
- if (ep.toLowerCase().startsWith("http"))
18
- url = ep;
19
- let resp = null;
20
- console.log("open url: ", url);
21
- try {
22
- if (m === "get")
23
- resp = await axios_1.default.get(url, { headers: headers });
24
- if (m === "post")
25
- resp = await axios_1.default.post(url, data, { headers: headers });
26
- }
27
- catch (e) {
28
- console.log("ERROR! cant open url", e);
29
- }
30
- console.log(resp);
31
5
  };
32
6
  exports.LocalRequest = LocalRequest;
@@ -33,7 +33,7 @@ const DefaultErrors_1 = __importStar(require("../structures/DefaultErrors"));
33
33
  const Validator_1 = require("../helper/Validator");
34
34
  const UID_1 = require("../helper/UID");
35
35
  const crypto_1 = __importDefault(require("crypto"));
36
- const axios_1 = __importDefault(require("axios"));
36
+ const __1 = require("../..");
37
37
  exports.REQ_EXTERNAL_CALL = new badmfck_signal_1.Req(undefined, "REQ_EXTERNAL_CALL");
38
38
  exports.REQ_XT = new badmfck_signal_1.Req(undefined, "REQ_XT");
39
39
  exports.REQ_XT.listener = async ({ id, req }) => {
@@ -89,33 +89,21 @@ class ExternalService extends BaseService_1.BaseService {
89
89
  const encrypted = this.encrypt({ requestName, requestData });
90
90
  if (this.options.url.endsWith("/"))
91
91
  this.options.url = this.options.url.substring(0, this.options.url.length - 1);
92
- try {
93
- const resp = await axios_1.default.post(this.options.url + "/external/call/" + this.options.id, encrypted.data, {
94
- headers: {
95
- "authorization": encrypted.authorization,
96
- "Content-Type": "application/json",
97
- "Accept": "application/json"
98
- }
99
- });
100
- const data = resp.data;
101
- if (typeof data === "object" && "error" in data)
102
- return data.error;
103
- if (typeof data === "object" && "data" in data)
104
- return data.data;
105
- return data;
106
- }
107
- catch (e) {
108
- const err = e;
109
- if (err.response && err.response.data)
110
- console.log("ExternalService.requestExternalCall", err.response.data);
111
- else
112
- console.error("ExternalService.requestExternalCall", err.cause);
113
- if (err.response) {
114
- if (err.response.data && typeof err.response.data === "object" && "error" in err.response.data)
115
- return err.response.data.error;
92
+ const resp = await __1.Http.post(this.options.url + "/external/call/" + this.options.id, encrypted.data, {
93
+ headers: {
94
+ "authorization": encrypted.authorization,
95
+ "Content-Type": "application/json",
96
+ "Accept": "application/json"
116
97
  }
117
- return { ...DefaultErrors_1.default.BAD_REQUEST, details: err.cause?.message };
98
+ });
99
+ if (!resp.ok) {
100
+ return { ...DefaultErrors_1.default.BAD_REQUEST, details: resp.details, stack: resp.error };
118
101
  }
102
+ const json = await resp.data.json();
103
+ if (typeof json === "object" && "error" in json)
104
+ return json.error;
105
+ if (typeof json === "object" && "data" in json)
106
+ return json.data;
119
107
  }
120
108
  async onExternalCall(req) {
121
109
  const data = this.decrypt(req);
@@ -0,0 +1,4 @@
1
+ import { BaseService } from "../BaseService";
2
+ export declare class MicroserviceClient extends BaseService {
3
+ constructor();
4
+ }
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MicroserviceClient = void 0;
4
+ const BaseService_1 = require("../BaseService");
5
+ class MicroserviceClient extends BaseService_1.BaseService {
6
+ constructor() {
7
+ super("MicroserviceClient");
8
+ }
9
+ }
10
+ exports.MicroserviceClient = MicroserviceClient;
@@ -0,0 +1,4 @@
1
+ import { BaseService } from "../BaseService";
2
+ export declare class MicroserviceHost extends BaseService {
3
+ constructor();
4
+ }
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MicroserviceHost = void 0;
4
+ const BaseService_1 = require("../BaseService");
5
+ class MicroserviceHost extends BaseService_1.BaseService {
6
+ constructor() {
7
+ super("MicroserviceHost");
8
+ }
9
+ }
10
+ exports.MicroserviceHost = MicroserviceHost;
File without changes
@@ -0,0 +1 @@
1
+ "use strict";
@@ -0,0 +1,97 @@
1
+ import { Dispatcher, Agent } from "undici";
2
+ export type HttpMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
3
+ export type THttpOk<T> = {
4
+ ok: true;
5
+ status: number;
6
+ data: T;
7
+ headers: Record<string, string>;
8
+ };
9
+ export type THttpErr<E = any> = {
10
+ ok: false;
11
+ status?: number;
12
+ error: E;
13
+ details?: any;
14
+ };
15
+ export type THttpResult<T, E = any> = THttpOk<T> | THttpErr<E>;
16
+ export type TRetryPolicy = {
17
+ enabled?: boolean;
18
+ maxAttempts?: number;
19
+ baseDelayMs?: number;
20
+ maxDelayMs?: number;
21
+ retryOnStatuses?: number[];
22
+ retryOnNetworkErrors?: boolean;
23
+ };
24
+ export type THttpConfig = {
25
+ baseUrl?: string;
26
+ dispatcher?: Dispatcher;
27
+ timeoutMs?: number;
28
+ headers?: Record<string, string>;
29
+ retry?: TRetryPolicy;
30
+ onRequest?: (ctx: {
31
+ method: HttpMethod;
32
+ url: string;
33
+ attempt: number;
34
+ }) => void;
35
+ onResponse?: (ctx: {
36
+ method: HttpMethod;
37
+ url: string;
38
+ attempt: number;
39
+ status: number;
40
+ ms: number;
41
+ }) => void;
42
+ onError?: (ctx: {
43
+ method: HttpMethod;
44
+ url: string;
45
+ attempt: number;
46
+ ms: number;
47
+ err: unknown;
48
+ }) => void;
49
+ };
50
+ export type TRequestOptions = {
51
+ method: HttpMethod;
52
+ url: string;
53
+ query?: Record<string, string | number | boolean | null | undefined>;
54
+ headers?: Record<string, string>;
55
+ body?: any;
56
+ timeoutMs?: number;
57
+ dispatcher?: Dispatcher;
58
+ responseType?: "json" | "text" | "raw";
59
+ throwOnJsonParseError?: boolean;
60
+ retry?: TRetryPolicy;
61
+ allowBody?: boolean;
62
+ };
63
+ export declare class Http {
64
+ private static _inited;
65
+ private static config;
66
+ private static _sharedAgent;
67
+ private static _dcEnabled;
68
+ private static _dcConnectedChannel;
69
+ private static _dcListener;
70
+ private static _activeConnectionsTotal;
71
+ private static _activeConnectionsByHost;
72
+ private static _countedSockets;
73
+ private static _socketHostKey;
74
+ private static ensureInit;
75
+ static configure(partial: Partial<THttpConfig>): void;
76
+ static createAgent(opts?: ConstructorParameters<typeof Agent>[0]): Agent;
77
+ static closeSharedAgent(): void;
78
+ static basicAuth(username: string, password: string): string;
79
+ static bearer(token: string): string;
80
+ static enableConnectionCounterByHost(): void;
81
+ static disableConnectionCounterByHost(options?: {
82
+ reset?: boolean;
83
+ }): void;
84
+ private static _makeHostKey;
85
+ static getActiveConnectionsTotal(): number;
86
+ static getActiveConnectionsByHost(): Map<string, number>;
87
+ static getActiveConnectionsSnapshot(): {
88
+ total: number;
89
+ byHost: Record<string, number>;
90
+ };
91
+ static request<T = any, E = any>(opts: TRequestOptions): Promise<THttpResult<T, E>>;
92
+ static get<T = any, E = any>(url: string, opts?: Omit<TRequestOptions, "method" | "url">): Promise<THttpResult<T, E>>;
93
+ static post<T = any, E = any>(url: string, body?: any, opts?: Omit<TRequestOptions, "method" | "url" | "body">): Promise<THttpResult<T, E>>;
94
+ static put<T = any, E = any>(url: string, body?: any, opts?: Omit<TRequestOptions, "method" | "url" | "body">): Promise<THttpResult<T, E>>;
95
+ static patch<T = any, E = any>(url: string, body?: any, opts?: Omit<TRequestOptions, "method" | "url" | "body">): Promise<THttpResult<T, E>>;
96
+ static delete<T = any, E = any>(url: string, opts?: Omit<TRequestOptions, "method" | "url">): Promise<THttpResult<T, E>>;
97
+ }
@@ -0,0 +1,356 @@
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.Http = void 0;
7
+ const undici_1 = require("undici");
8
+ const node_diagnostics_channel_1 = __importDefault(require("node:diagnostics_channel"));
9
+ function sleep(ms) {
10
+ return new Promise((r) => setTimeout(r, ms));
11
+ }
12
+ function clamp(n, min, max) {
13
+ return Math.max(min, Math.min(max, n));
14
+ }
15
+ function buildQuery(query) {
16
+ if (!query)
17
+ return "";
18
+ const params = new URLSearchParams();
19
+ for (const [k, v] of Object.entries(query)) {
20
+ if (v === null || v === undefined)
21
+ continue;
22
+ params.set(k, String(v));
23
+ }
24
+ const s = params.toString();
25
+ return s ? `?${s}` : "";
26
+ }
27
+ function mergeHeaders(a, b) {
28
+ return { ...(a || {}), ...(b || {}) };
29
+ }
30
+ function normalizeHeaders(headers) {
31
+ const out = {};
32
+ if (!headers || typeof headers !== "object")
33
+ return out;
34
+ for (const [k, v] of Object.entries(headers)) {
35
+ if (Array.isArray(v))
36
+ out[k.toLowerCase()] = v.join(", ");
37
+ else if (v !== undefined && v !== null)
38
+ out[k.toLowerCase()] = String(v);
39
+ }
40
+ return out;
41
+ }
42
+ function isRetryableStatus(status, retryOnStatuses) {
43
+ return retryOnStatuses.includes(status);
44
+ }
45
+ function isLikelyNetworkError(err) {
46
+ if (!err || typeof err !== "object")
47
+ return false;
48
+ const anyErr = err;
49
+ if (anyErr?.name === "AbortError")
50
+ return true;
51
+ const code = anyErr?.code;
52
+ const networkCodes = new Set(["ECONNRESET", "ETIMEDOUT", "ECONNREFUSED", "EAI_AGAIN", "ENOTFOUND", "EPIPE"]);
53
+ if (code && networkCodes.has(code))
54
+ return true;
55
+ if (err instanceof undici_1.errors.ConnectTimeoutError)
56
+ return true;
57
+ if (err instanceof undici_1.errors.HeadersTimeoutError)
58
+ return true;
59
+ if (err instanceof undici_1.errors.BodyTimeoutError)
60
+ return true;
61
+ if (err instanceof undici_1.errors.SocketError)
62
+ return true;
63
+ return false;
64
+ }
65
+ class Http {
66
+ static _inited = false;
67
+ static config = {
68
+ baseUrl: "",
69
+ timeoutMs: 5000,
70
+ headers: {
71
+ accept: "application/json",
72
+ },
73
+ retry: {
74
+ enabled: true,
75
+ maxAttempts: 2,
76
+ baseDelayMs: 200,
77
+ maxDelayMs: 1500,
78
+ retryOnStatuses: [429, 502, 503, 504],
79
+ retryOnNetworkErrors: true,
80
+ },
81
+ };
82
+ static _sharedAgent = null;
83
+ static _dcEnabled = false;
84
+ static _dcConnectedChannel = null;
85
+ static _dcListener = null;
86
+ static _activeConnectionsTotal = 0;
87
+ static _activeConnectionsByHost = new Map();
88
+ static _countedSockets = new WeakSet();
89
+ static _socketHostKey = new WeakMap();
90
+ static ensureInit() {
91
+ if (this._inited)
92
+ return;
93
+ this._inited = true;
94
+ this.enableConnectionCounterByHost();
95
+ if (!this.config.dispatcher) {
96
+ this._sharedAgent = this.createAgent({
97
+ connections: 100,
98
+ keepAliveTimeout: 60_000,
99
+ keepAliveMaxTimeout: 60_000,
100
+ });
101
+ this.config.dispatcher = this._sharedAgent;
102
+ }
103
+ }
104
+ static configure(partial) {
105
+ this.config = {
106
+ ...this.config,
107
+ ...partial,
108
+ headers: mergeHeaders(this.config.headers, partial.headers),
109
+ retry: { ...(this.config.retry || {}), ...(partial.retry || {}) },
110
+ };
111
+ if (partial.dispatcher && this._sharedAgent) {
112
+ try {
113
+ this._sharedAgent.close();
114
+ }
115
+ catch { }
116
+ this._sharedAgent = null;
117
+ }
118
+ this.ensureInit();
119
+ }
120
+ static createAgent(opts) {
121
+ return new undici_1.Agent({
122
+ keepAliveTimeout: 60_000,
123
+ keepAliveMaxTimeout: 60_000,
124
+ ...opts,
125
+ });
126
+ }
127
+ static closeSharedAgent() {
128
+ if (!this._sharedAgent)
129
+ return;
130
+ try {
131
+ this._sharedAgent.close();
132
+ }
133
+ catch { }
134
+ this._sharedAgent = null;
135
+ if (this.config.dispatcher === this._sharedAgent) {
136
+ this.config.dispatcher = undefined;
137
+ }
138
+ }
139
+ static basicAuth(username, password) {
140
+ const token = Buffer.from(`${username}:${password}`).toString("base64");
141
+ return `Basic ${token}`;
142
+ }
143
+ static bearer(token) {
144
+ return `Bearer ${token}`;
145
+ }
146
+ static enableConnectionCounterByHost() {
147
+ if (this._dcEnabled)
148
+ return;
149
+ this._dcEnabled = true;
150
+ const ch = node_diagnostics_channel_1.default.channel("undici:client:connected");
151
+ const listener = (message) => {
152
+ const msg = message;
153
+ const socket = msg?.socket;
154
+ if (!socket)
155
+ return;
156
+ if (this._countedSockets.has(socket))
157
+ return;
158
+ this._countedSockets.add(socket);
159
+ const key = this._makeHostKey(msg?.connectParams, socket);
160
+ this._socketHostKey.set(socket, key);
161
+ this._activeConnectionsTotal += 1;
162
+ this._activeConnectionsByHost.set(key, (this._activeConnectionsByHost.get(key) ?? 0) + 1);
163
+ const onClose = () => {
164
+ this._activeConnectionsTotal = Math.max(0, this._activeConnectionsTotal - 1);
165
+ const k = this._socketHostKey.get(socket) ?? "unknown://unknown:0";
166
+ const prev = this._activeConnectionsByHost.get(k) ?? 0;
167
+ const next = Math.max(0, prev - 1);
168
+ if (next === 0)
169
+ this._activeConnectionsByHost.delete(k);
170
+ else
171
+ this._activeConnectionsByHost.set(k, next);
172
+ socket.off("close", onClose);
173
+ };
174
+ socket.on("close", onClose);
175
+ };
176
+ ch.subscribe(listener);
177
+ this._dcConnectedChannel = ch;
178
+ this._dcListener = listener;
179
+ }
180
+ static disableConnectionCounterByHost(options) {
181
+ if (!this._dcEnabled)
182
+ return;
183
+ const ch = this._dcConnectedChannel;
184
+ const listener = this._dcListener;
185
+ if (ch && listener) {
186
+ ch.unsubscribe(listener);
187
+ }
188
+ this._dcConnectedChannel = null;
189
+ this._dcListener = null;
190
+ this._dcEnabled = false;
191
+ if (options?.reset) {
192
+ this._activeConnectionsTotal = 0;
193
+ this._activeConnectionsByHost.clear();
194
+ this._countedSockets = new WeakSet();
195
+ this._socketHostKey = new WeakMap();
196
+ }
197
+ }
198
+ static _makeHostKey(connectParams, socket) {
199
+ const protocolRaw = connectParams?.protocol || "unknown:";
200
+ const protocol = protocolRaw.replace(/\/+$/g, "");
201
+ const hostname = connectParams?.hostname || connectParams?.host;
202
+ let port = connectParams?.port !== undefined && connectParams?.port !== null ? String(connectParams.port) : undefined;
203
+ if (!port) {
204
+ if (protocol === "https:")
205
+ port = "443";
206
+ else if (protocol === "http:")
207
+ port = "80";
208
+ else
209
+ port = "?";
210
+ }
211
+ if (hostname)
212
+ return `${protocol}//${hostname}:${port}`;
213
+ const addr = socket?.remoteAddress ?? "unknown";
214
+ const p = socket?.remotePort ?? "?";
215
+ return `${protocol}//${addr}:${p}`;
216
+ }
217
+ static getActiveConnectionsTotal() {
218
+ return this._activeConnectionsTotal;
219
+ }
220
+ static getActiveConnectionsByHost() {
221
+ return new Map(this._activeConnectionsByHost);
222
+ }
223
+ static getActiveConnectionsSnapshot() {
224
+ return {
225
+ total: this._activeConnectionsTotal,
226
+ byHost: Object.fromEntries(this._activeConnectionsByHost.entries()),
227
+ };
228
+ }
229
+ static async request(opts) {
230
+ this.ensureInit();
231
+ const cfg = this.config;
232
+ const retryCfg = {
233
+ enabled: opts.retry?.enabled ?? cfg.retry?.enabled ?? true,
234
+ maxAttempts: opts.retry?.maxAttempts ?? cfg.retry?.maxAttempts ?? 2,
235
+ baseDelayMs: opts.retry?.baseDelayMs ?? cfg.retry?.baseDelayMs ?? 200,
236
+ maxDelayMs: opts.retry?.maxDelayMs ?? cfg.retry?.maxDelayMs ?? 1500,
237
+ retryOnStatuses: opts.retry?.retryOnStatuses ?? cfg.retry?.retryOnStatuses ?? [429, 502, 503, 504],
238
+ retryOnNetworkErrors: opts.retry?.retryOnNetworkErrors ?? cfg.retry?.retryOnNetworkErrors ?? true,
239
+ };
240
+ const timeoutMs = opts.timeoutMs ?? cfg.timeoutMs ?? 5000;
241
+ const dispatcher = opts.dispatcher ?? cfg.dispatcher;
242
+ const baseUrl = cfg.baseUrl || "";
243
+ const isAbsolute = /^https?:\/\//i.test(opts.url);
244
+ const query = buildQuery(opts.query);
245
+ const url = (isAbsolute ? opts.url : baseUrl + opts.url) + query;
246
+ const headers = mergeHeaders(cfg.headers, opts.headers);
247
+ let body = undefined;
248
+ const hasBody = opts.body !== undefined && opts.body !== null;
249
+ const allowBody = opts.allowBody ?? (opts.method !== "GET" && opts.method !== "DELETE");
250
+ if (hasBody && allowBody) {
251
+ const ct = (headers["content-type"] || headers["Content-Type"] || "").toLowerCase();
252
+ if (!ct)
253
+ headers["content-type"] = "application/json";
254
+ const ct2 = (headers["content-type"] || headers["Content-Type"] || "").toLowerCase();
255
+ if (ct2.includes("application/json") && typeof opts.body !== "string" && !(opts.body instanceof Buffer)) {
256
+ body = JSON.stringify(opts.body);
257
+ }
258
+ else {
259
+ body = opts.body;
260
+ }
261
+ }
262
+ const responseType = opts.responseType ?? "json";
263
+ const throwOnJsonParseError = opts.throwOnJsonParseError ?? false;
264
+ for (let attempt = 1; attempt <= retryCfg.maxAttempts; attempt++) {
265
+ const started = Date.now();
266
+ cfg.onRequest?.({ method: opts.method, url, attempt });
267
+ const controller = new AbortController();
268
+ const t = setTimeout(() => controller.abort(), timeoutMs);
269
+ try {
270
+ const res = await (0, undici_1.request)(url, {
271
+ method: opts.method,
272
+ headers,
273
+ body,
274
+ signal: controller.signal,
275
+ dispatcher,
276
+ });
277
+ const ms = Date.now() - started;
278
+ const status = res.statusCode;
279
+ cfg.onResponse?.({ method: opts.method, url, attempt, status, ms });
280
+ const hdrs = normalizeHeaders(res.headers);
281
+ let parsed = undefined;
282
+ let rawText;
283
+ if (responseType === "raw") {
284
+ await res.body.dump();
285
+ parsed = undefined;
286
+ }
287
+ else if (responseType === "text") {
288
+ rawText = await res.body.text();
289
+ parsed = rawText;
290
+ }
291
+ else {
292
+ rawText = await res.body.text();
293
+ if (rawText.length === 0) {
294
+ parsed = null;
295
+ }
296
+ else {
297
+ try {
298
+ parsed = JSON.parse(rawText);
299
+ }
300
+ catch (e) {
301
+ if (throwOnJsonParseError)
302
+ throw e;
303
+ parsed = rawText;
304
+ }
305
+ }
306
+ }
307
+ if (status >= 200 && status < 300) {
308
+ return { ok: true, status, data: parsed, headers: hdrs };
309
+ }
310
+ const retryable = retryCfg.enabled &&
311
+ isRetryableStatus(status, retryCfg.retryOnStatuses) &&
312
+ attempt < retryCfg.maxAttempts;
313
+ if (retryable) {
314
+ const backoff = clamp(retryCfg.baseDelayMs * Math.pow(2, attempt - 1), retryCfg.baseDelayMs, retryCfg.maxDelayMs);
315
+ await sleep(backoff);
316
+ continue;
317
+ }
318
+ return { ok: false, status, error: parsed, details: { headers: hdrs } };
319
+ }
320
+ catch (err) {
321
+ const ms = Date.now() - started;
322
+ cfg.onError?.({ method: opts.method, url, attempt, ms, err });
323
+ const retryable = retryCfg.enabled &&
324
+ retryCfg.retryOnNetworkErrors &&
325
+ isLikelyNetworkError(err) &&
326
+ attempt < retryCfg.maxAttempts;
327
+ if (retryable) {
328
+ const backoff = clamp(retryCfg.baseDelayMs * Math.pow(2, attempt - 1), retryCfg.baseDelayMs, retryCfg.maxDelayMs);
329
+ await sleep(backoff);
330
+ continue;
331
+ }
332
+ return { ok: false, error: err, details: { network: true } };
333
+ }
334
+ finally {
335
+ clearTimeout(t);
336
+ }
337
+ }
338
+ return { ok: false, error: "UNKNOWN" };
339
+ }
340
+ static get(url, opts) {
341
+ return this.request({ ...(opts || {}), method: "GET", url });
342
+ }
343
+ static post(url, body, opts) {
344
+ return this.request({ ...(opts || {}), method: "POST", url, body });
345
+ }
346
+ static put(url, body, opts) {
347
+ return this.request({ ...(opts || {}), method: "PUT", url, body });
348
+ }
349
+ static patch(url, body, opts) {
350
+ return this.request({ ...(opts || {}), method: "PATCH", url, body });
351
+ }
352
+ static delete(url, opts) {
353
+ return this.request({ ...(opts || {}), method: "DELETE", url });
354
+ }
355
+ }
356
+ exports.Http = Http;
@@ -13,7 +13,7 @@ const DefaultErrors_1 = __importDefault(require("../structures/DefaultErrors"));
13
13
  const MonitorService_1 = require("../MonitorService");
14
14
  const fs_1 = __importDefault(require("fs"));
15
15
  const path_1 = __importDefault(require("path"));
16
- const axios_1 = __importDefault(require("axios"));
16
+ const Http_1 = require("../http/Http");
17
17
  exports.S_MONITOR_REGISTRATE_ACTION = new badmfck_signal_1.Signal();
18
18
  const logMap = {
19
19
  ALL: 10,
@@ -54,43 +54,11 @@ class Monitor extends BaseEndpoint_1.BaseEndpoint {
54
54
  this.lastTimeCacheChecked = +new Date();
55
55
  let response = null;
56
56
  try {
57
- response = await axios_1.default.get("https://igorbloom.com/dist/monitor.html");
57
+ response = await Http_1.Http.get("https://igorbloom.com/dist/monitor.html");
58
58
  }
59
59
  catch (e) {
60
60
  (0, LogService_1.logError)("Error loading monitor.html from remote server", e);
61
61
  }
62
- if (response && response.status === 200 && response.data && response.data.length > 0) {
63
- if (typeof response.data !== "string")
64
- (0, LogService_1.logError)("Monitor HTML file is not a string", response.data);
65
- else {
66
- const hash = crypto_1.default.createHash("sha256").update(response.data).digest("hex");
67
- const oldHash = crypto_1.default.createHash("sha256").update(this.indexHtmlFile).digest("hex");
68
- if (hash !== oldHash) {
69
- if (response.data.length < 1000) {
70
- (0, LogService_1.logError)("Monitor HTML file is too small, probably not loaded correctly", response.data);
71
- }
72
- else {
73
- if (response.data.substring(0, 24).toLowerCase().indexOf("<!doctype html>") === -1) {
74
- (0, LogService_1.logError)("Monitor HTML file is not a valid HTML file, <!doctype html> tag not found");
75
- }
76
- else {
77
- try {
78
- fs_1.default.writeFileSync(path_1.default.resolve(__dirname, "index.html"), response.data, "utf-8");
79
- this.fileParsed = false;
80
- this.indexHtmlFile = response.data;
81
- (0, LogService_1.logInfo)("Monitor HTML file updated from remote server");
82
- }
83
- catch (e) {
84
- (0, LogService_1.logError)("Error writing monitor HTML file to local disk", e);
85
- }
86
- }
87
- }
88
- }
89
- else {
90
- (0, LogService_1.logInfo)("Remote monitor HTML file has the same hash");
91
- }
92
- }
93
- }
94
62
  }
95
63
  return this.indexHtmlFile;
96
64
  }
@@ -36,6 +36,8 @@ export interface HTTPRequestVO<T = any, TInterceport = any> {
36
36
  unlocked?: boolean;
37
37
  internalCall?: boolean;
38
38
  stream?: Readable;
39
+ endStream?: (force?: boolean) => void;
40
+ internalCallParams?: any;
39
41
  }
40
42
  export interface IError {
41
43
  code: number;
package/dist/index.d.ts CHANGED
@@ -13,4 +13,5 @@ import { YYYYMMDDHH } from "./apiServer/helper/YYYYMMDDHH";
13
13
  import { TimeframeService } from "./apiServer/TimeframeService";
14
14
  import { JSONStableStringify } from "./apiServer/helper/JSONStableStringify";
15
15
  import { ZipUtils } from "./apiServer/helper/ZipUtils";
16
- export { ZipUtils, UID, YYYYMMDDHH, JSONStableStringify, APIService, Initializer, LocalRequest, ValidationModel, MysqlService, TimeframeService, Validator, LogService, DataProvider, ErrorUtils, ExternalService, DBService, S_MONITOR_REGISTRATE_ACTION };
16
+ import { Http } from "./apiServer/http/Http";
17
+ export { Http, ZipUtils, UID, YYYYMMDDHH, JSONStableStringify, APIService, Initializer, LocalRequest, ValidationModel, MysqlService, TimeframeService, Validator, LogService, DataProvider, ErrorUtils, ExternalService, DBService, S_MONITOR_REGISTRATE_ACTION };
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.S_MONITOR_REGISTRATE_ACTION = exports.DBService = exports.ExternalService = exports.ErrorUtils = exports.DataProvider = exports.LogService = exports.Validator = exports.TimeframeService = exports.MysqlService = exports.LocalRequest = exports.Initializer = exports.APIService = exports.JSONStableStringify = exports.YYYYMMDDHH = exports.UID = exports.ZipUtils = void 0;
3
+ exports.S_MONITOR_REGISTRATE_ACTION = exports.DBService = exports.ExternalService = exports.ErrorUtils = exports.DataProvider = exports.LogService = exports.Validator = exports.TimeframeService = exports.MysqlService = exports.LocalRequest = exports.Initializer = exports.APIService = exports.JSONStableStringify = exports.YYYYMMDDHH = exports.UID = exports.ZipUtils = exports.Http = void 0;
4
4
  const APIService_1 = require("./apiServer/APIService");
5
5
  Object.defineProperty(exports, "APIService", { enumerable: true, get: function () { return APIService_1.APIService; } });
6
6
  Object.defineProperty(exports, "Initializer", { enumerable: true, get: function () { return APIService_1.Initializer; } });
@@ -32,3 +32,5 @@ const JSONStableStringify_1 = require("./apiServer/helper/JSONStableStringify");
32
32
  Object.defineProperty(exports, "JSONStableStringify", { enumerable: true, get: function () { return JSONStableStringify_1.JSONStableStringify; } });
33
33
  const ZipUtils_1 = require("./apiServer/helper/ZipUtils");
34
34
  Object.defineProperty(exports, "ZipUtils", { enumerable: true, get: function () { return ZipUtils_1.ZipUtils; } });
35
+ const Http_1 = require("./apiServer/http/Http");
36
+ Object.defineProperty(exports, "Http", { enumerable: true, get: function () { return Http_1.Http; } });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "badmfck-api-server",
3
- "version": "3.9.91",
3
+ "version": "3.9.94",
4
4
  "description": "Simple API http server based on express",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -19,15 +19,16 @@
19
19
  "dependencies": {
20
20
  "@types/express-fileupload": "^1.5.0",
21
21
  "@types/mysql": "^2.15.21",
22
+ "@types/node": "^25.0.3",
22
23
  "@types/pg": "^8.15.5",
23
24
  "@types/ws": "^8.5.9",
24
- "axios": "^1.10.0",
25
25
  "badmfck-signal": "^1.4.9",
26
26
  "cors": "^2.8.5",
27
27
  "express": "^4.21.2",
28
28
  "express-fileupload": "^1.5.2",
29
29
  "mysql2": "^3.14.2",
30
30
  "pg": "^8.16.3",
31
+ "undici": "^7.18.0",
31
32
  "ws": "^8.18.3"
32
33
  },
33
34
  "devDependencies": {