badmfck-api-server 2.4.2 → 2.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/apiServer/BaseEndpoint.d.ts +1 -0
- package/dist/apiServer/ClusterService.d.ts +16 -0
- package/dist/apiServer/ClusterService.js +50 -0
- package/dist/apiServer/DocumentService.d.ts +42 -0
- package/dist/apiServer/DocumentService.js +182 -0
- package/dist/apiServer/HUBService.d.ts +29 -0
- package/dist/apiServer/HUBService.js +69 -0
- package/dist/apiServer/cluster/HUBConnection.d.ts +39 -0
- package/dist/apiServer/cluster/HUBConnection.js +230 -0
- package/dist/apiServer/structures/DefaultErrors.d.ts +4 -0
- package/dist/apiServer/structures/DefaultErrors.js +4 -0
- package/package.json +6 -5
@@ -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;
|
@@ -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.
|
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.
|
24
|
-
"badmfck-signal": "^1.4.
|
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.
|
27
|
-
"express-fileupload": "^1.5.
|
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
|
},
|