badmfck-api-server 2.2.1 → 2.2.3
Sign up to get free protection for your applications and to get access to all the features.
@@ -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>;
|
@@ -9,7 +11,8 @@ export declare const REQ_MYSQL_TQUERY: Req<{
|
|
9
11
|
query: MySqlQuery;
|
10
12
|
tid: number;
|
11
13
|
}, MysqlResult>;
|
12
|
-
export declare const
|
14
|
+
export declare const REQ_MYSQL_TCOMMIT: Req<number, MysqlError | null>;
|
15
|
+
export declare const REQ_MYSQL_TROLLBACK: Req<number, MysqlError | null>;
|
13
16
|
export declare const executeQuery: (query: MySqlQuery | MySqlQuery[]) => Promise<MysqlResult[]>;
|
14
17
|
export interface MysqlServiceOptions {
|
15
18
|
connectionLimit: number;
|
@@ -44,6 +47,7 @@ export interface MySqlQuery {
|
|
44
47
|
query: string;
|
45
48
|
fields: MysqlQueryField[] | MysqlQueryFieldObject;
|
46
49
|
rollbackQuery?: string | null;
|
50
|
+
transactionID: number;
|
47
51
|
}
|
48
52
|
export interface MysqlQueryField {
|
49
53
|
name: string;
|
@@ -82,6 +86,9 @@ export declare class MysqlService extends BaseService {
|
|
82
86
|
static nextTransactionID: number;
|
83
87
|
transactions: ITransaction[];
|
84
88
|
maxTransactionWaitTime: number;
|
89
|
+
failReportFileStream: fs.WriteStream | null;
|
90
|
+
failReportFileStreamName: string | null;
|
91
|
+
failReportLastAccessTime: number;
|
85
92
|
constructor(options: MysqlServiceOptions);
|
86
93
|
static executeQuery(query: MySqlQuery | MySqlQuery[]): Promise<MysqlResult[]>;
|
87
94
|
init(): Promise<void>;
|
@@ -93,7 +100,7 @@ export declare class MysqlService extends BaseService {
|
|
93
100
|
static objectToFields(obj: MysqlQueryFieldObject | MysqlQueryField[]): MysqlQueryField[];
|
94
101
|
static prepareQuery(query: string, fields: MysqlQueryField[] | MysqlQueryFieldObject): string;
|
95
102
|
static prepareQueryFieldValue(value: string | number | boolean | null | undefined, system?: boolean): string | number | boolean | null | undefined;
|
96
|
-
execute(query: string, rollbackQuery: string | null): Promise<MysqlResult>;
|
103
|
+
execute(query: string, rollbackQuery: string | null, transactionID: number): Promise<MysqlResult>;
|
97
104
|
sendQuery(conn: mysql.PoolConnection, query: string, resolve: (data: MysqlResult) => void, rollback: string | null): Promise<void>;
|
98
105
|
queryAsync(conn: mysql.PoolConnection, query: string): Promise<{
|
99
106
|
err: MysqlError | null;
|
@@ -22,19 +22,25 @@ 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
|
-
exports.MysqlService = exports.executeQuery = exports.
|
29
|
+
exports.MysqlService = exports.executeQuery = exports.REQ_MYSQL_TROLLBACK = exports.REQ_MYSQL_TCOMMIT = 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");
|
28
31
|
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");
|
35
40
|
exports.REQ_MYSQL_TBEGIN = new badmfck_signal_1.Req(undefined, "REQ_MYSQL_TRANSACTION_BEGING");
|
36
41
|
exports.REQ_MYSQL_TQUERY = new badmfck_signal_1.Req(undefined, "REQ_MYSQL_TRANSACTION_ADD_OPERATION");
|
37
|
-
exports.
|
42
|
+
exports.REQ_MYSQL_TCOMMIT = new badmfck_signal_1.Req(undefined, "REQ_MYSQL_TCOMMIT");
|
43
|
+
exports.REQ_MYSQL_TROLLBACK = new badmfck_signal_1.Req(undefined, "REQ_MYSQL_TROLLBACK");
|
38
44
|
const executeQuery = async (query) => { return await exports.REQ_MYSQL_QUERY.request(query); };
|
39
45
|
exports.executeQuery = executeQuery;
|
40
46
|
class MysqlService extends BaseService_1.BaseService {
|
@@ -48,6 +54,9 @@ class MysqlService extends BaseService_1.BaseService {
|
|
48
54
|
static nextTransactionID = 0;
|
49
55
|
transactions = [];
|
50
56
|
maxTransactionWaitTime = 1000 * 60 * 2;
|
57
|
+
failReportFileStream = null;
|
58
|
+
failReportFileStreamName = null;
|
59
|
+
failReportLastAccessTime = 0;
|
51
60
|
constructor(options) {
|
52
61
|
super("mysql");
|
53
62
|
this.options = options;
|
@@ -76,11 +85,18 @@ class MysqlService extends BaseService_1.BaseService {
|
|
76
85
|
return false;
|
77
86
|
});
|
78
87
|
}, 1000 * 30);
|
88
|
+
setInterval(() => {
|
89
|
+
if (!this.failReportFileStream)
|
90
|
+
return;
|
91
|
+
if (Date.now() - this.failReportLastAccessTime > 1000 * 60 * 15) {
|
92
|
+
this.failReportFileStream.end();
|
93
|
+
this.failReportFileStream = null;
|
94
|
+
}
|
95
|
+
}, 1000 * 60 * 5);
|
79
96
|
if (options.transactionFailReportDir) {
|
80
|
-
const fs = require('fs');
|
81
97
|
try {
|
82
|
-
if (!
|
83
|
-
|
98
|
+
if (!fs_1.default.existsSync(options.transactionFailReportDir)) {
|
99
|
+
fs_1.default.mkdirSync(options.transactionFailReportDir);
|
84
100
|
}
|
85
101
|
}
|
86
102
|
catch (e) {
|
@@ -118,7 +134,7 @@ class MysqlService extends BaseService_1.BaseService {
|
|
118
134
|
const promises = [];
|
119
135
|
for (let i of data) {
|
120
136
|
const query = MysqlService.prepareQuery(i.query, i.fields);
|
121
|
-
promises.push(this.execute(query, i.rollbackQuery ?? null));
|
137
|
+
promises.push(this.execute(query, i.rollbackQuery ?? null, i.transactionID));
|
122
138
|
}
|
123
139
|
return await Promise.all(promises);
|
124
140
|
};
|
@@ -186,7 +202,30 @@ class MysqlService extends BaseService_1.BaseService {
|
|
186
202
|
rollbackError: rollbackError
|
187
203
|
};
|
188
204
|
};
|
189
|
-
exports.
|
205
|
+
exports.REQ_MYSQL_TROLLBACK.listener = async (tid) => {
|
206
|
+
const trx = this.transactions.find(i => i.id === tid);
|
207
|
+
if (!trx)
|
208
|
+
return {
|
209
|
+
code: "NO_TRX",
|
210
|
+
errno: 100004,
|
211
|
+
fatal: true,
|
212
|
+
sql: "",
|
213
|
+
name: "NO_TRX",
|
214
|
+
message: "Transaction not found"
|
215
|
+
};
|
216
|
+
this.transactions = this.transactions.filter(i => i.id !== tid);
|
217
|
+
try {
|
218
|
+
await trx.conn.rollback();
|
219
|
+
trx.conn.removeAllListeners();
|
220
|
+
trx.conn.release();
|
221
|
+
}
|
222
|
+
catch (e) {
|
223
|
+
(0, LogService_1.logError)("Can't rollback transaction");
|
224
|
+
return this.createMysqlQueryError(e);
|
225
|
+
}
|
226
|
+
return null;
|
227
|
+
};
|
228
|
+
exports.REQ_MYSQL_TCOMMIT.listener = async (tid) => {
|
190
229
|
const trx = this.transactions.find(i => i.id === tid);
|
191
230
|
if (!trx)
|
192
231
|
return {
|
@@ -205,6 +244,14 @@ class MysqlService extends BaseService_1.BaseService {
|
|
205
244
|
}
|
206
245
|
catch (e) {
|
207
246
|
this.storeTransactionAsProblem(trx, "Can't commit transaction");
|
247
|
+
try {
|
248
|
+
trx.conn.removeAllListeners();
|
249
|
+
await trx.conn.rollback();
|
250
|
+
trx.conn.release();
|
251
|
+
}
|
252
|
+
catch (e) {
|
253
|
+
(0, LogService_1.logError)("Can't rollback transaction");
|
254
|
+
}
|
208
255
|
return this.createMysqlQueryError(e);
|
209
256
|
}
|
210
257
|
return null;
|
@@ -225,40 +272,46 @@ class MysqlService extends BaseService_1.BaseService {
|
|
225
272
|
try {
|
226
273
|
await conn.beginTransaction();
|
227
274
|
await conn.query("SET autocommit=0");
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
}
|
238
|
-
trx.queries.push({
|
239
|
-
sql: query,
|
240
|
-
status: err ? err.message : "completed"
|
241
|
-
});
|
242
|
-
if (err)
|
243
|
-
return { error: err, data: null };
|
244
|
-
}
|
275
|
+
}
|
276
|
+
catch (e) {
|
277
|
+
const err = this.createMysqlQueryError(e);
|
278
|
+
this.storeTransactionAsProblem(trx, err.message);
|
279
|
+
return { error: err, data: null };
|
280
|
+
}
|
281
|
+
for (let i of data) {
|
282
|
+
const query = MysqlService.prepareQuery(i.query, i.fields);
|
283
|
+
let err = null;
|
245
284
|
try {
|
246
|
-
await conn.
|
285
|
+
const d = await conn.query(query);
|
286
|
+
income.push(d[0]);
|
247
287
|
}
|
248
288
|
catch (e) {
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
return { error:
|
289
|
+
err = this.createMysqlQueryError(e);
|
290
|
+
}
|
291
|
+
trx.queries.push({
|
292
|
+
sql: query,
|
293
|
+
status: err ? err.message : "completed"
|
294
|
+
});
|
295
|
+
if (err) {
|
296
|
+
this.storeTransactionAsProblem(trx, err.message);
|
297
|
+
return { error: err, data: null };
|
258
298
|
}
|
259
299
|
}
|
300
|
+
try {
|
301
|
+
await conn.commit();
|
302
|
+
}
|
260
303
|
catch (e) {
|
261
|
-
|
304
|
+
let rollbackError = null;
|
305
|
+
try {
|
306
|
+
await conn.rollback();
|
307
|
+
}
|
308
|
+
catch (e2) {
|
309
|
+
(0, LogService_1.logCrit)("${MysqlService.js}", "Can't rollback transaction!");
|
310
|
+
rollbackError = this.createMysqlQueryError(e2);
|
311
|
+
}
|
312
|
+
const err = this.createMysqlQueryError(e);
|
313
|
+
this.storeTransactionAsProblem(trx, err.message);
|
314
|
+
return { error: err, data: null, rollbackError: rollbackError };
|
262
315
|
}
|
263
316
|
return { error: null, data: income };
|
264
317
|
};
|
@@ -283,13 +336,37 @@ class MysqlService extends BaseService_1.BaseService {
|
|
283
336
|
(0, LogService_1.logCrit)("${MysqlService.js}", "Can't close MYSQL pool!");
|
284
337
|
}
|
285
338
|
}
|
339
|
+
if (this.failReportFileStream)
|
340
|
+
this.failReportFileStream.end();
|
341
|
+
this.failReportFileStream = null;
|
286
342
|
}
|
287
343
|
async storeTransactionAsProblem(trx, message) {
|
288
|
-
if (!this.options.transactionFailReport) {
|
289
|
-
(0, LogService_1.logCrit)("${MysqlService.js}", "Can't report failed transaction, no report function: transactionFailReport in options");
|
344
|
+
if (!this.options.transactionFailReport && !this.options.transactionFailReportDir) {
|
345
|
+
(0, LogService_1.logCrit)("${MysqlService.js}", "Can't report failed transaction, no report function: transactionFailReport in options, and transactionFailReportDir isnt set");
|
290
346
|
return;
|
291
347
|
}
|
292
|
-
this.options.
|
348
|
+
if (this.options.transactionFailReportDir) {
|
349
|
+
const yyymmdd = new Date().toISOString().substring(0, 10).replaceAll("-", "");
|
350
|
+
const date = new Date();
|
351
|
+
const file = path_1.default.resolve(this.options.transactionFailReportDir, yyymmdd + ".json");
|
352
|
+
if (this.failReportFileStream) {
|
353
|
+
if (this.failReportFileStreamName !== file) {
|
354
|
+
this.failReportFileStream.end();
|
355
|
+
this.failReportFileStream = null;
|
356
|
+
}
|
357
|
+
}
|
358
|
+
if (this.failReportFileStreamName) {
|
359
|
+
this.failReportFileStream = fs_1.default.createWriteStream(file, { flags: 'a' });
|
360
|
+
this.failReportFileStreamName = file;
|
361
|
+
}
|
362
|
+
this.failReportFileStream?.write(JSON.stringify({ trx, date, message }) + "\n{'s':'&$5__1AzZa'}\n", err => {
|
363
|
+
if (err)
|
364
|
+
(0, LogService_1.logCrit)("${MysqlService.js}", "Can't write to transaction fail report file");
|
365
|
+
});
|
366
|
+
this.failReportLastAccessTime = Date.now();
|
367
|
+
}
|
368
|
+
if (this.options.transactionFailReport)
|
369
|
+
this.options.transactionFailReport(trx, message);
|
293
370
|
}
|
294
371
|
async recreatePool() {
|
295
372
|
(0, LogService_1.logInfo)("${MysqlService.js}", "Connecting to mysql: \n HOST: " + this.options.host + '\n PORT:' + this.options.port);
|
@@ -466,7 +543,7 @@ class MysqlService extends BaseService_1.BaseService {
|
|
466
543
|
return "NULL";
|
467
544
|
return value;
|
468
545
|
}
|
469
|
-
async execute(query, rollbackQuery) {
|
546
|
+
async execute(query, rollbackQuery, transactionID) {
|
470
547
|
return new Promise(async (resolve, reject) => {
|
471
548
|
if (!this.pool) {
|
472
549
|
(0, LogService_1.logError)("${MysqlService.js}", "No pool");
|
@@ -484,7 +561,13 @@ class MysqlService extends BaseService_1.BaseService {
|
|
484
561
|
return;
|
485
562
|
}
|
486
563
|
try {
|
487
|
-
|
564
|
+
let conn = null;
|
565
|
+
if (transactionID && transactionID > 0) {
|
566
|
+
conn = this.transactions.find(i => i.id === transactionID)?.conn ?? null;
|
567
|
+
}
|
568
|
+
else {
|
569
|
+
conn = await this.pool.getConnection();
|
570
|
+
}
|
488
571
|
if (!conn) {
|
489
572
|
(0, LogService_1.logCrit)("${MysqlService.js}", `No connection created!`);
|
490
573
|
resolve({
|