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 CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "flexbiz-server",
3
3
  "main": "./server/app.js",
4
4
  "description": "Flexible Server",
5
- "version": "12.3.57",
5
+ "version": "12.3.59",
6
6
  "author": {
7
7
  "name": "Van Truong Pham",
8
8
  "email": "invncur@gmail.com"
@@ -1,11 +1,200 @@
1
- /*
2
- Prevent Closure Compiler rename this file */
3
- const {AsyncLocalStorage}=require("node:async_hooks"),storage=new AsyncLocalStorage;exports.storage=storage;exports.getCurrentStore=function(){return storage.getStore()};const SESSION_EXCLUDE_MODELS="app log token otp cache notification approve email tontucthoi listinfo reportinfo labelinfo".split(" ");let globalStoreCounter=0,globalSessionCounter=0;
4
- exports.runWithSession=async function($session$$,$fn$$){if(exports.getCurrentStore())return $fn$$();const $storeId$$=++globalStoreCounter,$sessionId$$=$session$$?._debugId||`txn-${++globalSessionCounter}`;$session$$&&!$session$$._debugId&&($session$$._debugId=$sessionId$$);console.log(`[runWithSession] start storeId=${$storeId$$}, sessionId=${$sessionId$$}`);return storage.run({session:$session$$,storeId:$storeId$$,afterCommit:[]},async()=>await $fn$$())};
5
- exports.handlerWithSession=async function($handler$$,$ctrl$$,$req$$,$callback$$,...$extraArgs$$){const $session$$=await mongoose.startSession();try{await exports.runWithSession($session$$,async()=>{const $store$$=storage.getStore();console.log(`[handlerWithSession] start transaction storeId=${$store$$?.storeId},sessionId=${$store$$?.session?._debugId}, ctrl=${$ctrl$$.name}`);await $session$$.startTransaction();let $finished$$=!1;$handler$$($ctrl$$,$req$$,async($error$jscomp$2_store$$,$result$$)=>
6
- {if(!$finished$$){$finished$$=!0;try{if($error$jscomp$2_store$$)console.error("[handlerWithSession] abort transaction...",$ctrl$$.name,$error$jscomp$2_store$$),await $session$$.abortTransaction().catch(()=>{}),$callback$$($error$jscomp$2_store$$);else{console.log("[handlerWithSession] commit transaction...",$ctrl$$.name);await $session$$.commitTransaction();const $store$$=storage.getStore(),$afterCommitCallbacks$$=exports.getAfterCommitList();console.log(`[onAfterCommit] running storeId=${$store$$?.storeId||
7
- "no-store"}, count=${$afterCommitCallbacks$$.length}`);await $session$$.endSession();for(const $cb$$ of $afterCommitCallbacks$$)try{await $cb$$()}catch($err$$){console.error("[onAfterCommit error]",$err$$)}$callback$$(null,$result$$)}}catch($e$$){console.error("[handlerWithSession] error during commit/abort",$e$$),await $session$$.abortTransaction().catch(()=>{}),$callback$$($e$$)}finally{if(await $session$$.endSession().catch(()=>{}),$error$jscomp$2_store$$=storage.getStore())$error$jscomp$2_store$$.session=
8
- null}}},...$extraArgs$$)})}catch($e$$){console.error("[handlerWithSession] abort transaction (outer)...",$ctrl$$?.name,$e$$);try{await $session$$.abortTransaction().catch(()=>{})}finally{await $session$$.endSession().catch(()=>{})}$callback$$($e$$)}};
9
- exports.onAfterCommit=function($cb$$){const $store$$=storage.getStore();$store$$&&$store$$.session?($store$$.session.afterCommit=$store$$.session.afterCommit||[],$store$$.session.afterCommit.push($cb$$),console.log(`\u2705 [onAfterCommit] added event.. storeId=${$store$$.storeId}, count=${$store$$.session.afterCommit.length}`)):Promise.resolve().then($cb$$).catch($err$$=>console.error("[onAfterCommit immediate error]",$err$$))};
10
- exports.getAfterCommitList=function(){const $store$$=storage.getStore();return $store$$&&$store$$.session?$store$$.session.afterCommit||[]:[]};exports.runWithoutSession=async function($fn$$){return storage.run({session:void 0},$fn$$)};exports.getCurrentSession=function(){const $store$$=storage.getStore();return $store$$?$store$$.session:void 0};
11
- exports.isModelExcludedFromSession=function($model_name$$){$model_name$$=typeof $model_name$$==="string"?$model_name$$:$model_name$$?.modelName;return!(!$model_name$$||!SESSION_EXCLUDE_MODELS.includes($model_name$$))};exports.isSessionActive=function($session$$){if(!$session$$)return!1;try{return $session$$.inTransaction()}catch{return!1}};console.log("[sessionContext] \u2705 Loaded with model exclude & safety check");
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");