badmfck-api-server 2.4.1 → 2.4.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -83,7 +83,7 @@ async function Initializer(services) {
83
83
  exports.Initializer = Initializer;
84
84
  class APIService extends BaseService_1.BaseService {
85
85
  static nextLogID = 0;
86
- version = "2.4.1";
86
+ version = "2.4.2";
87
87
  options;
88
88
  monitor;
89
89
  monitorIndexFile;
@@ -12,6 +12,7 @@ export interface IEndpointHandler {
12
12
  ignoreInterceptor?: boolean;
13
13
  independed?: boolean;
14
14
  allowInterceptorError?: boolean;
15
+ httpMethod?: "GET" | "POST" | "PUT" | "DELETE";
15
16
  }
16
17
  export declare class BaseEndpoint implements IBaseEndpoint {
17
18
  private inializedEndpointNames;
@@ -0,0 +1,16 @@
1
+ import { BaseService } from "./BaseService";
2
+ import { HUBConnection } from "./cluster/HUBConnection";
3
+ export interface ClusterServiceOptions {
4
+ id: string;
5
+ privateKey: string;
6
+ hubURL: string;
7
+ hubPublicKey: string;
8
+ nodes: string[];
9
+ }
10
+ export declare class ClusterService extends BaseService {
11
+ options: ClusterServiceOptions;
12
+ ws: HUBConnection | null;
13
+ constructor(opt: ClusterServiceOptions);
14
+ init(): Promise<void>;
15
+ connectToHUB(): Promise<void>;
16
+ }
@@ -0,0 +1,50 @@
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.ClusterService = void 0;
7
+ const ws_1 = __importDefault(require("ws"));
8
+ const BaseService_1 = require("./BaseService");
9
+ const LogService_1 = require("./LogService");
10
+ const HUBConnection_1 = require("./cluster/HUBConnection");
11
+ class ClusterService extends BaseService_1.BaseService {
12
+ options;
13
+ ws = null;
14
+ constructor(opt) {
15
+ super("ClusterService");
16
+ this.options = opt;
17
+ if (!opt.nodes.length)
18
+ throw new Error("ClusterService: No nodes provided");
19
+ }
20
+ async init() {
21
+ (0, LogService_1.logInfo)("ClusterService: initialized");
22
+ this.connectToHUB();
23
+ }
24
+ async connectToHUB() {
25
+ (0, LogService_1.logInfo)("ClusterService: connecting to HUB");
26
+ if (this.ws)
27
+ this.ws.close();
28
+ this.ws = new HUBConnection_1.HUBConnection({
29
+ ws: new ws_1.default(this.options.hubURL),
30
+ privateKey: this.options.privateKey,
31
+ hubID: this.options.id,
32
+ onAuthorized: () => {
33
+ this.ws?.send({
34
+ method: "auth",
35
+ data: {
36
+ id: this.options.id,
37
+ publicKey: this.options.hubPublicKey
38
+ },
39
+ callbackID: 0
40
+ });
41
+ },
42
+ onClose: () => {
43
+ console.log("ClusterService: connection to HUB closed");
44
+ this.connectToHUB();
45
+ },
46
+ handlers: []
47
+ });
48
+ }
49
+ }
50
+ exports.ClusterService = ClusterService;
@@ -0,0 +1,42 @@
1
+ declare type DocumentGeneratorConfig = {
2
+ sourcePath: string;
3
+ apiEndpoint: string;
4
+ savePath: string;
5
+ savePathFileName: string;
6
+ };
7
+ declare type DocumentGeneratorDocumentation = {
8
+ [baseEndpointName: string]: {
9
+ [endPointName: string]: DocumentGeneratorDocumentationEndpoint;
10
+ };
11
+ };
12
+ declare type DocumentGeneratorDocumentationEndpoint = {
13
+ handler: string;
14
+ fullPath: string;
15
+ ignoreInterceptor?: string;
16
+ description?: string;
17
+ parameters: DocumentGeneratorDocumentationEndpointParameter[];
18
+ returns?: Omit<DocumentGeneratorDocumentationEndpointParameter, "optional">;
19
+ example?: string[];
20
+ throws?: Omit<DocumentGeneratorDocumentationEndpointParameter, "optional">[];
21
+ };
22
+ declare type DocumentGeneratorDocumentationEndpointParameter = {
23
+ name: string;
24
+ type: string;
25
+ description: string;
26
+ optional?: boolean;
27
+ };
28
+ export declare class DocumentGenerator {
29
+ private documentation;
30
+ private config;
31
+ constructor(config: DocumentGeneratorConfig);
32
+ private getFiles;
33
+ private getFileContent;
34
+ private generateDocumentation;
35
+ private getCommentsByEndpoint;
36
+ private parseEndPointComments;
37
+ private tagToEndpoint;
38
+ private tagModifications;
39
+ private saveDocumentation;
40
+ getDocumentation(): DocumentGeneratorDocumentation;
41
+ }
42
+ export {};
@@ -0,0 +1,182 @@
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
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.DocumentGenerator = void 0;
27
+ const fs = __importStar(require("fs"));
28
+ const path = __importStar(require("path"));
29
+ const comment_parser_1 = require("comment-parser");
30
+ class DocumentGenerator {
31
+ documentation = {};
32
+ config;
33
+ constructor(config) {
34
+ this.config = config;
35
+ this.generateDocumentation();
36
+ }
37
+ getFiles() {
38
+ return fs.readdirSync(this.config.sourcePath);
39
+ }
40
+ getFileContent(file) {
41
+ return fs.readFileSync(path.resolve(this.config.sourcePath, file), "utf-8");
42
+ }
43
+ generateDocumentation = () => {
44
+ for (const file of this.getFiles()) {
45
+ const fileContent = this.getFileContent(file);
46
+ const baseEndpoint = fileContent.match(/super\(['"](.*?)['"]\)/);
47
+ if (!baseEndpoint) {
48
+ console.error(`No base endpoint found in ${file}`);
49
+ continue;
50
+ }
51
+ const nodeEndpoint = this.config.apiEndpoint + "/" + baseEndpoint[1];
52
+ const regex = /{\s*endpoint:\s*"(.*?)",\s*handler:\s*this\.(.*?)(?:,\s*ignoreInterceptor:\s*(true|false))?\s*}/g;
53
+ const endpoints = fileContent.matchAll(regex);
54
+ for (const match of endpoints) {
55
+ const endpointName = match[1];
56
+ const handler = match[2];
57
+ const ignoreInterceptor = match[3];
58
+ if (!this.documentation.hasOwnProperty(nodeEndpoint)) {
59
+ this.documentation[nodeEndpoint] = {};
60
+ }
61
+ const endpoint = {
62
+ handler,
63
+ ignoreInterceptor,
64
+ fullPath: nodeEndpoint + "/" + endpointName,
65
+ description: "",
66
+ parameters: [],
67
+ };
68
+ this.documentation[nodeEndpoint][endpointName] = this.parseEndPointComments(this.getCommentsByEndpoint(fileContent, handler), endpoint);
69
+ }
70
+ }
71
+ this.saveDocumentation();
72
+ console.log("Documentation generated and saved successfully");
73
+ };
74
+ getCommentsByEndpoint(fileContent, handler) {
75
+ let comments = "";
76
+ const regex = new RegExp(`(async )*${handler}(\\s*)\\(`, "g");
77
+ const match = fileContent.match(regex);
78
+ if (match) {
79
+ const indexHandler = fileContent.indexOf(`${match[0]}`);
80
+ if (indexHandler != -1) {
81
+ const indexCommentStart = fileContent.lastIndexOf("/**", indexHandler);
82
+ if (indexCommentStart != -1) {
83
+ const indexCommentEnd = fileContent.indexOf("*/", indexCommentStart);
84
+ if (indexCommentEnd != -1) {
85
+ const commentsSubstring = fileContent.substring(indexCommentStart, indexCommentEnd + 2);
86
+ if (commentsSubstring) {
87
+ const regexToCheckCommentWithMethod = new RegExp(`${commentsSubstring.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}([\\s]*?)(async )*${handler}(\\s*)\\(`, "g");
88
+ const matchToCheckCommentWithMethod = fileContent.match(regexToCheckCommentWithMethod);
89
+ if (matchToCheckCommentWithMethod) {
90
+ comments = commentsSubstring;
91
+ }
92
+ }
93
+ }
94
+ }
95
+ }
96
+ }
97
+ return comments;
98
+ }
99
+ parseEndPointComments(comments, endpoint) {
100
+ if (comments) {
101
+ const parsed = (0, comment_parser_1.parse)(comments, { spacing: "preserve" });
102
+ if (parsed[0]) {
103
+ const firstParsed = parsed[0];
104
+ endpoint.description = this.tagModifications(firstParsed.description);
105
+ firstParsed.tags.forEach((tag) => {
106
+ endpoint = this.tagToEndpoint(tag, endpoint);
107
+ });
108
+ }
109
+ }
110
+ return endpoint;
111
+ }
112
+ tagToEndpoint(tag, endpoint) {
113
+ switch (tag.tag) {
114
+ case "param":
115
+ endpoint.parameters.push({
116
+ name: this.tagModifications(tag.name, "name"),
117
+ type: tag.type,
118
+ description: this.tagModifications(tag.description),
119
+ optional: tag.optional,
120
+ });
121
+ break;
122
+ case "description":
123
+ endpoint.description = this.tagModifications(tag.description);
124
+ break;
125
+ case "example":
126
+ if (!endpoint.example) {
127
+ endpoint.example = [];
128
+ }
129
+ endpoint.example.push(this.tagModifications(tag.description));
130
+ break;
131
+ case "returns":
132
+ endpoint.returns = {
133
+ name: this.tagModifications(tag.name, "name"),
134
+ type: tag.type,
135
+ description: this.tagModifications(tag.description),
136
+ };
137
+ break;
138
+ case "throws":
139
+ if (!endpoint.throws) {
140
+ endpoint.throws = [];
141
+ }
142
+ endpoint.throws.push({
143
+ name: this.tagModifications(tag.name, "name"),
144
+ type: tag.type,
145
+ description: this.tagModifications(tag.description),
146
+ });
147
+ break;
148
+ }
149
+ return endpoint;
150
+ }
151
+ tagModifications(text, field = "description") {
152
+ switch (field) {
153
+ case "description":
154
+ text = text
155
+ .trim()
156
+ .replace(/^(\-.)/, "")
157
+ .trim();
158
+ break;
159
+ case "name":
160
+ if (text === "-") {
161
+ text = "";
162
+ }
163
+ break;
164
+ }
165
+ return text;
166
+ }
167
+ saveDocumentation() {
168
+ const formatForSave = this.documentation;
169
+ const filePath = this.config.savePath + this.config.savePathFileName;
170
+ fs.writeFileSync(filePath, JSON.stringify(formatForSave, null, 2));
171
+ }
172
+ getDocumentation() {
173
+ return this.documentation;
174
+ }
175
+ }
176
+ exports.DocumentGenerator = DocumentGenerator;
177
+ new DocumentGenerator({
178
+ sourcePath: "./src/api",
179
+ apiEndpoint: "api/v1",
180
+ savePath: "./bin/",
181
+ savePathFileName: "doc.json",
182
+ });
@@ -0,0 +1,29 @@
1
+ import { BaseService } from "./BaseService";
2
+ import { HUBConnection } from "./cluster/HUBConnection";
3
+ import Signal, { Req } from "badmfck-signal";
4
+ import { IError } from "./structures/Interfaces";
5
+ export interface HUBServiceOptions {
6
+ url: string;
7
+ privateKey: string;
8
+ hubID: string;
9
+ }
10
+ export interface IClusterNode {
11
+ clusterID: string;
12
+ url: string;
13
+ publicKey: string;
14
+ ws: HUBConnection[];
15
+ }
16
+ export declare const REQ_CLUSTER_NODE: Req<string, IError | {
17
+ url: string;
18
+ publicKey: string;
19
+ }>;
20
+ export declare class HUBService extends BaseService {
21
+ options: HUBServiceOptions;
22
+ nodes: Map<string, IClusterNode>;
23
+ handlers: (Signal<any> | Req<any, any>)[];
24
+ constructor(options: HUBServiceOptions);
25
+ init(): Promise<void>;
26
+ applicationReady(): Promise<void>;
27
+ onConnectionAuthorized(conn: HUBConnection): void;
28
+ onConnectionClosed(conn: HUBConnection): void;
29
+ }
@@ -0,0 +1,69 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.HUBService = exports.REQ_CLUSTER_NODE = void 0;
4
+ const ws_1 = require("ws");
5
+ const APIService_1 = require("./APIService");
6
+ const BaseService_1 = require("./BaseService");
7
+ const HUBConnection_1 = require("./cluster/HUBConnection");
8
+ const LogService_1 = require("./LogService");
9
+ const badmfck_signal_1 = require("badmfck-signal");
10
+ exports.REQ_CLUSTER_NODE = new badmfck_signal_1.Req(undefined, "REQ_CLUSTER_NODE");
11
+ class HUBService extends BaseService_1.BaseService {
12
+ options;
13
+ nodes = new Map();
14
+ handlers = [];
15
+ constructor(options) {
16
+ super("HUBService");
17
+ this.options = options;
18
+ }
19
+ async init() {
20
+ super.init();
21
+ }
22
+ async applicationReady() {
23
+ const server = await APIService_1.REQ_HTTP_SERVER.request();
24
+ const wss = new ws_1.WebSocketServer({
25
+ server: server.http,
26
+ path: "/hub"
27
+ });
28
+ wss.on("connection", (ws) => {
29
+ const conn = new HUBConnection_1.HUBConnection({
30
+ ws: ws,
31
+ privateKey: this.options.privateKey,
32
+ hubID: this.options.hubID,
33
+ onAuthorized: () => this.onConnectionAuthorized(conn),
34
+ onClose: () => this.onConnectionClosed(conn),
35
+ handlers: this.handlers
36
+ });
37
+ });
38
+ wss.on("error", (err) => {
39
+ console.error("HUBService: error: ", err);
40
+ });
41
+ }
42
+ onConnectionAuthorized(conn) {
43
+ (0, LogService_1.logInfo)("HUBService: authorized connection");
44
+ let clusterNode = this.nodes.get(conn.clientID);
45
+ if (!clusterNode) {
46
+ (0, LogService_1.logInfo)("HUBService: new cluster node connected");
47
+ clusterNode = {
48
+ clusterID: conn.clientID,
49
+ url: conn.url,
50
+ publicKey: conn.publicKey,
51
+ ws: []
52
+ };
53
+ this.nodes.set(conn.clientID, clusterNode);
54
+ }
55
+ clusterNode.ws.push(conn);
56
+ }
57
+ onConnectionClosed(conn) {
58
+ (0, LogService_1.logInfo)("HUBService: connection closed");
59
+ const clusterNode = this.nodes.get(conn.clientID);
60
+ if (clusterNode) {
61
+ clusterNode.ws = clusterNode.ws.filter(x => x !== conn);
62
+ if (!clusterNode.ws.length) {
63
+ (0, LogService_1.logInfo)("HUBService: cluster node disconnected");
64
+ this.nodes.delete(conn.clientID);
65
+ }
66
+ }
67
+ }
68
+ }
69
+ exports.HUBService = HUBService;
@@ -137,7 +137,7 @@ class MysqlService extends BaseService_1.BaseService {
137
137
  promises.push(this.execute(query, i.rollbackQuery ?? null, i.transactionID ?? 0));
138
138
  }
139
139
  if (this.debug)
140
- console.log("Execute queries: ", data);
140
+ console.log("Execute queries: ", data.length);
141
141
  return await Promise.all(promises);
142
142
  };
143
143
  exports.REQ_MYSQL_TBEGIN.listener = async () => {
@@ -0,0 +1,39 @@
1
+ import { WebSocket } from "ws";
2
+ import { IError } from "../structures/Interfaces";
3
+ import Signal, { Req } from "badmfck-signal";
4
+ export interface IClusterPacket {
5
+ method: string;
6
+ data?: any | null;
7
+ error?: IError;
8
+ callbackID: number;
9
+ }
10
+ export declare class HUBConnection {
11
+ static callbackID: number;
12
+ callbacks: Map<number, {
13
+ time: number;
14
+ callback: (packet: IClusterPacket) => void;
15
+ }>;
16
+ authorized: boolean;
17
+ ws: WebSocket;
18
+ privateKey: string;
19
+ publicKey: string | null;
20
+ hubID: string;
21
+ clientID: string | null;
22
+ url: string | null;
23
+ onAuthorized: () => void;
24
+ intervalID: number;
25
+ constructor(options: {
26
+ ws: WebSocket;
27
+ privateKey: string;
28
+ hubID: string;
29
+ onAuthorized: () => void;
30
+ onClose: () => void;
31
+ handlers: (Req<any, any> | Signal<any>)[];
32
+ authorized?: boolean;
33
+ maxTimeout?: number;
34
+ });
35
+ authorize(data: any): Promise<boolean | IError>;
36
+ sendCallback(packet: IClusterPacket, data: IError | any): void;
37
+ send(packet: IClusterPacket): Promise<IClusterPacket | IError | null>;
38
+ close(): void;
39
+ }
@@ -0,0 +1,230 @@
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
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.HUBConnection = void 0;
27
+ const ws_1 = require("ws");
28
+ const LogService_1 = require("../LogService");
29
+ const DefaultErrors_1 = __importStar(require("../structures/DefaultErrors"));
30
+ const crypto_1 = require("crypto");
31
+ class HUBConnection {
32
+ static callbackID = 1;
33
+ callbacks = new Map();
34
+ authorized = false;
35
+ ws;
36
+ privateKey;
37
+ publicKey;
38
+ hubID;
39
+ clientID = null;
40
+ url = null;
41
+ onAuthorized;
42
+ intervalID = 0;
43
+ constructor(options) {
44
+ (0, LogService_1.logInfo)("HUBConnection: new connection");
45
+ this.privateKey = options.privateKey;
46
+ this.publicKey = null;
47
+ this.hubID = options.hubID;
48
+ this.ws = options.ws;
49
+ this.onAuthorized = options.onAuthorized;
50
+ if (options.authorized)
51
+ this.authorized = true;
52
+ this.ws.on("close", () => {
53
+ if (!this.authorized)
54
+ return;
55
+ (0, LogService_1.logInfo)("HUBConnection: connection closed");
56
+ if (options.onClose)
57
+ options.onClose();
58
+ });
59
+ this.ws.on("error", (e) => {
60
+ (0, LogService_1.logError)("HUBConnection: connection error", e);
61
+ });
62
+ this.ws.on("open", () => {
63
+ (0, LogService_1.logInfo)("HUBConnection: connection opened");
64
+ if (options.authorized) {
65
+ if (this.onAuthorized)
66
+ this.onAuthorized();
67
+ }
68
+ });
69
+ this.ws.on("message", async (msg, isBinary) => {
70
+ if (!this.authorized && isBinary)
71
+ return;
72
+ let data = null;
73
+ try {
74
+ data = JSON.parse(msg.toString());
75
+ }
76
+ catch (e) { }
77
+ if (!data || !data.method || !data.callbackID) {
78
+ (0, LogService_1.logError)("HUBConnection: invalid packet structure", data);
79
+ return;
80
+ }
81
+ if (typeof data.method !== "string" || typeof data.callbackID !== "number") {
82
+ (0, LogService_1.logError)("HUBConnection: invalid packet structure", data);
83
+ return;
84
+ }
85
+ if (!this.authorized && data.method === "auth") {
86
+ const result = await this.authorize(data.data);
87
+ if (DefaultErrors_1.ErrorUtils.isError(result)) {
88
+ (0, LogService_1.logError)("HUBConnection: authorization failed", result);
89
+ this.sendCallback(data, result);
90
+ return;
91
+ }
92
+ this.authorized = true;
93
+ if (this.onAuthorized)
94
+ this.onAuthorized();
95
+ this.sendCallback(data, result);
96
+ return;
97
+ }
98
+ if (!this.authorized) {
99
+ (0, LogService_1.logError)("HUBConnection: not authorized");
100
+ this.sendCallback(data, DefaultErrors_1.default.UNAUTHORIZED);
101
+ return;
102
+ }
103
+ if (typeof data.data !== "string")
104
+ return this.sendCallback(data, { ...DefaultErrors_1.default.BAD_REQUEST, details: "invalid data, must be a string" });
105
+ let packetData = null;
106
+ try {
107
+ const encryptedPacketData = (0, crypto_1.privateDecrypt)(this.privateKey, data.data).toString("utf8");
108
+ packetData = JSON.parse(encryptedPacketData);
109
+ }
110
+ catch (e) {
111
+ return this.sendCallback(data, { ...DefaultErrors_1.default.BAD_REQUEST, details: "invalid data type, must be a json" });
112
+ }
113
+ if (data.method === "callback") {
114
+ if (!this.callbacks.has(data.callbackID)) {
115
+ (0, LogService_1.logError)("HUBConnection: callback not found", data);
116
+ return;
117
+ }
118
+ const cb = this.callbacks.get(data.callbackID);
119
+ if (cb) {
120
+ cb.callback({
121
+ method: "callback",
122
+ data: packetData,
123
+ callbackID: data.callbackID,
124
+ error: data.error
125
+ });
126
+ this.callbacks.delete(data.callbackID);
127
+ }
128
+ return;
129
+ }
130
+ ;
131
+ for (let i of options.handlers) {
132
+ if (i.name === data.method) {
133
+ if (i.type === "signal") {
134
+ i.invoke(packetData);
135
+ if (data.callbackID > 0)
136
+ this.sendCallback(data, {});
137
+ return;
138
+ }
139
+ else if (i.type === "request") {
140
+ const result = await i.request(packetData);
141
+ this.sendCallback(data, result);
142
+ return;
143
+ }
144
+ }
145
+ }
146
+ this.sendCallback(data, { ...DefaultErrors_1.default.NOT_IMPLEMENTED, details: "method not found: " + data.method });
147
+ });
148
+ if (!options.authorized) {
149
+ setTimeout(() => {
150
+ if (!this.authorized) {
151
+ this.close();
152
+ }
153
+ }, 2000);
154
+ }
155
+ setInterval(() => {
156
+ const now = +new Date();
157
+ for (let [key, value] of this.callbacks) {
158
+ if (now - value.time > (options.maxTimeout ?? 1000 * 60 * 15)) {
159
+ value.callback({ method: "callback", data: null, callbackID: key, error: { ...DefaultErrors_1.default.TIMEOUT } });
160
+ this.callbacks.delete(key);
161
+ }
162
+ }
163
+ }, options.maxTimeout || 1000 * 60 * 15);
164
+ }
165
+ async authorize(data) {
166
+ if (typeof data !== "object" || !data || !data.signature || !data.publicKey || !data.clientID || !data.url)
167
+ return { ...DefaultErrors_1.default.BAD_REQUEST, details: "invalid data, expected {signature:encrypted hub key,publicKey:encrypted public key, clientID:clientID,url: service url}" };
168
+ let hubKey = null;
169
+ try {
170
+ hubKey = (0, crypto_1.privateDecrypt)(this.privateKey, data.signature).toString("utf8");
171
+ }
172
+ catch (e) {
173
+ return { ...DefaultErrors_1.default.UNAUTHORIZED, details: "cant check signature" };
174
+ }
175
+ if (hubKey !== this.hubID)
176
+ return { ...DefaultErrors_1.default.UNAUTHORIZED, details: "signature is invalid" };
177
+ try {
178
+ this.publicKey = (0, crypto_1.privateDecrypt)(this.privateKey, data.publicKey).toString("utf8");
179
+ }
180
+ catch (e) {
181
+ return { ...DefaultErrors_1.default.UNAUTHORIZED, details: "cant obtain public key" };
182
+ }
183
+ if (!this.publicKey)
184
+ return { ...DefaultErrors_1.default.UNAUTHORIZED, details: "wrong public key" };
185
+ return true;
186
+ }
187
+ sendCallback(packet, data) {
188
+ if (packet.callbackID === -1)
189
+ return;
190
+ if (this.ws && this.ws.readyState === ws_1.WebSocket.OPEN) {
191
+ this.ws.send(JSON.stringify({
192
+ method: "callback",
193
+ error: DefaultErrors_1.ErrorUtils.isError(data) ? data : null,
194
+ data: DefaultErrors_1.ErrorUtils.isError(data) ? null : data,
195
+ callbackID: packet.callbackID
196
+ }));
197
+ }
198
+ else {
199
+ (0, LogService_1.logError)("HUBConnection: ws not connected, cant send callback", packet);
200
+ }
201
+ }
202
+ async send(packet) {
203
+ if (!this.ws || this.ws.readyState !== ws_1.WebSocket.OPEN) {
204
+ (0, LogService_1.logError)("HUBConnection: ws not connected, cant send packet", packet);
205
+ return { ...DefaultErrors_1.default.SERVICE_NOT_WORKING, details: "ws not connected" };
206
+ }
207
+ let cb = null;
208
+ const promise = new Promise((resolve) => cb = resolve);
209
+ if (packet.callbackID > 0) {
210
+ const callbackID = HUBConnection.callbackID++;
211
+ this.callbacks.set(callbackID, { time: +new Date(), callback: cb });
212
+ packet.callbackID = callbackID;
213
+ }
214
+ this.ws.send(JSON.stringify(packet));
215
+ if (packet.callbackID === 0)
216
+ return null;
217
+ return promise;
218
+ }
219
+ close() {
220
+ this.callbacks.clear();
221
+ if (this.ws) {
222
+ try {
223
+ this.ws.removeAllListeners();
224
+ this.ws.close();
225
+ }
226
+ catch (e) { }
227
+ }
228
+ }
229
+ }
230
+ exports.HUBConnection = HUBConnection;
@@ -6,6 +6,10 @@ declare class DefaultErrors {
6
6
  static FILE_TOO_LARGE: IError;
7
7
  static CANT_SEND_FILE: IError;
8
8
  static FILE_NOT_EXISTS: IError;
9
+ static UNAUTHORIZED: IError;
10
+ static BAD_REQUEST: IError;
11
+ static SERVICE_NOT_WORKING: IError;
12
+ static TIMEOUT: IError;
9
13
  }
10
14
  export declare class ErrorUtils {
11
15
  static isError(obj: any): boolean;
@@ -9,6 +9,10 @@ class DefaultErrors {
9
9
  static FILE_TOO_LARGE = { code: 4, message: "File is too large" };
10
10
  static CANT_SEND_FILE = { code: 5, message: "Can't send file" };
11
11
  static FILE_NOT_EXISTS = { code: 5, message: "File not exists" };
12
+ static UNAUTHORIZED = { code: 6, message: "Unauthorized" };
13
+ static BAD_REQUEST = { code: 7, message: "Bad request", httpStatus: 400 };
14
+ static SERVICE_NOT_WORKING = { code: 8, message: "Service not working" };
15
+ static TIMEOUT = { code: 9, message: "Timeout" };
12
16
  }
13
17
  class ErrorUtils {
14
18
  static isError(obj) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "badmfck-api-server",
3
- "version": "2.4.1",
3
+ "version": "2.4.3",
4
4
  "description": "Simple API http server based on express",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -20,11 +20,12 @@
20
20
  "@types/express-fileupload": "^1.5.0",
21
21
  "@types/mysql": "^2.15.21",
22
22
  "@types/ws": "^8.5.9",
23
- "axios": "^1.7.2",
24
- "badmfck-signal": "^1.4.5",
23
+ "axios": "^1.7.7",
24
+ "badmfck-signal": "^1.4.7",
25
+ "comment-parser": "^1.4.1",
25
26
  "cors": "^2.8.5",
26
- "express": "^4.19.2",
27
- "express-fileupload": "^1.5.0",
27
+ "express": "^4.21.1",
28
+ "express-fileupload": "^1.5.1",
28
29
  "mysql2": "^3.11.3",
29
30
  "ws": "^8.18.0"
30
31
  },