badmfck-api-server 2.2.0 → 2.2.2

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.2.0";
86
+ version = "2.2.2";
87
87
  options;
88
88
  monitor;
89
89
  monitorIndexFile;
@@ -1,6 +1,8 @@
1
+ /// <reference types="node" />
1
2
  import { BaseService } from "./BaseService";
2
3
  import Signal, { Req } from "badmfck-signal";
3
4
  import * as mysql from "mysql2/promise";
5
+ import fs from "fs";
4
6
  export declare const S_MYSQL_STARTED: Signal<void>;
5
7
  export declare const REQ_MYSQL_QUERY: Req<MySqlQuery | MySqlQuery[], MysqlResult[]>;
6
8
  export declare const REQ_MYSQL_TRANSACTION: Req<MySqlQuery[], MysqlResult>;
@@ -20,6 +22,7 @@ export interface MysqlServiceOptions {
20
22
  database: string;
21
23
  queueLimit?: number;
22
24
  transactionFailReport?: (trx: ITransaction, message: string) => void;
25
+ transactionFailReportDir?: string;
23
26
  migrations?: {
24
27
  dir: string;
25
28
  callback: () => void;
@@ -81,9 +84,13 @@ export declare class MysqlService extends BaseService {
81
84
  static nextTransactionID: number;
82
85
  transactions: ITransaction[];
83
86
  maxTransactionWaitTime: number;
87
+ failReportFileStream: fs.WriteStream | null;
88
+ failReportFileStreamName: string | null;
89
+ failReportLastAccessTime: number;
84
90
  constructor(options: MysqlServiceOptions);
85
91
  static executeQuery(query: MySqlQuery | MySqlQuery[]): Promise<MysqlResult[]>;
86
92
  init(): Promise<void>;
93
+ finishApp(): Promise<void>;
87
94
  storeTransactionAsProblem(trx: ITransaction, message: string): Promise<void>;
88
95
  recreatePool(): Promise<boolean>;
89
96
  onApplicationReady(): Promise<void>;
@@ -22,6 +22,9 @@ var __importStar = (this && this.__importStar) || function (mod) {
22
22
  __setModuleDefault(result, mod);
23
23
  return result;
24
24
  };
25
+ var __importDefault = (this && this.__importDefault) || function (mod) {
26
+ return (mod && mod.__esModule) ? mod : { "default": mod };
27
+ };
25
28
  Object.defineProperty(exports, "__esModule", { value: true });
26
29
  exports.MysqlService = exports.executeQuery = exports.REQ_MYSQL_TEND = exports.REQ_MYSQL_TQUERY = exports.REQ_MYSQL_TBEGIN = exports.REQ_MYSQL_TRANSACTION = exports.REQ_MYSQL_QUERY = exports.S_MYSQL_STARTED = void 0;
27
30
  const BaseService_1 = require("./BaseService");
@@ -29,6 +32,8 @@ const badmfck_signal_1 = __importStar(require("badmfck-signal"));
29
32
  const crypto_1 = require("crypto");
30
33
  const LogService_1 = require("./LogService");
31
34
  const mysql = __importStar(require("mysql2/promise"));
35
+ const fs_1 = __importDefault(require("fs"));
36
+ const path_1 = __importDefault(require("path"));
32
37
  exports.S_MYSQL_STARTED = new badmfck_signal_1.default();
33
38
  exports.REQ_MYSQL_QUERY = new badmfck_signal_1.Req(undefined, "REQ_MYSQL_QUERY");
34
39
  exports.REQ_MYSQL_TRANSACTION = new badmfck_signal_1.Req(undefined, "REQ_MYSQL_TRANSACTION");
@@ -48,6 +53,9 @@ class MysqlService extends BaseService_1.BaseService {
48
53
  static nextTransactionID = 0;
49
54
  transactions = [];
50
55
  maxTransactionWaitTime = 1000 * 60 * 2;
56
+ failReportFileStream = null;
57
+ failReportFileStreamName = null;
58
+ failReportLastAccessTime = 0;
51
59
  constructor(options) {
52
60
  super("mysql");
53
61
  this.options = options;
@@ -76,35 +84,37 @@ class MysqlService extends BaseService_1.BaseService {
76
84
  return false;
77
85
  });
78
86
  }, 1000 * 30);
87
+ setInterval(() => {
88
+ if (!this.failReportFileStream)
89
+ return;
90
+ if (Date.now() - this.failReportLastAccessTime > 1000 * 60 * 15) {
91
+ this.failReportFileStream.end();
92
+ this.failReportFileStream = null;
93
+ }
94
+ }, 1000 * 60 * 5);
95
+ if (options.transactionFailReportDir) {
96
+ try {
97
+ if (!fs_1.default.existsSync(options.transactionFailReportDir)) {
98
+ fs_1.default.mkdirSync(options.transactionFailReportDir);
99
+ }
100
+ }
101
+ catch (e) {
102
+ options.transactionFailReportDir = undefined;
103
+ (0, LogService_1.logCrit)("${MysqlService.js}", "Can't create transaction fail report dir");
104
+ }
105
+ }
79
106
  }
80
107
  static async executeQuery(query) { return await exports.REQ_MYSQL_QUERY.request(query); }
81
108
  async init() {
82
109
  super.init();
83
110
  process.on('SIGINT', async () => {
84
111
  console.log('1. Received SIGINT. Performing cleanup...');
85
- for (let i of this.transactions) {
86
- try {
87
- this.storeTransactionAsProblem(i, "SIGINT");
88
- i.conn.removeAllListeners();
89
- await i.conn.rollback();
90
- i.conn.release();
91
- }
92
- catch (e) {
93
- (0, LogService_1.logError)("Can't release transaction connection", e);
94
- }
95
- }
96
- if (this.pool) {
97
- try {
98
- await this.pool.end();
99
- }
100
- catch (e) {
101
- (0, LogService_1.logCrit)("${MysqlService.js}", "Can't close MYSQL pool!");
102
- }
103
- }
112
+ await this.finishApp();
104
113
  process.exit(0);
105
114
  });
106
- process.on('SIGTERM', () => {
115
+ process.on('SIGTERM', async () => {
107
116
  console.log('2. Received SIGTERM. Performing cleanup...');
117
+ await this.finishApp();
108
118
  process.exit(0);
109
119
  });
110
120
  this.serviceStarted = false;
@@ -220,15 +230,42 @@ class MysqlService extends BaseService_1.BaseService {
220
230
  const conn = await this.pool.getConnection();
221
231
  if (!conn)
222
232
  return { error: { code: "NO_CONN", errno: 100001, fatal: true, sql: "", name: "NO_CONN", message: "Mysql pool cant get connection" }, data: null };
223
- let result = null;
233
+ const trx = {
234
+ id: MysqlService.nextTransactionID++,
235
+ timestamp: Date.now(),
236
+ conn: conn,
237
+ queries: []
238
+ };
224
239
  let income = [];
225
240
  try {
226
241
  await conn.beginTransaction();
227
242
  await conn.query("SET autocommit=0");
228
- for (let i of data) {
229
- const d = await conn.query(MysqlService.prepareQuery(i.query, i.fields));
243
+ }
244
+ catch (e) {
245
+ const err = this.createMysqlQueryError(e);
246
+ this.storeTransactionAsProblem(trx, err.message);
247
+ return { error: err, data: null };
248
+ }
249
+ for (let i of data) {
250
+ const query = MysqlService.prepareQuery(i.query, i.fields);
251
+ let err = null;
252
+ try {
253
+ const d = await conn.query(query);
230
254
  income.push(d[0]);
231
255
  }
256
+ catch (e) {
257
+ err = this.createMysqlQueryError(e);
258
+ }
259
+ trx.queries.push({
260
+ sql: query,
261
+ status: err ? err.message : "completed"
262
+ });
263
+ if (err) {
264
+ this.storeTransactionAsProblem(trx, err.message);
265
+ return { error: err, data: null };
266
+ }
267
+ }
268
+ try {
232
269
  await conn.commit();
233
270
  }
234
271
  catch (e) {
@@ -240,25 +277,64 @@ class MysqlService extends BaseService_1.BaseService {
240
277
  (0, LogService_1.logCrit)("${MysqlService.js}", "Can't rollback transaction!");
241
278
  rollbackError = this.createMysqlQueryError(e2);
242
279
  }
243
- result = { error: this.createMysqlQueryError(e), data: null, rollbackError: rollbackError };
280
+ const err = this.createMysqlQueryError(e);
281
+ this.storeTransactionAsProblem(trx, err.message);
282
+ return { error: err, data: null, rollbackError: rollbackError };
244
283
  }
284
+ return { error: null, data: income };
285
+ };
286
+ }
287
+ async finishApp() {
288
+ for (let i of this.transactions) {
245
289
  try {
246
- conn.release();
290
+ this.storeTransactionAsProblem(i, "SIGINT");
291
+ i.conn.removeAllListeners();
292
+ await i.conn.rollback();
293
+ i.conn.release();
247
294
  }
248
295
  catch (e) {
249
- (0, LogService_1.logCrit)("${MysqlService.js}", "Can't release connection!");
296
+ (0, LogService_1.logError)("Can't release transaction connection", e);
250
297
  }
251
- if (!result)
252
- result = { error: null, data: income };
253
- return result;
254
- };
298
+ }
299
+ if (this.pool) {
300
+ try {
301
+ await this.pool.end();
302
+ }
303
+ catch (e) {
304
+ (0, LogService_1.logCrit)("${MysqlService.js}", "Can't close MYSQL pool!");
305
+ }
306
+ }
307
+ if (this.failReportFileStream)
308
+ this.failReportFileStream.end();
309
+ this.failReportFileStream = null;
255
310
  }
256
311
  async storeTransactionAsProblem(trx, message) {
257
- if (!this.options.transactionFailReport) {
258
- (0, LogService_1.logCrit)("${MysqlService.js}", "Can't report failed transaction, no report function: transactionFailReport in options");
312
+ if (!this.options.transactionFailReport && !this.options.transactionFailReportDir) {
313
+ (0, LogService_1.logCrit)("${MysqlService.js}", "Can't report failed transaction, no report function: transactionFailReport in options, and transactionFailReportDir isnt set");
259
314
  return;
260
315
  }
261
- this.options.transactionFailReport(trx, message);
316
+ if (this.options.transactionFailReportDir) {
317
+ const yyymmdd = new Date().toISOString().substring(0, 10).replaceAll("-", "");
318
+ const date = new Date();
319
+ const file = path_1.default.resolve(this.options.transactionFailReportDir, yyymmdd + ".json");
320
+ if (this.failReportFileStream) {
321
+ if (this.failReportFileStreamName !== file) {
322
+ this.failReportFileStream.end();
323
+ this.failReportFileStream = null;
324
+ }
325
+ }
326
+ if (this.failReportFileStreamName) {
327
+ this.failReportFileStream = fs_1.default.createWriteStream(file, { flags: 'a' });
328
+ this.failReportFileStreamName = file;
329
+ }
330
+ this.failReportFileStream?.write(JSON.stringify({ trx, date, message }) + "\n{'s':'&$5__1AzZa'}\n", err => {
331
+ if (err)
332
+ (0, LogService_1.logCrit)("${MysqlService.js}", "Can't write to transaction fail report file");
333
+ });
334
+ this.failReportLastAccessTime = Date.now();
335
+ }
336
+ if (this.options.transactionFailReport)
337
+ this.options.transactionFailReport(trx, message);
262
338
  }
263
339
  async recreatePool() {
264
340
  (0, LogService_1.logInfo)("${MysqlService.js}", "Connecting to mysql: \n HOST: " + this.options.host + '\n PORT:' + this.options.port);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "badmfck-api-server",
3
- "version": "2.2.0",
3
+ "version": "2.2.2",
4
4
  "description": "Simple API http server based on express",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",