badmfck-api-server 2.1.9 → 2.2.0
Sign up to get free protection for your applications and to get access to all the features.
@@ -4,6 +4,12 @@ import * as mysql from "mysql2/promise";
|
|
4
4
|
export declare const S_MYSQL_STARTED: Signal<void>;
|
5
5
|
export declare const REQ_MYSQL_QUERY: Req<MySqlQuery | MySqlQuery[], MysqlResult[]>;
|
6
6
|
export declare const REQ_MYSQL_TRANSACTION: Req<MySqlQuery[], MysqlResult>;
|
7
|
+
export declare const REQ_MYSQL_TBEGIN: Req<void, number | MysqlError>;
|
8
|
+
export declare const REQ_MYSQL_TQUERY: Req<{
|
9
|
+
query: MySqlQuery;
|
10
|
+
tid: number;
|
11
|
+
}, MysqlResult>;
|
12
|
+
export declare const REQ_MYSQL_TEND: Req<number, MysqlError | null>;
|
7
13
|
export declare const executeQuery: (query: MySqlQuery | MySqlQuery[]) => Promise<MysqlResult[]>;
|
8
14
|
export interface MysqlServiceOptions {
|
9
15
|
connectionLimit: number;
|
@@ -13,6 +19,7 @@ export interface MysqlServiceOptions {
|
|
13
19
|
port: number;
|
14
20
|
database: string;
|
15
21
|
queueLimit?: number;
|
22
|
+
transactionFailReport?: (trx: ITransaction, message: string) => void;
|
16
23
|
migrations?: {
|
17
24
|
dir: string;
|
18
25
|
callback: () => void;
|
@@ -46,6 +53,15 @@ export interface MysqlQueryField {
|
|
46
53
|
useInReplace?: boolean;
|
47
54
|
_parsedValue?: string | number | boolean | null;
|
48
55
|
}
|
56
|
+
export interface ITransaction {
|
57
|
+
id: number;
|
58
|
+
timestamp: number;
|
59
|
+
conn: mysql.PoolConnection;
|
60
|
+
queries: {
|
61
|
+
sql: string;
|
62
|
+
status: string;
|
63
|
+
}[];
|
64
|
+
}
|
49
65
|
export interface MysqlQueryFieldObject extends Record<string, string | number | boolean | null | undefined | {
|
50
66
|
value: string | number | boolean | null | undefined;
|
51
67
|
system?: boolean;
|
@@ -62,9 +78,13 @@ export declare class MysqlService extends BaseService {
|
|
62
78
|
serviceStarted: boolean;
|
63
79
|
timeoutID: any;
|
64
80
|
queries: never[];
|
81
|
+
static nextTransactionID: number;
|
82
|
+
transactions: ITransaction[];
|
83
|
+
maxTransactionWaitTime: number;
|
65
84
|
constructor(options: MysqlServiceOptions);
|
66
85
|
static executeQuery(query: MySqlQuery | MySqlQuery[]): Promise<MysqlResult[]>;
|
67
86
|
init(): Promise<void>;
|
87
|
+
storeTransactionAsProblem(trx: ITransaction, message: string): Promise<void>;
|
68
88
|
recreatePool(): Promise<boolean>;
|
69
89
|
onApplicationReady(): Promise<void>;
|
70
90
|
static fieldsToObject(fields: MysqlQueryField[] | MysqlQueryFieldObject, ignoreSystemParameters?: boolean): MysqlQueryFieldObject;
|
@@ -23,7 +23,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
23
23
|
return result;
|
24
24
|
};
|
25
25
|
Object.defineProperty(exports, "__esModule", { value: true });
|
26
|
-
exports.MysqlService = exports.executeQuery = exports.REQ_MYSQL_TRANSACTION = exports.REQ_MYSQL_QUERY = exports.S_MYSQL_STARTED = void 0;
|
26
|
+
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
27
|
const BaseService_1 = require("./BaseService");
|
28
28
|
const badmfck_signal_1 = __importStar(require("badmfck-signal"));
|
29
29
|
const crypto_1 = require("crypto");
|
@@ -32,6 +32,9 @@ const mysql = __importStar(require("mysql2/promise"));
|
|
32
32
|
exports.S_MYSQL_STARTED = new badmfck_signal_1.default();
|
33
33
|
exports.REQ_MYSQL_QUERY = new badmfck_signal_1.Req(undefined, "REQ_MYSQL_QUERY");
|
34
34
|
exports.REQ_MYSQL_TRANSACTION = new badmfck_signal_1.Req(undefined, "REQ_MYSQL_TRANSACTION");
|
35
|
+
exports.REQ_MYSQL_TBEGIN = new badmfck_signal_1.Req(undefined, "REQ_MYSQL_TRANSACTION_BEGING");
|
36
|
+
exports.REQ_MYSQL_TQUERY = new badmfck_signal_1.Req(undefined, "REQ_MYSQL_TRANSACTION_ADD_OPERATION");
|
37
|
+
exports.REQ_MYSQL_TEND = new badmfck_signal_1.Req(undefined, "REQ_MYSQL_TRANSACTION_END");
|
35
38
|
const executeQuery = async (query) => { return await exports.REQ_MYSQL_QUERY.request(query); };
|
36
39
|
exports.executeQuery = executeQuery;
|
37
40
|
class MysqlService extends BaseService_1.BaseService {
|
@@ -42,6 +45,9 @@ class MysqlService extends BaseService_1.BaseService {
|
|
42
45
|
serviceStarted = false;
|
43
46
|
timeoutID;
|
44
47
|
queries = [];
|
48
|
+
static nextTransactionID = 0;
|
49
|
+
transactions = [];
|
50
|
+
maxTransactionWaitTime = 1000 * 60 * 2;
|
45
51
|
constructor(options) {
|
46
52
|
super("mysql");
|
47
53
|
this.options = options;
|
@@ -53,12 +59,40 @@ class MysqlService extends BaseService_1.BaseService {
|
|
53
59
|
this.options.database = decrypt(this.options.database.substring(1)) ?? this.options.database;
|
54
60
|
if (this.options.password.startsWith("_"))
|
55
61
|
this.options.password = decrypt(this.options.password.substring(1)) ?? this.options.password;
|
62
|
+
setInterval(() => {
|
63
|
+
const now = Date.now();
|
64
|
+
this.transactions = this.transactions.filter(async (i) => {
|
65
|
+
if (now - i.timestamp < this.maxTransactionWaitTime)
|
66
|
+
return true;
|
67
|
+
try {
|
68
|
+
(0, LogService_1.logError)("Release transaction connection due to timeout");
|
69
|
+
i.conn.removeAllListeners();
|
70
|
+
await i.conn.rollback();
|
71
|
+
i.conn.release();
|
72
|
+
}
|
73
|
+
catch (e) {
|
74
|
+
(0, LogService_1.logError)("Can't release transaction", e);
|
75
|
+
}
|
76
|
+
return false;
|
77
|
+
});
|
78
|
+
}, 1000 * 30);
|
56
79
|
}
|
57
80
|
static async executeQuery(query) { return await exports.REQ_MYSQL_QUERY.request(query); }
|
58
81
|
async init() {
|
59
82
|
super.init();
|
60
83
|
process.on('SIGINT', async () => {
|
61
84
|
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
|
+
}
|
62
96
|
if (this.pool) {
|
63
97
|
try {
|
64
98
|
await this.pool.end();
|
@@ -93,6 +127,93 @@ class MysqlService extends BaseService_1.BaseService {
|
|
93
127
|
}
|
94
128
|
return await Promise.all(promises);
|
95
129
|
};
|
130
|
+
exports.REQ_MYSQL_TBEGIN.listener = async () => {
|
131
|
+
const conn = await this.pool?.getConnection();
|
132
|
+
if (!conn)
|
133
|
+
return { code: "NO_POOL", errno: 100000, fatal: true, sql: "", name: "NO_POOL", message: "Mysql pool not created" };
|
134
|
+
const tid = MysqlService.nextTransactionID++;
|
135
|
+
try {
|
136
|
+
await conn.beginTransaction();
|
137
|
+
await conn.query("SET autocommit=0");
|
138
|
+
}
|
139
|
+
catch (e) {
|
140
|
+
try {
|
141
|
+
conn.removeAllListeners();
|
142
|
+
await conn.rollback();
|
143
|
+
conn.release();
|
144
|
+
}
|
145
|
+
catch (e) { }
|
146
|
+
return this.createMysqlQueryError(e);
|
147
|
+
}
|
148
|
+
this.transactions.push({
|
149
|
+
id: tid,
|
150
|
+
timestamp: Date.now(),
|
151
|
+
conn: conn,
|
152
|
+
queries: []
|
153
|
+
});
|
154
|
+
return tid;
|
155
|
+
};
|
156
|
+
exports.REQ_MYSQL_TQUERY.listener = async (data) => {
|
157
|
+
const trx = this.transactions.find(i => i.id === data.tid);
|
158
|
+
if (!trx)
|
159
|
+
return { err: {
|
160
|
+
code: "NO_TRX",
|
161
|
+
errno: 100004,
|
162
|
+
fatal: true,
|
163
|
+
sql: "",
|
164
|
+
name: "NO_TRX",
|
165
|
+
message: "Transaction not found"
|
166
|
+
}, data: null, rollbackError: null };
|
167
|
+
const query = MysqlService.prepareQuery(data.query.query, data.query.fields);
|
168
|
+
let err = null;
|
169
|
+
let rollbackError = null;
|
170
|
+
let sqlData = null;
|
171
|
+
try {
|
172
|
+
sqlData = await trx.conn.query(query);
|
173
|
+
}
|
174
|
+
catch (e) {
|
175
|
+
err = this.createMysqlQueryError(e);
|
176
|
+
}
|
177
|
+
if (err) {
|
178
|
+
try {
|
179
|
+
trx.conn.removeAllListeners();
|
180
|
+
await trx.conn.rollback();
|
181
|
+
trx.conn.release();
|
182
|
+
}
|
183
|
+
catch (e) {
|
184
|
+
rollbackError = this.createMysqlQueryError(e);
|
185
|
+
}
|
186
|
+
}
|
187
|
+
trx.queries.push({ sql: query, status: err ? err.message : "completed" });
|
188
|
+
return {
|
189
|
+
data: sqlData,
|
190
|
+
error: err,
|
191
|
+
rollbackError: rollbackError
|
192
|
+
};
|
193
|
+
};
|
194
|
+
exports.REQ_MYSQL_TEND.listener = async (tid) => {
|
195
|
+
const trx = this.transactions.find(i => i.id === tid);
|
196
|
+
if (!trx)
|
197
|
+
return {
|
198
|
+
code: "NO_TRX",
|
199
|
+
errno: 100004,
|
200
|
+
fatal: true,
|
201
|
+
sql: "",
|
202
|
+
name: "NO_TRX",
|
203
|
+
message: "Transaction not found"
|
204
|
+
};
|
205
|
+
this.transactions = this.transactions.filter(i => i.id !== tid);
|
206
|
+
try {
|
207
|
+
await trx.conn.commit();
|
208
|
+
trx.conn.removeAllListeners();
|
209
|
+
trx.conn.release();
|
210
|
+
}
|
211
|
+
catch (e) {
|
212
|
+
this.storeTransactionAsProblem(trx, "Can't commit transaction");
|
213
|
+
return this.createMysqlQueryError(e);
|
214
|
+
}
|
215
|
+
return null;
|
216
|
+
};
|
96
217
|
exports.REQ_MYSQL_TRANSACTION.listener = async (data) => {
|
97
218
|
if (!this.pool)
|
98
219
|
return { error: { code: "NO_POOL", errno: 100000, fatal: true, sql: "", name: "NO_POOL", message: "Mysql pool not created" }, data: null };
|
@@ -124,12 +245,21 @@ class MysqlService extends BaseService_1.BaseService {
|
|
124
245
|
try {
|
125
246
|
conn.release();
|
126
247
|
}
|
127
|
-
catch (e) {
|
248
|
+
catch (e) {
|
249
|
+
(0, LogService_1.logCrit)("${MysqlService.js}", "Can't release connection!");
|
250
|
+
}
|
128
251
|
if (!result)
|
129
252
|
result = { error: null, data: income };
|
130
253
|
return result;
|
131
254
|
};
|
132
255
|
}
|
256
|
+
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");
|
259
|
+
return;
|
260
|
+
}
|
261
|
+
this.options.transactionFailReport(trx, message);
|
262
|
+
}
|
133
263
|
async recreatePool() {
|
134
264
|
(0, LogService_1.logInfo)("${MysqlService.js}", "Connecting to mysql: \n HOST: " + this.options.host + '\n PORT:' + this.options.port);
|
135
265
|
if (this.pool) {
|