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.
- package/dist/apiServer/APIService.d.ts +2 -0
- package/dist/apiServer/APIService.js +7 -1
- package/dist/apiServer/DBService.d.ts +1 -1
- package/dist/apiServer/db/MysqlAdapter.js +4 -0
- package/dist/apiServer/external/MicroserviceClient.js +44 -24
- package/dist/apiServer/external/MicroserviceHost.d.ts +8 -0
- package/dist/apiServer/external/MicroserviceHost.js +61 -2
- package/dist/apiServer/structures/DefaultErrors.d.ts +2 -0
- package/dist/apiServer/structures/DefaultErrors.js +2 -0
- package/package.json +1 -1
|
@@ -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.
|
|
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
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
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 =
|
|
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
|
-
|
|
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) {
|