badmfck-api-server 2.2.0 → 2.2.2

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.
@@ -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",