badmfck-api-server 4.0.76 → 4.0.79

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.
@@ -31,6 +31,8 @@ export interface APIServiceOptions<TInterceptor = unknown> {
31
31
  corsHostWhiteList: string[];
32
32
  projectName: string;
33
33
  endpoints: IBaseEndpoint[];
34
+ keepAliveTimeout?: number;
35
+ headersTimeout?: number;
34
36
  jsonLimit: string;
35
37
  isProductionEnvironment: boolean;
36
38
  interceptor?: IInterceptor<TInterceptor>;
@@ -66,6 +66,8 @@ function defaultOptions() {
66
66
  appVersion: undefined,
67
67
  fileTempDir: "/tmp",
68
68
  fileLimit: 10_000_000,
69
+ keepAliveTimeout: 25 * 1000,
70
+ headersTimeout: 26 * 1000,
69
71
  };
70
72
  }
71
73
  const getDefaultOptions = (overrides) => {
@@ -95,7 +97,7 @@ async function Initializer(services) {
95
97
  }
96
98
  exports.Initializer = Initializer;
97
99
  class APIService extends BaseService_1.BaseService {
98
- version = "4.0.76";
100
+ version = "4.0.79";
99
101
  options;
100
102
  monitor = null;
101
103
  started = new Date();
@@ -159,6 +161,10 @@ class APIService extends BaseService_1.BaseService {
159
161
  };
160
162
  const app = (0, express_1.default)();
161
163
  const server = http_1.default.createServer(app);
164
+ if (this.options.keepAliveTimeout !== undefined)
165
+ server.keepAliveTimeout = this.options.keepAliveTimeout;
166
+ if (this.options.headersTimeout !== undefined)
167
+ server.headersTimeout = this.options.headersTimeout;
162
168
  exports.REQ_HTTP_SERVER.listener = async (_) => { return { express: app, http: server }; };
163
169
  if (this.options.isProductionEnvironment)
164
170
  app.set("env", 'production');
@@ -12,7 +12,7 @@ export interface IDBQueryField {
12
12
  fields?: Record<string, number | boolean | string | null>;
13
13
  }
14
14
  export interface IDBQueryFields {
15
- [key: string]: string | number | boolean | IDBQueryField | null | undefined;
15
+ [key: string]: string | number | boolean | Date | IDBQueryField | null | undefined;
16
16
  }
17
17
  export interface IDBQuery {
18
18
  dbid?: string;
@@ -628,6 +628,10 @@ class MysqlAdapter {
628
628
  }
629
629
  if (typeof value === "boolean")
630
630
  value = value ? 1 : 0;
631
+ if (typeof value === "object" && value instanceof Date) {
632
+ const utcString = value.toISOString().slice(0, 19).replace('T', ' ');
633
+ value = promise_1.default.escape(utcString);
634
+ }
631
635
  if (field.fields && typeof field.fields === "object" && value && typeof value === "string" && value.indexOf("@") !== -1) {
632
636
  for (let key in field.fields) {
633
637
  if (!field.fields[key])
@@ -9,6 +9,7 @@ const BaseService_1 = require("../BaseService");
9
9
  const DefaultErrors_1 = __importDefault(require("../structures/DefaultErrors"));
10
10
  const Http_1 = require("../http/Http");
11
11
  const crypto_1 = __importDefault(require("crypto"));
12
+ const TimeframeService_1 = require("../TimeframeService");
12
13
  exports.REQ_MICROSERVICE_CALL = new badmfck_signal_1.Req(undefined, "REQ_MICROSERVICE_CALL");
13
14
  class MicroserviceClient extends BaseService_1.BaseService {
14
15
  static #clients = new Map();
@@ -37,30 +38,49 @@ class MicroserviceClient extends BaseService_1.BaseService {
37
38
  const url = this.options.host + "/__ms_host_control/receiver";
38
39
  const ts = Date.now().toString();
39
40
  const nonce = crypto_1.default.randomBytes(12).toString("hex");
40
- const headers = {
41
- "Content-Type": "application/json",
42
- "x-microservice-id": this.options.id,
43
- "x-microservice-ts": ts,
44
- "x-microservice-nonce": nonce
45
- };
46
- const rawSecret = Buffer.from(this.options.securityKey, "base64");
47
- const key = crypto_1.default.createHash("sha256").update(rawSecret).digest();
48
- const hash = crypto_1.default.createHash("sha256").update(nonce + ts + this.options.securityKey).digest();
49
- const iv = hash.subarray(0, 12);
50
- const aad = Buffer.from(`${ts}.${nonce}.${this.options.id}`, "utf8");
51
- const data = Buffer.from(JSON.stringify(req), "utf8");
52
- const cipher = crypto_1.default.createCipheriv("aes-256-gcm", key, iv);
53
- cipher.setAAD(aad);
54
- const encrypted = Buffer.concat([cipher.update(data), cipher.final()]);
55
- const tag = cipher.getAuthTag();
56
- const resp = await Http_1.Http.post(url, {
57
- tag: tag.toString("base64"),
58
- enc: encrypted.toString("base64")
59
- }, { headers });
60
- if (!resp.ok)
61
- return { ...DefaultErrors_1.default.BAD_REQUEST, details: resp.details };
62
- const body = resp.data;
63
- return (body && typeof body === "object" && "error" in body) ? body.error : (body.data ?? null);
41
+ const maxAttempts = 5;
42
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
43
+ const rawSecret = Buffer.from(this.options.securityKey, "base64");
44
+ const key = crypto_1.default.createHash("sha256").update(rawSecret).digest();
45
+ const hash = crypto_1.default.createHash("sha256").update(nonce + ts + this.options.securityKey).digest();
46
+ const iv = hash.subarray(0, 12);
47
+ const aad = Buffer.from(`${ts}.${nonce}.${this.options.id}`, "utf8");
48
+ const data = Buffer.from(JSON.stringify(req), "utf8");
49
+ const cipher = crypto_1.default.createCipheriv("aes-256-gcm", key, iv);
50
+ cipher.setAAD(aad);
51
+ const encrypted = Buffer.concat([cipher.update(data), cipher.final()]);
52
+ const tag = cipher.getAuthTag();
53
+ const headers = {
54
+ "Content-Type": "application/json",
55
+ "x-microservice-id": this.options.id,
56
+ "x-microservice-ts": ts,
57
+ "x-microservice-nonce": nonce
58
+ };
59
+ const resp = await Http_1.Http.post(url, {
60
+ tag: tag.toString("base64"),
61
+ enc: encrypted.toString("base64")
62
+ }, { headers });
63
+ if (!resp.ok && resp.details?.network) {
64
+ if (attempt < maxAttempts) {
65
+ TimeframeService_1.TimeframeService.wait(500);
66
+ continue;
67
+ }
68
+ return { ...DefaultErrors_1.default.EXTERNAL_SERVICE_ERROR, details: "Network connection lost after multiple retries" };
69
+ }
70
+ if (!resp.ok)
71
+ return { ...DefaultErrors_1.default.BAD_REQUEST, details: resp.details };
72
+ const body = resp.data;
73
+ const isAccepted = body?.error?.code === DefaultErrors_1.default.ACCEPTED.code || body?.code === DefaultErrors_1.default.ACCEPTED.code;
74
+ if (isAccepted) {
75
+ if (attempt < maxAttempts) {
76
+ TimeframeService_1.TimeframeService.wait(500);
77
+ continue;
78
+ }
79
+ return { ...DefaultErrors_1.default.EXTERNAL_SERVICE_ERROR, details: "Microservice processing timeout" };
80
+ }
81
+ return (body && typeof body === "object" && "error" in body) ? body.error : (body.data ?? null);
82
+ }
83
+ return { ...DefaultErrors_1.default.EXTERNAL_SERVICE_ERROR, details: "Unknown polling error" };
64
84
  }
65
85
  }
66
86
  exports.MicroserviceClient = MicroserviceClient;
@@ -7,11 +7,19 @@ export interface IMicroserviceHostOptions {
7
7
  password: string;
8
8
  requests: Req<any, any>[];
9
9
  }
10
+ type TJobStatus = "processing" | "completed";
11
+ type TJob = {
12
+ status: TJobStatus;
13
+ result?: any | IError;
14
+ expires: number;
15
+ };
10
16
  export declare class MicroserviceHost extends BaseService {
11
17
  #private;
12
18
  options: IMicroserviceHostOptions;
19
+ jobs: Map<string, TJob>;
13
20
  constructor(opt: IMicroserviceHostOptions);
14
21
  init(): Promise<void>;
15
22
  static getMicroservice(id: string): MicroserviceHost | null;
16
23
  handleIncomingRequest(req: HTTPRequestVO): Promise<any | IError>;
17
24
  }
25
+ export {};
@@ -1,16 +1,40 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
2
25
  var __importDefault = (this && this.__importDefault) || function (mod) {
3
26
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
27
  };
5
28
  Object.defineProperty(exports, "__esModule", { value: true });
6
29
  exports.MicroserviceHost = void 0;
7
30
  const BaseService_1 = require("../BaseService");
8
- const DefaultErrors_1 = __importDefault(require("../structures/DefaultErrors"));
31
+ const DefaultErrors_1 = __importStar(require("../structures/DefaultErrors"));
9
32
  const crypto_1 = __importDefault(require("crypto"));
10
33
  class MicroserviceHost extends BaseService_1.BaseService {
11
34
  static #calls = new Set();
12
35
  static #instances = new Map();
13
36
  options;
37
+ jobs = new Map();
14
38
  constructor(opt) {
15
39
  super("Microservice-host-" + opt.id);
16
40
  this.options = opt;
@@ -18,6 +42,13 @@ class MicroserviceHost extends BaseService_1.BaseService {
18
42
  }
19
43
  async init() {
20
44
  super.init();
45
+ setInterval(() => {
46
+ const now = Date.now();
47
+ for (const [key, job] of this.jobs) {
48
+ if (job.expires < now)
49
+ this.jobs.delete(key);
50
+ }
51
+ }, 1000 * 60 * 5);
21
52
  }
22
53
  static getMicroservice(id) {
23
54
  return MicroserviceHost.#instances.get(id) || null;
@@ -27,6 +58,18 @@ class MicroserviceHost extends BaseService_1.BaseService {
27
58
  const ts = req.headers["x-microservice-ts"];
28
59
  if (!nonce || !ts)
29
60
  return { ...DefaultErrors_1.default.BAD_REQUEST, details: "missing headers" };
61
+ const jobKey = `${nonce}|${ts}`;
62
+ const job = this.jobs.get(jobKey);
63
+ if (job) {
64
+ if (job.expires < Date.now()) {
65
+ this.jobs.delete(jobKey);
66
+ }
67
+ else {
68
+ if (job.status === "processing")
69
+ return { ...DefaultErrors_1.default.ACCEPTED, details: "request is being processed" };
70
+ return job.result;
71
+ }
72
+ }
30
73
  const requestTime = parseInt(ts);
31
74
  const now = Date.now();
32
75
  if (isNaN(requestTime) || Math.abs(now - requestTime) > 1000 * 60 * 2) {
@@ -61,7 +104,23 @@ class MicroserviceHost extends BaseService_1.BaseService {
61
104
  const reqCall = this.options.requests.find(r => r.name === request?.requestName);
62
105
  if (!reqCall)
63
106
  return { ...DefaultErrors_1.default.BAD_REQUEST, details: "method not found" };
64
- return await reqCall.request(request.requestData);
107
+ const job = {
108
+ status: "processing",
109
+ expires: Date.now() + 1000 * 60 * 5
110
+ };
111
+ this.jobs.set(jobKey, job);
112
+ try {
113
+ const result = await reqCall.request(request.requestData);
114
+ job.status = "completed";
115
+ job.result = result;
116
+ return result;
117
+ }
118
+ catch (e) {
119
+ const err = DefaultErrors_1.ErrorUtils.isError(e) ? e : { ...DefaultErrors_1.default.INTERNAL_ERROR, details: "unknown error" };
120
+ job.status = "completed";
121
+ job.result = err;
122
+ return job.result;
123
+ }
65
124
  }
66
125
  catch (e) {
67
126
  return { ...DefaultErrors_1.default.BAD_REQUEST, details: "decryption error" };
@@ -25,6 +25,8 @@ declare class DefaultErrors {
25
25
  static WRONG_DATA_TYPE: IError;
26
26
  static UPROCESSIABLE_CONTENT: IError;
27
27
  static WRONG_PARAMS: IError;
28
+ static ACCEPTED: IError;
29
+ static INTERNAL_ERROR: IError;
28
30
  }
29
31
  export declare class ErrorUtils {
30
32
  static isError(obj: any): obj is IError;
@@ -28,6 +28,8 @@ class DefaultErrors {
28
28
  static WRONG_DATA_TYPE = { code: 23, message: "Wrong data type", httpStatus: 422 };
29
29
  static UPROCESSIABLE_CONTENT = { code: 24, message: "Unprocessable content", httpStatus: 422 };
30
30
  static WRONG_PARAMS = { code: 25, message: "Wrong params", httpStatus: 400 };
31
+ static ACCEPTED = { code: 26, message: "Accepted", httpStatus: 202 };
32
+ static INTERNAL_ERROR = { code: 27, message: "Internal error", httpStatus: 500 };
31
33
  }
32
34
  class ErrorUtils {
33
35
  static isError(obj) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "badmfck-api-server",
3
- "version": "4.0.76",
3
+ "version": "4.0.79",
4
4
  "description": "Simple API http server based on express",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",