flexbiz-server 12.3.57 → 12.3.59
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.
- package/package.json +1 -1
- package/server/libs/sessionContext.js +200 -11
- package/server/modules/vouchers/vo-hd2.js +1895 -107
package/package.json
CHANGED
|
@@ -1,11 +1,200 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
const {AsyncLocalStorage}=require("node:async_hooks")
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
exports.
|
|
11
|
-
|
|
1
|
+
/** @fileoverview @preserve Prevent Closure Compiler rename this file */
|
|
2
|
+
'use strict';
|
|
3
|
+
/** @const */ const { AsyncLocalStorage } = require("node:async_hooks");
|
|
4
|
+
|
|
5
|
+
/** @const */ const storage = new AsyncLocalStorage();
|
|
6
|
+
|
|
7
|
+
exports.storage = storage;
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
exports.getCurrentStore = function() {
|
|
11
|
+
const store = storage.getStore();
|
|
12
|
+
return store;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Danh sách các model KHÔNG bao giờ được gắn session
|
|
17
|
+
* (ví dụ: các model ghi log, lịch sử, audit,...)
|
|
18
|
+
*/
|
|
19
|
+
const SESSION_EXCLUDE_MODELS = ["app","log","token","otp","cache", "notification","approve","email",'tontucthoi','listinfo','reportinfo','labelinfo'];
|
|
20
|
+
// ====== Thêm bộ đếm ID ======
|
|
21
|
+
let globalStoreCounter = 0;
|
|
22
|
+
let globalSessionCounter = 0;
|
|
23
|
+
/**
|
|
24
|
+
* Chạy một hàm trong context có session.
|
|
25
|
+
* Mọi câu lệnh Mongoose trong hàm này sẽ được gắn cùng session đó.
|
|
26
|
+
* @param {ClientSession} session - mongoose session
|
|
27
|
+
* @param {Function} fn - async function cần chạy
|
|
28
|
+
*/
|
|
29
|
+
exports.runWithSession = async function (session, fn) {
|
|
30
|
+
|
|
31
|
+
const existingStore = exports.getCurrentStore();
|
|
32
|
+
|
|
33
|
+
// Nếu đã có store rồi thì chỉ chạy trong context đó
|
|
34
|
+
if (existingStore) {
|
|
35
|
+
return fn();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const storeId = ++globalStoreCounter;
|
|
39
|
+
const sessionId = session?._debugId|| `txn-${++globalSessionCounter}`;
|
|
40
|
+
|
|
41
|
+
if (session && !session._debugId) session._debugId = sessionId; // Gắn id cho session để trace
|
|
42
|
+
|
|
43
|
+
console.log(`[runWithSession] start storeId=${storeId}, sessionId=${sessionId}`);
|
|
44
|
+
const context = { session,storeId, afterCommit: [] };
|
|
45
|
+
return storage.run(context, async () => {
|
|
46
|
+
try {
|
|
47
|
+
return await fn();
|
|
48
|
+
} finally {
|
|
49
|
+
//console.log(`[runWithSession] end storeId=${storeId}, sessionId=${sessionId}`);
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Hàm bao bọc handler và session
|
|
57
|
+
* @param {*} handler
|
|
58
|
+
* @param {*} ctrl
|
|
59
|
+
* @param {*} req
|
|
60
|
+
* @param {*} callback
|
|
61
|
+
* @param {...any} extraArgs
|
|
62
|
+
*/
|
|
63
|
+
exports.handlerWithSession = async function (handler, ctrl, req, callback, ...extraArgs) {
|
|
64
|
+
const session = await mongoose.startSession();
|
|
65
|
+
|
|
66
|
+
try {
|
|
67
|
+
await exports.runWithSession(session, async () => {
|
|
68
|
+
const store = storage.getStore();
|
|
69
|
+
console.log(`🔥 [handlerWithSession] start transaction storeId=${store?.storeId},sessionId=${store?.session?._debugId}, ctrl=${ctrl.name}`);
|
|
70
|
+
await session.startTransaction();
|
|
71
|
+
|
|
72
|
+
let finished = false;
|
|
73
|
+
|
|
74
|
+
handler(ctrl, req, async (error, result) => {
|
|
75
|
+
if (finished) return;
|
|
76
|
+
finished = true;
|
|
77
|
+
|
|
78
|
+
try {
|
|
79
|
+
if (error) {
|
|
80
|
+
console.error("❌ [handlerWithSession] abort transaction...", ctrl.name, error);
|
|
81
|
+
await session.abortTransaction().catch(() => {});
|
|
82
|
+
callback(error);
|
|
83
|
+
} else {
|
|
84
|
+
console.log("✅ [handlerWithSession] commit transaction...", ctrl.name);
|
|
85
|
+
await session.commitTransaction();
|
|
86
|
+
|
|
87
|
+
// 🔥 Giữ lại danh sách afterCommit trước khi end session
|
|
88
|
+
const store = storage.getStore();
|
|
89
|
+
const afterCommitCallbacks = exports.getAfterCommitList();
|
|
90
|
+
const sid = store?.storeId || "no-store";
|
|
91
|
+
console.log(`[onAfterCommit] running storeId=${sid}, count=${afterCommitCallbacks.length}`);
|
|
92
|
+
|
|
93
|
+
await session.endSession();
|
|
94
|
+
|
|
95
|
+
// 🔥 Chạy callback sau khi session đã đóng
|
|
96
|
+
for (const cb of afterCommitCallbacks) {
|
|
97
|
+
try {
|
|
98
|
+
await cb();
|
|
99
|
+
} catch (err) {
|
|
100
|
+
console.error("[onAfterCommit error]", err);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
callback(null, result);
|
|
105
|
+
}
|
|
106
|
+
} catch (e) {
|
|
107
|
+
console.error("❌ [handlerWithSession] error during commit/abort", e);
|
|
108
|
+
await session.abortTransaction().catch(() => {});
|
|
109
|
+
callback(e);
|
|
110
|
+
} finally {
|
|
111
|
+
await session.endSession().catch(() => {});
|
|
112
|
+
// Xóa session trong store để tránh reuse nhầm
|
|
113
|
+
const store = storage.getStore();
|
|
114
|
+
if (store) store.session = null;
|
|
115
|
+
}
|
|
116
|
+
}, ...extraArgs);
|
|
117
|
+
});
|
|
118
|
+
} catch (e) {
|
|
119
|
+
console.error("❌ [handlerWithSession] abort transaction (outer)...", ctrl?.name, e);
|
|
120
|
+
try {
|
|
121
|
+
await session.abortTransaction().catch(() => {});
|
|
122
|
+
} finally {
|
|
123
|
+
await session.endSession().catch(() => {});
|
|
124
|
+
}
|
|
125
|
+
callback(e);
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Thêm callback để chạy sau khi commit.
|
|
132
|
+
* - Nếu không có session => chạy ngay lập tức.
|
|
133
|
+
*/
|
|
134
|
+
exports.onAfterCommit = function (cb) {
|
|
135
|
+
const store = storage.getStore();
|
|
136
|
+
if (store && store.session) {
|
|
137
|
+
store.session.afterCommit = store.session.afterCommit || [];
|
|
138
|
+
store.session.afterCommit.push(cb);
|
|
139
|
+
console.log(`🔥 [onAfterCommit] added event.. storeId=${store.storeId}, count=${store.session.afterCommit.length}`);
|
|
140
|
+
} else {
|
|
141
|
+
// Không có session → chạy ngay
|
|
142
|
+
//console.log("[onAfterCommit] no session, running immediately");
|
|
143
|
+
Promise.resolve()
|
|
144
|
+
.then(cb)
|
|
145
|
+
.catch((err) => console.error("[onAfterCommit immediate error]", err));
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Lấy danh sách afterCommit hiện tại (dùng cho middleware)
|
|
151
|
+
*/
|
|
152
|
+
exports.getAfterCommitList = function () {
|
|
153
|
+
const store = storage.getStore();
|
|
154
|
+
return store && store.session ? store.session.afterCommit || [] : [];
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Chạy một hàm mà KHÔNG gắn session (dù đang trong transaction).
|
|
159
|
+
* Dùng cho các thao tác như ghi log, gửi email, lưu lịch sử...
|
|
160
|
+
* @param {Function} fn - async function cần chạy
|
|
161
|
+
*/
|
|
162
|
+
exports.runWithoutSession = async function (fn) {
|
|
163
|
+
return storage.run({ session: undefined }, fn);
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Lấy session hiện tại trong context.
|
|
168
|
+
* @returns {ClientSession|undefined}
|
|
169
|
+
*/
|
|
170
|
+
exports.getCurrentSession = function () {
|
|
171
|
+
const store = storage.getStore();
|
|
172
|
+
return store ? store.session : undefined;
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Kiểm tra xem model có được loại trừ khỏi session tự động không.
|
|
177
|
+
* @param {String|Model} model - model name hoặc đối tượng model
|
|
178
|
+
* @returns {Boolean}
|
|
179
|
+
*/
|
|
180
|
+
exports.isModelExcludedFromSession = function (model) {
|
|
181
|
+
const name = typeof model === "string" ? model : model?.modelName;
|
|
182
|
+
return !!(name && SESSION_EXCLUDE_MODELS.includes(name));
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Kiểm tra session còn hợp lệ (chưa commit hoặc abort)
|
|
187
|
+
* @param {ClientSession} session
|
|
188
|
+
* @returns {Boolean}
|
|
189
|
+
*/
|
|
190
|
+
exports.isSessionActive = function (session) {
|
|
191
|
+
if (!session) return false;
|
|
192
|
+
try {
|
|
193
|
+
// Một số driver Mongoose không expose state, nên ta kiểm tra gián tiếp:
|
|
194
|
+
return session.inTransaction(); // true nếu đang trong transaction
|
|
195
|
+
} catch {
|
|
196
|
+
return false;
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
console.log("[sessionContext] ✅ Loaded with model exclude & safety check");
|