badmfck-api-server 1.0.1 → 1.0.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.
@@ -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