badmfck-api-server 1.0.1 → 1.0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,8 +1,8 @@
1
1
  import { Response } from 'express';
2
2
  import { BaseService, IBaseService } from './BaseService';
3
3
  import { IBaseEndpoint } from './BaseEndpoint';
4
- import { SyncSignal } from '../signal/Signal';
5
4
  import { TransferPacketVO } from './structures/Interfaces';
5
+ import { Req } from "badmfck-signal";
6
6
  export interface APIServiceNetworkLogItem {
7
7
  id: number;
8
8
  created: number;
@@ -23,10 +23,11 @@ export interface APIServiceOptions {
23
23
  jsonLimit: string;
24
24
  onNetworkLog?: ((log: APIServiceNetworkLogItem) => void) | null;
25
25
  onError?: ((...rest: any[]) => void) | null;
26
+ isProductionEnvironment: boolean;
26
27
  }
27
28
  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[]>;
29
+ export declare const REQ_CREATE_NET_LOG: Req<void, APIServiceNetworkLogItem>;
30
+ export declare const REQ_HTTP_LOG: Req<void, APIServiceNetworkLogItem[]>;
30
31
  export declare function Initializer(services: IBaseService[]): Promise<void>;
31
32
  export declare class APIService extends BaseService {
32
33
  private static nextLogID;
@@ -9,7 +9,7 @@ const BaseService_1 = require("./BaseService");
9
9
  const cors_1 = __importDefault(require("cors"));
10
10
  const BaseEndpoint_1 = require("./BaseEndpoint");
11
11
  const DefaultErrors_1 = __importDefault(require("./structures/DefaultErrors"));
12
- const Signal_1 = require("../signal/Signal");
12
+ const badmfck_signal_1 = require("badmfck-signal");
13
13
  function getDefaultOptions() {
14
14
  return {
15
15
  port: 8091,
@@ -25,12 +25,13 @@ function getDefaultOptions() {
25
25
  },
26
26
  onError: function (err) {
27
27
  console.error.call(this, err);
28
- }
28
+ },
29
+ isProductionEnvironment: false
29
30
  };
30
31
  }
31
32
  exports.getDefaultOptions = getDefaultOptions;
32
- exports.REQ_CREATE_NET_LOG = new Signal_1.SyncSignal();
33
- exports.REQ_HTTP_LOG = new Signal_1.SyncSignal();
33
+ exports.REQ_CREATE_NET_LOG = new badmfck_signal_1.Req();
34
+ exports.REQ_HTTP_LOG = new badmfck_signal_1.Req();
34
35
  async function Initializer(services) {
35
36
  for (let i of services) {
36
37
  await i.init();
@@ -66,8 +67,32 @@ class APIService extends BaseService_1.BaseService {
66
67
  cb(log);
67
68
  };
68
69
  const app = (0, express_1.default)();
70
+ if (this.options.isProductionEnvironment)
71
+ app.set("env", 'production');
69
72
  app.use(express_1.default.json({ limit: '10mb' }));
70
73
  app.use(express_1.default.urlencoded({ limit: '10mb', extended: true }));
74
+ app.use(async (err, req, resp, next) => {
75
+ if (!err) {
76
+ next();
77
+ return;
78
+ }
79
+ const tme = +new Date();
80
+ const log = await exports.REQ_CREATE_NET_LOG.request();
81
+ log.request = {
82
+ method: req.method,
83
+ data: "",
84
+ params: req.params
85
+ };
86
+ let responseError = DefaultErrors_1.default.UNKNOWN_REQUEST;
87
+ if (typeof err === "object" && err.status === 400 && 'body' in err && err.type === 'entity.parse.failed') {
88
+ responseError = DefaultErrors_1.default.JSON_MALFORMED;
89
+ }
90
+ this.sendResponse(resp, {
91
+ error: responseError,
92
+ data: null,
93
+ httpStatus: 400
94
+ }, tme, "", log);
95
+ });
71
96
  const corsOptions = {
72
97
  origin: (origin, callback) => {
73
98
  const originIsWhitelisted = this.options.corsHostWhiteList.includes(origin);
@@ -0,0 +1,46 @@
1
+ import { FieldInfo, MysqlError, Pool, PoolConnection } from "mysql";
2
+ import { BaseService } from "./BaseService";
3
+ export interface MysqlServiceOptions {
4
+ connectionLimit: number;
5
+ host: string;
6
+ user: string;
7
+ password: string;
8
+ port: number;
9
+ database: string;
10
+ onLog?: (level: number, data: any) => void;
11
+ }
12
+ export interface MysqlResult {
13
+ error?: MysqlError | null;
14
+ isDuplicateError?: boolean;
15
+ fields?: FieldInfo[] | null;
16
+ data: any;
17
+ }
18
+ export interface MySqlQuery {
19
+ query: string;
20
+ fields: MysqlQueryField[];
21
+ }
22
+ export interface MysqlQueryField {
23
+ name: string;
24
+ value: string | number | boolean | null | undefined;
25
+ system?: boolean;
26
+ ignoreInInsert?: boolean;
27
+ ignoreInUpdate?: boolean;
28
+ useInReplace?: boolean;
29
+ _parsedValue?: string | number | boolean | null;
30
+ }
31
+ declare class MysqlService extends BaseService {
32
+ reconnectionTimeout: number;
33
+ reconnecting: boolean;
34
+ pool: Pool | null;
35
+ options: MysqlServiceOptions;
36
+ retries: number;
37
+ constructor(options: MysqlServiceOptions);
38
+ init(): Promise<void>;
39
+ onApplicationReady(): Promise<void>;
40
+ static prepareQuery(query: string, fields: MysqlQueryField[]): string;
41
+ static prepareQueryFieldValue(value: string | number | boolean | null | undefined, system?: boolean): string | number | boolean | null | undefined;
42
+ execute(query: string): Promise<MysqlResult>;
43
+ sendQuery(conn: PoolConnection, query: string, resolve: (data: MysqlResult) => void): void;
44
+ createPool(): Promise<boolean>;
45
+ }
46
+ export default MysqlService;
@@ -0,0 +1,248 @@
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
+ const mysql_1 = __importDefault(require("mysql"));
7
+ const BaseService_1 = require("./BaseService");
8
+ const badmfck_signal_1 = require("badmfck-signal");
9
+ const REQ_MYSQL_QUERY = new badmfck_signal_1.Req();
10
+ class MysqlService extends BaseService_1.BaseService {
11
+ reconnectionTimeout = 1000 * 3;
12
+ reconnecting = false;
13
+ pool = null;
14
+ options;
15
+ retries = 5;
16
+ constructor(options) {
17
+ super("mysql");
18
+ this.options = options;
19
+ this.init();
20
+ }
21
+ async init() {
22
+ const ok = await this.createPool();
23
+ if (!ok) {
24
+ if (this.options.onLog)
25
+ this.options.onLog(2, "Mysql server not connected");
26
+ }
27
+ REQ_MYSQL_QUERY.listener = async (data) => {
28
+ if (!Array.isArray(data))
29
+ data = [data];
30
+ const promises = [];
31
+ for (let i of data) {
32
+ const query = MysqlService.prepareQuery(i.query, i.fields);
33
+ promises.push(this.execute(query));
34
+ }
35
+ return await Promise.all(promises);
36
+ };
37
+ }
38
+ async onApplicationReady() { }
39
+ static prepareQuery(query, fields) {
40
+ for (let i of fields) {
41
+ const val = this.prepareQueryFieldValue(i.value, i.system);
42
+ i._parsedValue = val;
43
+ query = query.replaceAll("@" + i.name, val + "");
44
+ }
45
+ const tmp = query.toLowerCase();
46
+ if (tmp.indexOf("select") === 0 && tmp.indexOf("limit") === -1)
47
+ query += " LIMIT 100";
48
+ query = query.replaceAll("@NOLIMIT", "");
49
+ if (query.indexOf("@fields") !== -1) {
50
+ let insertFieldNames = [];
51
+ let insertFieldValues = [];
52
+ for (let i of fields) {
53
+ if (i.ignoreInInsert)
54
+ continue;
55
+ insertFieldNames.push('`' + i.name.replaceAll("`", '').replaceAll('\"', "").replaceAll('\'', "") + '`');
56
+ insertFieldValues.push(i._parsedValue);
57
+ }
58
+ query = query
59
+ .replaceAll('@fields', insertFieldNames.join(","))
60
+ .replaceAll('@values', insertFieldValues.join(","));
61
+ }
62
+ if (query.indexOf("@onupdate") !== -1) {
63
+ let onUpdate = [];
64
+ for (let i of fields) {
65
+ if (i.ignoreInUpdate)
66
+ continue;
67
+ onUpdate.push('`' + i.name.replaceAll("`", '').replaceAll('\"', "").replaceAll('\'', "") + '` = ' + i._parsedValue);
68
+ }
69
+ query = query.replaceAll("@onupdate", onUpdate.join(" , "));
70
+ }
71
+ if (query.indexOf('@onduplicate') !== -1) {
72
+ let onDuplicate = [];
73
+ for (let i of fields) {
74
+ if (!i.useInReplace)
75
+ continue;
76
+ onDuplicate.push('`' + i.name.replaceAll("`", '').replaceAll('\"', "").replaceAll('\'', "") + '` = ' + i._parsedValue);
77
+ }
78
+ query = query.replaceAll("@onduplicate", onDuplicate.join(" , "));
79
+ }
80
+ return query;
81
+ }
82
+ static prepareQueryFieldValue(value, system) {
83
+ if (!system && typeof value === "string") {
84
+ value = value ? value.replaceAll('"', '\\"') : null;
85
+ value = '"' + value + '"';
86
+ }
87
+ return value;
88
+ }
89
+ async execute(query) {
90
+ return new Promise((resolve, reject) => {
91
+ if (!this.pool) {
92
+ if (this.options.onLog)
93
+ this.options.onLog(2, "No pool");
94
+ resolve({
95
+ error: {
96
+ code: "NO_POOL",
97
+ errno: 100000,
98
+ fatal: true,
99
+ sql: query,
100
+ name: "NO_POOL",
101
+ message: "Mysql pool not created"
102
+ },
103
+ data: null,
104
+ fields: null
105
+ });
106
+ return;
107
+ }
108
+ this.pool.getConnection((err, conn) => {
109
+ if (err) {
110
+ if (this.options.onLog)
111
+ this.options.onLog(2, `${err}`);
112
+ resolve({
113
+ error: err,
114
+ data: null,
115
+ fields: null
116
+ });
117
+ return;
118
+ }
119
+ if (!conn) {
120
+ resolve({
121
+ error: {
122
+ code: "NO_CONN",
123
+ errno: 100001,
124
+ fatal: true,
125
+ sql: query,
126
+ name: "NO_CONN",
127
+ message: "Mysql pool cant get connection"
128
+ },
129
+ data: null,
130
+ fields: null
131
+ });
132
+ }
133
+ try {
134
+ this.sendQuery(conn, query, resolve);
135
+ }
136
+ catch (e) {
137
+ try {
138
+ conn.removeAllListeners();
139
+ conn.release();
140
+ }
141
+ catch (e) { }
142
+ resolve({
143
+ error: {
144
+ code: "QUERY_ERR",
145
+ errno: 100002,
146
+ fatal: true,
147
+ sql: query,
148
+ name: "QUERY_ERR",
149
+ message: "Error: " + e
150
+ },
151
+ data: null,
152
+ fields: null
153
+ });
154
+ }
155
+ });
156
+ });
157
+ }
158
+ sendQuery(conn, query, resolve) {
159
+ let errCatched = false;
160
+ conn.on("error", err => {
161
+ errCatched = true;
162
+ conn.release();
163
+ conn.removeAllListeners();
164
+ if (this.options.onLog)
165
+ this.options.onLog(2, `${err}`);
166
+ conn.removeAllListeners();
167
+ resolve({
168
+ error: {
169
+ code: "CONN_ERR",
170
+ errno: 100003,
171
+ fatal: true,
172
+ sql: query,
173
+ name: "CONN_ERR",
174
+ message: "Error: " + err
175
+ },
176
+ data: null,
177
+ fields: null
178
+ });
179
+ });
180
+ if (this.options.onLog)
181
+ this.options.onLog(0, query);
182
+ conn.query(query, (err, results, fields) => {
183
+ conn.release();
184
+ conn.removeAllListeners();
185
+ if (errCatched)
186
+ return;
187
+ if (err) {
188
+ const dup = `${err}`.toLowerCase().indexOf("er_dup_entry") !== -1;
189
+ if (this.options.onLog)
190
+ this.options.onLog(2, `${err}`);
191
+ resolve({
192
+ error: err,
193
+ data: null,
194
+ fields: null,
195
+ isDuplicateError: dup
196
+ });
197
+ return;
198
+ }
199
+ resolve({
200
+ error: err,
201
+ data: results,
202
+ fields: fields
203
+ });
204
+ return;
205
+ });
206
+ }
207
+ async createPool() {
208
+ if (this.options.onLog)
209
+ this.options.onLog(1, "Connecting to mysql: \n HOST: " + this.options.host + '\n USER:' + this.options.user + '\n PORT:' + this.options.port);
210
+ let err = false;
211
+ try {
212
+ this.pool = mysql_1.default.createPool({
213
+ connectionLimit: this.options.connectionLimit,
214
+ host: this.options.host,
215
+ user: this.options.user,
216
+ password: this.options.password,
217
+ port: this.options.port,
218
+ database: this.options.database,
219
+ multipleStatements: true
220
+ });
221
+ }
222
+ catch (e) {
223
+ if (this.options.onLog)
224
+ this.options.onLog(2, "Can't connect to MYSQL!");
225
+ err = true;
226
+ }
227
+ return new Promise((res, rej) => {
228
+ if (err) {
229
+ res(false);
230
+ return;
231
+ }
232
+ if (!this.pool) {
233
+ res(false);
234
+ return;
235
+ }
236
+ this.pool.getConnection((e, cnn) => {
237
+ if (e) {
238
+ if (this.options.onLog)
239
+ this.options.onLog(2, e.message);
240
+ res(false);
241
+ return;
242
+ }
243
+ res(true);
244
+ });
245
+ });
246
+ }
247
+ }
248
+ exports.default = MysqlService;
@@ -2,5 +2,6 @@ import { ErrorVO } from "./Interfaces";
2
2
  declare class DefaultErrors {
3
3
  static NOT_IMPLEMENTED: ErrorVO;
4
4
  static UNKNOWN_REQUEST: ErrorVO;
5
+ static JSON_MALFORMED: ErrorVO;
5
6
  }
6
7
  export default DefaultErrors;
@@ -3,5 +3,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  class DefaultErrors {
4
4
  static NOT_IMPLEMENTED = { code: 1, message: "Not implemented" };
5
5
  static UNKNOWN_REQUEST = { code: 2, message: "Unknown request" };
6
+ static JSON_MALFORMED = { code: 3, message: "JSON malformed" };
6
7
  }
7
8
  exports.default = DefaultErrors;
@@ -8,4 +8,4 @@ var HTTP_STATUS;
8
8
  HTTP_STATUS[HTTP_STATUS["UNAUTHORIZED"] = 401] = "UNAUTHORIZED";
9
9
  HTTP_STATUS[HTTP_STATUS["BAD_REQUEST"] = 400] = "BAD_REQUEST";
10
10
  HTTP_STATUS[HTTP_STATUS["SERVER_ERROR"] = 500] = "SERVER_ERROR";
11
- })(HTTP_STATUS = exports.HTTP_STATUS || (exports.HTTP_STATUS = {}));
11
+ })(HTTP_STATUS || (exports.HTTP_STATUS = HTTP_STATUS = {}));
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "badmfck-api-server",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "Simple API http server based on express",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "scripts": {
8
8
  "prepublish": "tsc",
9
- "build":"tsc",
9
+ "build": "tsc",
10
10
  "test": "echo \"Error: no test specified\" && exit 1"
11
11
  },
12
12
  "keywords": [
@@ -17,10 +17,12 @@
17
17
  "express"
18
18
  ],
19
19
  "dependencies": {
20
+ "@types/mysql": "^2.15.21",
20
21
  "axios": "^1.4.0",
21
22
  "badmfck-signal": "^1.0.1",
22
23
  "cors": "^2.8.5",
23
- "express": "^4.18.2"
24
+ "express": "^4.18.2",
25
+ "mysql": "^2.18.1"
24
26
  },
25
27
  "devDependencies": {
26
28
  "@types/cors": "^2.8.13",
Binary file