badmfck-api-server 1.0.1

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.
Binary file
@@ -0,0 +1,39 @@
1
+ import { Response } from 'express';
2
+ import { BaseService, IBaseService } from './BaseService';
3
+ import { IBaseEndpoint } from './BaseEndpoint';
4
+ import { SyncSignal } from '../signal/Signal';
5
+ import { TransferPacketVO } from './structures/Interfaces';
6
+ export interface APIServiceNetworkLogItem {
7
+ id: number;
8
+ created: number;
9
+ time: number;
10
+ request: {
11
+ [key: string]: any;
12
+ };
13
+ response: {
14
+ [key: string]: any;
15
+ };
16
+ error?: string | null;
17
+ }
18
+ export interface APIServiceOptions {
19
+ port: number;
20
+ baseEndPoint: string;
21
+ corsHostWhiteList: string[];
22
+ endpoints: IBaseEndpoint[];
23
+ jsonLimit: string;
24
+ onNetworkLog?: ((log: APIServiceNetworkLogItem) => void) | null;
25
+ onError?: ((...rest: any[]) => void) | null;
26
+ }
27
+ export declare function getDefaultOptions(): APIServiceOptions;
28
+ export declare const REQ_CREATE_NET_LOG: SyncSignal<void, APIServiceNetworkLogItem>;
29
+ export declare const REQ_HTTP_LOG: SyncSignal<void, APIServiceNetworkLogItem[]>;
30
+ export declare function Initializer(services: IBaseService[]): Promise<void>;
31
+ export declare class APIService extends BaseService {
32
+ private static nextLogID;
33
+ private version;
34
+ private options;
35
+ netLog: APIServiceNetworkLogItem[];
36
+ constructor(options?: APIServiceOptions | null);
37
+ init(): Promise<void>;
38
+ sendResponse(res: Response, data: TransferPacketVO<any>, requestTime: number, endpoint?: string, log?: APIServiceNetworkLogItem | null): void;
39
+ }
@@ -0,0 +1,169 @@
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.APIService = exports.Initializer = exports.REQ_HTTP_LOG = exports.REQ_CREATE_NET_LOG = exports.getDefaultOptions = void 0;
7
+ const express_1 = __importDefault(require("express"));
8
+ const BaseService_1 = require("./BaseService");
9
+ const cors_1 = __importDefault(require("cors"));
10
+ const BaseEndpoint_1 = require("./BaseEndpoint");
11
+ const DefaultErrors_1 = __importDefault(require("./structures/DefaultErrors"));
12
+ const Signal_1 = require("../signal/Signal");
13
+ function getDefaultOptions() {
14
+ return {
15
+ port: 8091,
16
+ baseEndPoint: '/api/',
17
+ corsHostWhiteList: [
18
+ 'http://localhost:3000',
19
+ 'http://localhost:8091'
20
+ ],
21
+ endpoints: [],
22
+ jsonLimit: "10mb",
23
+ onNetworkLog: function (log) {
24
+ console.log(log);
25
+ },
26
+ onError: function (err) {
27
+ console.error.call(this, err);
28
+ }
29
+ };
30
+ }
31
+ exports.getDefaultOptions = getDefaultOptions;
32
+ exports.REQ_CREATE_NET_LOG = new Signal_1.SyncSignal();
33
+ exports.REQ_HTTP_LOG = new Signal_1.SyncSignal();
34
+ async function Initializer(services) {
35
+ for (let i of services) {
36
+ await i.init();
37
+ }
38
+ for (let i of services) {
39
+ i.applicationReady();
40
+ }
41
+ }
42
+ exports.Initializer = Initializer;
43
+ class APIService extends BaseService_1.BaseService {
44
+ static nextLogID = 0;
45
+ version = "1.2.0";
46
+ options;
47
+ netLog = [];
48
+ constructor(options) {
49
+ super('HTTP Service');
50
+ this.options = options ?? getDefaultOptions();
51
+ }
52
+ async init() {
53
+ exports.REQ_HTTP_LOG.listener = (ignore, cb) => { cb(this.netLog); };
54
+ exports.REQ_CREATE_NET_LOG.listener = (ignore, cb) => {
55
+ const log = {
56
+ created: +new Date(),
57
+ time: 0,
58
+ request: {},
59
+ response: {},
60
+ error: null,
61
+ id: APIService.nextLogID++
62
+ };
63
+ this.netLog.push(log);
64
+ if (this.netLog.length > 1000)
65
+ this.netLog.shift();
66
+ cb(log);
67
+ };
68
+ const app = (0, express_1.default)();
69
+ app.use(express_1.default.json({ limit: '10mb' }));
70
+ app.use(express_1.default.urlencoded({ limit: '10mb', extended: true }));
71
+ const corsOptions = {
72
+ origin: (origin, callback) => {
73
+ const originIsWhitelisted = this.options.corsHostWhiteList.includes(origin);
74
+ callback(null, originIsWhitelisted);
75
+ },
76
+ credentials: true
77
+ };
78
+ app.use((0, cors_1.default)(corsOptions));
79
+ BaseEndpoint_1.BaseEndpoint.setEntryPoint(this.options.baseEndPoint);
80
+ for (let i of this.options.endpoints) {
81
+ await i.init();
82
+ for (let j of i.endpoints) {
83
+ const ep = BaseEndpoint_1.BaseEndpoint.getEntryPoint() + j;
84
+ app.all(ep, async (req, res) => {
85
+ const tme = +new Date();
86
+ let log = null;
87
+ if (!i.ignoreHttpLogging) {
88
+ log = await exports.REQ_CREATE_NET_LOG.request();
89
+ log.request = {
90
+ method: req.method,
91
+ data: req.body,
92
+ params: req.params
93
+ };
94
+ }
95
+ if (this.options.onNetworkLog && log)
96
+ this.options.onNetworkLog(log);
97
+ const execute = async () => {
98
+ const httpRequest = {
99
+ raw: req,
100
+ method: req.method,
101
+ data: req.body,
102
+ params: req.params,
103
+ headers: req.headers,
104
+ endpoint: ep
105
+ };
106
+ const result = await i.execute(httpRequest);
107
+ this.sendResponse(res, result, tme, ep, log);
108
+ };
109
+ execute();
110
+ });
111
+ }
112
+ }
113
+ app.use((req, res, next) => {
114
+ const tme = +new Date();
115
+ this.sendResponse(res, {
116
+ error: DefaultErrors_1.default.UNKNOWN_REQUEST,
117
+ data: null,
118
+ httpStatus: 404
119
+ }, tme, req.path);
120
+ });
121
+ app.listen(this.options.port, () => {
122
+ console.log('API Service started at: ' + this.options.port + ", with base endpoint:" + this.options.baseEndPoint + ", ver.: " + this.version);
123
+ });
124
+ }
125
+ sendResponse(res, data, requestTime, endpoint, log) {
126
+ data.responseTime = (+new Date()) - requestTime;
127
+ data.version = this.version;
128
+ data.endpoint = endpoint ?? "no_endpoint";
129
+ if (log)
130
+ log.time = data.responseTime;
131
+ if (res.destroyed || res.closed) {
132
+ if (log)
133
+ log.error = "Connection already closed, can't send response";
134
+ if (this.options.onError)
135
+ this.options.onError("Connection already closed, can't send response", data);
136
+ }
137
+ else {
138
+ try {
139
+ if (data.redirect) {
140
+ res.redirect(data.redirect);
141
+ if (log)
142
+ log.response = { redirect: data.redirect };
143
+ }
144
+ else {
145
+ res.statusCode = data.httpStatus ?? 200;
146
+ if (data.rawResponse) {
147
+ res.send(data.data);
148
+ if (log)
149
+ log.response = data.data;
150
+ }
151
+ else {
152
+ res.send(data);
153
+ if (log)
154
+ log.response = data;
155
+ }
156
+ }
157
+ }
158
+ catch (e) {
159
+ if (this.options.onError)
160
+ this.options.onError("Can't send response", e);
161
+ if (log)
162
+ log.error = "Can't send response";
163
+ }
164
+ }
165
+ if (this.options.onNetworkLog && log)
166
+ this.options.onNetworkLog(log);
167
+ }
168
+ }
169
+ exports.APIService = APIService;
@@ -0,0 +1,17 @@
1
+ import { HTTPRequestVO, TransferPacketVO } from "./structures/Interfaces";
2
+ export interface IBaseEndpoint {
3
+ endpoints: string[];
4
+ execute: (req: HTTPRequestVO) => Promise<TransferPacketVO<any>>;
5
+ init: () => Promise<void>;
6
+ ignoreHttpLogging: boolean;
7
+ }
8
+ export declare class BaseEndpoint implements IBaseEndpoint {
9
+ ignoreHttpLogging: boolean;
10
+ private static entrypoint;
11
+ static setEntryPoint: (ep: string) => void;
12
+ static getEntryPoint: () => string;
13
+ endpoints: string[];
14
+ constructor(ep: string | string[]);
15
+ init(): Promise<void>;
16
+ execute(req: HTTPRequestVO): Promise<TransferPacketVO<any>>;
17
+ }
@@ -0,0 +1,46 @@
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.BaseEndpoint = void 0;
7
+ const DefaultErrors_1 = __importDefault(require("./structures/DefaultErrors"));
8
+ class BaseEndpoint {
9
+ ignoreHttpLogging = false;
10
+ static entrypoint = "/";
11
+ static setEntryPoint = (ep) => {
12
+ this.entrypoint = ep;
13
+ if (!this.entrypoint.endsWith("/")) {
14
+ this.entrypoint += "/";
15
+ }
16
+ if (!ep.startsWith("/"))
17
+ this.entrypoint = "/" + ep;
18
+ };
19
+ static getEntryPoint = () => {
20
+ return this.entrypoint;
21
+ };
22
+ endpoints;
23
+ constructor(ep) {
24
+ if (typeof ep === "string")
25
+ ep = [ep];
26
+ let eps = [];
27
+ for (let i of ep) {
28
+ if (i.startsWith("/"))
29
+ i = i.substring(1);
30
+ eps.push(i);
31
+ }
32
+ this.endpoints = eps;
33
+ }
34
+ async init() {
35
+ for (let i of this.endpoints)
36
+ console.log("endpoint: " + BaseEndpoint.entrypoint + i + " initalized");
37
+ }
38
+ ;
39
+ async execute(req) {
40
+ return {
41
+ error: DefaultErrors_1.default.NOT_IMPLEMENTED,
42
+ data: null
43
+ };
44
+ }
45
+ }
46
+ exports.BaseEndpoint = BaseEndpoint;
@@ -0,0 +1,10 @@
1
+ export interface IBaseService {
2
+ init: () => Promise<void>;
3
+ applicationReady: () => void;
4
+ }
5
+ export declare class BaseService implements IBaseService {
6
+ protected name: string;
7
+ constructor(name: string);
8
+ init(): Promise<void>;
9
+ applicationReady(): void;
10
+ }
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BaseService = void 0;
4
+ class BaseService {
5
+ name;
6
+ constructor(name) {
7
+ this.name = name;
8
+ }
9
+ async init() {
10
+ console.log("Service: " + this.name + " initialized");
11
+ }
12
+ applicationReady() { }
13
+ }
14
+ exports.BaseService = BaseService;
@@ -0,0 +1,3 @@
1
+ export declare const LocalRequest: (ep: string, data?: any, method?: string, headers?: {
2
+ [key: string]: string;
3
+ } | undefined) => Promise<void>;
@@ -0,0 +1,64 @@
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.LocalRequest = void 0;
7
+ const axios_1 = __importDefault(require("axios"));
8
+ axios_1.default.interceptors.request.use((x) => {
9
+ console.log(`AXIOS REQ:\n\turl:${x.url}\n\tmethod: ${x.method}\n\tdata: ${(() => {
10
+ if (typeof x.data === "string")
11
+ return x.data;
12
+ return JSON.stringify(x.data, null, "\t\t");
13
+ })()}\n\theaders:\n${(() => {
14
+ let h = "";
15
+ for (let i in x.headers) {
16
+ if (h !== "")
17
+ h += "\n";
18
+ h += "\t\t" + i + " = " + x.headers[i];
19
+ }
20
+ return h;
21
+ })()}`);
22
+ return x;
23
+ });
24
+ axios_1.default.interceptors.response.use((x) => {
25
+ console.log(`AXIOS RESP:\n\tdata: ${(() => {
26
+ if (typeof x.data === "string")
27
+ return x.data;
28
+ return JSON.stringify(x.data, null, "\t\t");
29
+ })()}\n\theaders:\n${(() => {
30
+ let h = "";
31
+ for (let i in x.headers) {
32
+ if (h !== "")
33
+ h += "\n";
34
+ h += "\t\t" + i + " = " + x.headers[i];
35
+ }
36
+ return h;
37
+ })()}`);
38
+ return x;
39
+ });
40
+ const LocalRequest = async (ep, data, method, headers) => {
41
+ let m = "get";
42
+ if (method)
43
+ m = method.toLowerCase();
44
+ if (data && !method)
45
+ m = "post";
46
+ if (ep.startsWith("/"))
47
+ ep = ep.substring(1);
48
+ let url = "http://localhost:8091/" + ep;
49
+ if (ep.toLowerCase().startsWith("http"))
50
+ url = ep;
51
+ let resp = null;
52
+ console.log("open url: ", url);
53
+ try {
54
+ if (m === "get")
55
+ resp = await axios_1.default.get(url, { headers: headers });
56
+ if (m === "post")
57
+ resp = await axios_1.default.post(url, data, { headers: headers });
58
+ }
59
+ catch (e) {
60
+ console.log("ERROR! cant open url", e);
61
+ }
62
+ console.log(resp);
63
+ };
64
+ exports.LocalRequest = LocalRequest;
@@ -0,0 +1,6 @@
1
+ import { ErrorVO } from "./Interfaces";
2
+ declare class DefaultErrors {
3
+ static NOT_IMPLEMENTED: ErrorVO;
4
+ static UNKNOWN_REQUEST: ErrorVO;
5
+ }
6
+ export default DefaultErrors;
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ class DefaultErrors {
4
+ static NOT_IMPLEMENTED = { code: 1, message: "Not implemented" };
5
+ static UNKNOWN_REQUEST = { code: 2, message: "Unknown request" };
6
+ }
7
+ exports.default = DefaultErrors;
@@ -0,0 +1,7 @@
1
+ export declare enum HTTP_STATUS {
2
+ OK = 200,
3
+ NOT_FOUND = 404,
4
+ UNAUTHORIZED = 401,
5
+ BAD_REQUEST = 400,
6
+ SERVER_ERROR = 500
7
+ }
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.HTTP_STATUS = void 0;
4
+ var HTTP_STATUS;
5
+ (function (HTTP_STATUS) {
6
+ HTTP_STATUS[HTTP_STATUS["OK"] = 200] = "OK";
7
+ HTTP_STATUS[HTTP_STATUS["NOT_FOUND"] = 404] = "NOT_FOUND";
8
+ HTTP_STATUS[HTTP_STATUS["UNAUTHORIZED"] = 401] = "UNAUTHORIZED";
9
+ HTTP_STATUS[HTTP_STATUS["BAD_REQUEST"] = 400] = "BAD_REQUEST";
10
+ HTTP_STATUS[HTTP_STATUS["SERVER_ERROR"] = 500] = "SERVER_ERROR";
11
+ })(HTTP_STATUS = exports.HTTP_STATUS || (exports.HTTP_STATUS = {}));
@@ -0,0 +1,25 @@
1
+ export interface TransferPacketVO<T> {
2
+ data?: T | null;
3
+ error?: ErrorVO | null;
4
+ responseTime?: number;
5
+ version?: string;
6
+ endpoint?: string;
7
+ httpStatus?: number;
8
+ redirect?: string;
9
+ rawResponse?: boolean;
10
+ }
11
+ export interface HTTPRequestVO {
12
+ raw: any;
13
+ method: string;
14
+ data: any;
15
+ params: {
16
+ [key: string]: string;
17
+ };
18
+ headers: any;
19
+ endpoint: string;
20
+ }
21
+ export interface ErrorVO {
22
+ code: number;
23
+ message: string;
24
+ details?: string;
25
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,3 @@
1
+ import { APIService, Initializer } from "./apiServer/APIService";
2
+ import { LocalRequest } from "./apiServer/LocalRequest";
3
+ export { APIService, Initializer, LocalRequest };
package/dist/index.js ADDED
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.LocalRequest = exports.Initializer = exports.APIService = void 0;
4
+ const APIService_1 = require("./apiServer/APIService");
5
+ Object.defineProperty(exports, "APIService", { enumerable: true, get: function () { return APIService_1.APIService; } });
6
+ Object.defineProperty(exports, "Initializer", { enumerable: true, get: function () { return APIService_1.Initializer; } });
7
+ const LocalRequest_1 = require("./apiServer/LocalRequest");
8
+ Object.defineProperty(exports, "LocalRequest", { enumerable: true, get: function () { return LocalRequest_1.LocalRequest; } });
@@ -0,0 +1,28 @@
1
+ declare class Signal<T> {
2
+ static nextID: number;
3
+ private busy;
4
+ private tempAdd;
5
+ private tempRem;
6
+ private tempInvoke;
7
+ private callbacks;
8
+ private tempClear;
9
+ constructor(name?: string);
10
+ add(callback: (data: T) => void, id?: string): string;
11
+ clear(): void;
12
+ remove(callback?: (data: T) => void, id?: string): string | undefined;
13
+ invoke(data: T): void;
14
+ }
15
+ export declare class SignalHandler {
16
+ static nextID: number;
17
+ private id;
18
+ private signals;
19
+ constructor();
20
+ add<T>(signal: Signal<T>, cb: (data: T) => void): void;
21
+ clear(): void;
22
+ }
23
+ export declare class SyncSignal<T, K> {
24
+ private worker?;
25
+ request(data: T): Promise<K>;
26
+ set listener(_listener: (request: T, response: (data: K) => void) => void);
27
+ }
28
+ export default Signal;
@@ -0,0 +1,140 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SyncSignal = exports.SignalHandler = void 0;
4
+ class Signal {
5
+ static nextID = 0;
6
+ busy = false;
7
+ tempAdd = [];
8
+ tempRem = [];
9
+ tempInvoke = [];
10
+ callbacks = [];
11
+ tempClear = false;
12
+ constructor(name) { }
13
+ add(callback, id) {
14
+ if (!id)
15
+ id = "" + (Signal.nextID++);
16
+ if (this.busy) {
17
+ for (let i of this.tempAdd) {
18
+ if (i.cb === callback)
19
+ return i.id;
20
+ }
21
+ this.tempAdd.push({ cb: callback, id: id });
22
+ return id;
23
+ }
24
+ for (let i of this.callbacks) {
25
+ if (i.cb === callback)
26
+ return i.id;
27
+ }
28
+ this.callbacks.push({ cb: callback, id: id });
29
+ return id;
30
+ }
31
+ clear() {
32
+ if (this.busy) {
33
+ this.tempClear = true;
34
+ return;
35
+ }
36
+ this.callbacks = [];
37
+ }
38
+ remove(callback, id) {
39
+ if (!callback && !id)
40
+ return;
41
+ if (this.busy) {
42
+ for (let i of this.tempRem) {
43
+ if (callback) {
44
+ if (i.cb === callback)
45
+ return;
46
+ }
47
+ if (id) {
48
+ if (i.id === id)
49
+ return;
50
+ }
51
+ }
52
+ this.tempRem.push({ cb: callback, id: id });
53
+ return id;
54
+ }
55
+ for (let i = 0; i < this.callbacks.length; i++) {
56
+ const itm = this.callbacks[i];
57
+ let remove = false;
58
+ if (callback)
59
+ remove = itm.cb === callback;
60
+ if (id)
61
+ remove = remove || itm.id === id;
62
+ if (remove) {
63
+ this.callbacks.splice(i, 1);
64
+ i--;
65
+ }
66
+ }
67
+ }
68
+ invoke(data) {
69
+ if (this.busy) {
70
+ this.tempInvoke.push({ data: data });
71
+ return;
72
+ }
73
+ this.busy = true;
74
+ for (let i of this.callbacks) {
75
+ if (i && i.cb && typeof i.cb === "function") {
76
+ i.cb(data);
77
+ }
78
+ }
79
+ this.busy = false;
80
+ for (let i of this.tempAdd) {
81
+ this.add(i.cb, i.id);
82
+ }
83
+ this.tempAdd = [];
84
+ for (let i of this.tempRem) {
85
+ this.remove(i.cb, i.id);
86
+ }
87
+ this.tempRem = [];
88
+ for (let i of this.tempInvoke) {
89
+ this.invoke(i.data);
90
+ }
91
+ if (this.tempClear)
92
+ this.clear();
93
+ }
94
+ }
95
+ class SignalHandler {
96
+ static nextID = 0;
97
+ id;
98
+ signals = [];
99
+ constructor() {
100
+ this.id = SignalHandler.nextID++;
101
+ }
102
+ add(signal, cb) {
103
+ let added = false;
104
+ for (let i of this.signals) {
105
+ if (i === signal) {
106
+ added = true;
107
+ break;
108
+ }
109
+ }
110
+ if (!added)
111
+ this.signals.push(signal);
112
+ signal.add(cb, "signaller_" + this.id);
113
+ }
114
+ clear() {
115
+ for (let i of this.signals)
116
+ i.remove(undefined, "signaller_" + this.id);
117
+ this.signals = [];
118
+ }
119
+ }
120
+ exports.SignalHandler = SignalHandler;
121
+ class SyncSignal {
122
+ worker;
123
+ request(data) {
124
+ const executor = (resolve, reject) => {
125
+ if (!this.worker) {
126
+ console.error("No worker registered in SyncSignal");
127
+ reject("No worker registered in SyncSignal");
128
+ return;
129
+ }
130
+ this.worker(data, resolve);
131
+ };
132
+ const promise = new Promise(executor);
133
+ return promise;
134
+ }
135
+ set listener(_listener) {
136
+ this.worker = _listener;
137
+ }
138
+ }
139
+ exports.SyncSignal = SyncSignal;
140
+ exports.default = Signal;
package/package.json ADDED
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "badmfck-api-server",
3
+ "version": "1.0.1",
4
+ "description": "Simple API http server based on express",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "scripts": {
8
+ "prepublish": "tsc",
9
+ "build":"tsc",
10
+ "test": "echo \"Error: no test specified\" && exit 1"
11
+ },
12
+ "keywords": [
13
+ "api",
14
+ "rest",
15
+ "http",
16
+ "server",
17
+ "express"
18
+ ],
19
+ "dependencies": {
20
+ "axios": "^1.4.0",
21
+ "badmfck-signal": "^1.0.1",
22
+ "cors": "^2.8.5",
23
+ "express": "^4.18.2"
24
+ },
25
+ "devDependencies": {
26
+ "@types/cors": "^2.8.13",
27
+ "@types/express": "^4.17.17"
28
+ },
29
+ "author": "Igor Bloom",
30
+ "license": "MIT"
31
+ }