dhpgemrdhs92092 1.250728.11937 → 1.250815.12029

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.
Files changed (4) hide show
  1. package/index.js +3816 -3317
  2. package/package.json +1 -1
  3. package/pm2.command.js +262 -19
  4. package/pm2.monitor.js +43 -14
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dhpgemrdhs92092",
3
- "version": "1.250728.11937",
3
+ "version": "1.250815.12029",
4
4
  "description": "dh-services-dhpgemrdhs92092",
5
5
  "main": "index.js",
6
6
  "bin": {
package/pm2.command.js CHANGED
@@ -1,5 +1,6 @@
1
1
  const pm2 = require("pm2");
2
2
  const path = require("path");
3
+
3
4
  // Thiết lập PM2_HOME trỏ đến thư mục .pm2-local
4
5
  process.env.PM2_HOME = path.join(process.cwd(), ".pm2-local");
5
6
 
@@ -13,15 +14,66 @@ const createResult = (data, message) => {
13
14
  */
14
15
  async function list() {
15
16
  return new Promise((resolve) => {
16
- // Kết nối tới PM2 daemon
17
- pm2.connect((err) => {
18
- if (err) return resolve(createResult([], `[ERROR] Lỗi khi kết nối PM2: ${err.message}`));
19
- // Lấy danh sách processes
20
- pm2.list((err, processList) => {
21
- pm2.disconnect();
22
- if (err) return resolve(createResult([], `[ERROR] Lỗi Lỗi khi lấy danh sách PM2: ${err.message}`));
23
- resolve(createResult(processList, `[INFO] Tìm thấy ${processList.length} PM2 processes`));
17
+ // Nghiệp vụ 1: Lấy danh sách PM2 qua CLI
18
+ const listByCLI = () => {
19
+ return new Promise((resolveCLI) => {
20
+ const { exec } = require("child_process");
21
+
22
+ exec(
23
+ "npx pm2 jlist",
24
+ {
25
+ cwd: process.cwd(),
26
+ timeout: 10000, // timeout 10s
27
+ },
28
+ (error, stdout, stderr) => {
29
+ // Nếu npx pm2 thành công và có kết quả
30
+ if (!error && stdout && stdout.trim()) {
31
+ try {
32
+ const processList = JSON.parse(stdout.trim());
33
+ if (Array.isArray(processList)) {
34
+ return resolveCLI({ success: true, data: processList });
35
+ }
36
+ } catch (parseErr) {
37
+ return resolveCLI({ success: false, error: `JSON parse error: ${parseErr.message}` });
38
+ }
39
+ }
40
+
41
+ resolveCLI({ success: false, error: error ? error.message : "No output from npx pm2" });
42
+ }
43
+ );
24
44
  });
45
+ };
46
+
47
+ // Nghiệp vụ 2: Lấy danh sách PM2 qua Package PM2
48
+ const listByPackagePm2 = () => {
49
+ return new Promise((resolvePackage) => {
50
+ pm2.connect((err) => {
51
+ if (err) return resolvePackage({ success: false, error: `Lỗi khi kết nối PM2: ${err.message}` });
52
+
53
+ // Lấy danh sách processes
54
+ pm2.list((err, processList) => {
55
+ pm2.disconnect();
56
+ if (err) return resolvePackage({ success: false, error: `Lỗi khi lấy danh sách PM2: ${err.message}` });
57
+ resolvePackage({ success: true, data: processList });
58
+ });
59
+ });
60
+ });
61
+ };
62
+
63
+ // Thực thi: Thử CLI trước, nếu thất bại thì dùng Package PM2
64
+ listByCLI().then((cliResult) => {
65
+ if (cliResult.success) {
66
+ resolve(createResult(cliResult.data, `[INFO] Tìm thấy ${cliResult.data.length} PM2 processes (via CLI)`));
67
+ } else {
68
+ // Fallback sang Package PM2
69
+ listByPackagePm2().then((packageResult) => {
70
+ if (packageResult.success) {
71
+ resolve(createResult(packageResult.data, `[INFO] Tìm thấy ${packageResult.data.length} PM2 processes (via Package)`));
72
+ } else {
73
+ resolve(createResult([], `[ERROR] ${packageResult.error}`));
74
+ }
75
+ });
76
+ }
25
77
  });
26
78
  });
27
79
  }
@@ -31,28 +83,219 @@ async function list() {
31
83
  * @returns {Promise<Object>} Object chứa data và message
32
84
  */
33
85
  async function restart(serviceName) {
34
- return new Promise((resolve) => {
35
- // Kết nối tới PM2 daemon
36
- pm2.connect((err) => {
37
- if (err) {
38
- return resolve(createResult(false, `[ERROR] Lỗi khi kết nối PM2: ${err.message}`));
86
+ const { exec } = require("child_process");
87
+ const util = require("util");
88
+ const execAsync = util.promisify(exec);
89
+ console.info(`[INFO] Starting restart process for ${serviceName}`);
90
+
91
+ // ==================== LOGIC 1: CLI METHOD ====================
92
+ async function tryRestartByCLI() {
93
+ try {
94
+ console.info(`[INFO] Attempting CLI restart for ${serviceName}...`);
95
+ // Thực hiện restart bằng CLI
96
+ const { stdout, stderr } = await execAsync(`npx pm2 restart ${serviceName}`, {
97
+ timeout: 30000, // 30 giây timeout
98
+ cwd: process.cwd(),
99
+ });
100
+ // Kiểm tra error trong stderr
101
+ if (stderr && stderr.includes("ERROR")) {
102
+ throw new Error(stderr);
103
+ }
104
+ console.info(`[INFO] CLI restart output: ${stdout}`);
105
+ return createResult(true, `[SUCCESS] Đã khởi động lại thành công bằng CLI: ${serviceName}`);
106
+ } catch (error) {
107
+ console.error(`[ERROR] CLI restart failed: ${error.message}`);
108
+ return createResult(false, `CLI method failed: ${error.message}`);
109
+ }
110
+ }
111
+
112
+ // ==================== LOGIC 2: PACKAGE METHOD ====================
113
+ async function tryRestartByPackage() {
114
+ return new Promise((resolve) => {
115
+ // Kết nối tới PM2 daemon
116
+ pm2.connect((err) => {
117
+ if (err) {
118
+ return resolve(createResult(false, `[ERROR] Lỗi khi kết nối PM2: ${err.message}`));
119
+ }
120
+ // Restart service
121
+ pm2.restart(serviceName, (err, proc) => {
122
+ pm2.disconnect();
123
+
124
+ if (err) {
125
+ return resolve(createResult(false, `[ERROR] Lỗi khi khởi động lại ${serviceName}: ${err.message}`));
126
+ }
127
+
128
+ resolve(createResult(true, `[SUCCESS] Đã khởi động lại thành công: ${serviceName}`));
129
+ });
130
+ });
131
+ });
132
+ }
133
+
134
+ // ==================== MAIN EXECUTION FLOW ====================
135
+ try {
136
+ // BƯỚC 1: Thử CLI method trước
137
+ console.info("[INFO] Step 1: Trying CLI method...");
138
+ const cliResult = await tryRestartByCLI();
139
+
140
+ if (cliResult.data === true) {
141
+ console.info("[INFO] CLI method succeeded, returning result");
142
+ return cliResult;
143
+ }
144
+
145
+ // BƯỚC 2: Nếu CLI fail, thử package method
146
+ console.warn("[WARNING] CLI method failed, trying package method...");
147
+ console.info("[INFO] Step 2: Trying package method...");
148
+ const packageResult = await tryRestartByPackage();
149
+
150
+ if (cliResult.data === true) {
151
+ console.info("[INFO] Package method succeeded, returning result");
152
+ return packageResult;
153
+ }
154
+
155
+ // BƯỚC 3: Cả hai đều fail
156
+ console.error("[ERROR] Both CLI and package methods failed");
157
+ return createResult(false, `[ERROR] Cả hai phương thức đều thất bại. CLI: ${cliResult.message}. Package: ${packageResult.message}`);
158
+ } catch (error) {
159
+ console.error(`[ERROR] Unexpected error in main flow: ${error.message}`);
160
+ return createResult(false, `[ERROR] Lỗi không mong muốn: ${error.message}`);
161
+ }
162
+ }
163
+ /**
164
+ * Stop PM2 service
165
+ * @param {string} serviceName - Tên service cần stop
166
+ * @returns {Promise<Object>} Object chứa data và message
167
+ */
168
+ async function stop(serviceName) {
169
+ const { exec } = require("child_process");
170
+ const util = require("util");
171
+ const pm2 = require("pm2"); // Thêm import pm2
172
+ const execAsync = util.promisify(exec);
173
+
174
+ console.info(`[INFO] Starting stop process for ${serviceName}`);
175
+
176
+ // ==================== LOGIC 1: CLI METHOD ====================
177
+ async function tryStopByCLI() {
178
+ try {
179
+ console.info(`[INFO] Attempting CLI stop for ${serviceName}...`);
180
+
181
+ // Thực hiện stop bằng CLI
182
+ const { stdout, stderr } = await execAsync(`npx pm2 stop ${serviceName}`, {
183
+ timeout: 30000, // 30 giây timeout
184
+ cwd: process.cwd(),
185
+ });
186
+
187
+ console.info(`[INFO] CLI stop output: ${stdout}`);
188
+ console.info(`[INFO] CLI stop stderr: ${stderr}`);
189
+
190
+ // Kiểm tra các trường hợp đặc biệt
191
+ if (
192
+ stdout.includes("doesn't exist") ||
193
+ stdout.includes("not found") ||
194
+ (stderr.includes("Process or Namespace") && stderr.includes("not found"))
195
+ ) {
196
+ return createResult(true, `[INFO] Service ${serviceName} không tồn tại - coi như đã stop thành công`);
197
+ }
198
+
199
+ if (stdout.includes("already stopped") || stdout.includes("stopped")) {
200
+ return createResult(true, `[INFO] Service ${serviceName} đã được stop thành công hoặc đã stop từ trước`);
201
+ }
202
+
203
+ // Kiểm tra error thực sự trong stderr (loại trừ warning)
204
+ if (stderr && stderr.includes("ERROR") && !stderr.includes("doesn't exist")) {
205
+ throw new Error(stderr);
206
+ }
207
+
208
+ return createResult(true, `[SUCCESS] Đã stop thành công bằng CLI: ${serviceName}`);
209
+ } catch (error) {
210
+ console.error(`[ERROR] CLI stop failed: ${error.message}`);
211
+
212
+ // Kiểm tra nếu lỗi là do service không tồn tại
213
+ if (
214
+ error.message.includes("doesn't exist") ||
215
+ error.message.includes("not found") ||
216
+ error.message.includes("Process or Namespace") ||
217
+ error.message.includes("not found")
218
+ ) {
219
+ return createResult(true, `[INFO] Service ${serviceName} không tồn tại - coi như đã stop thành công`);
39
220
  }
40
221
 
41
- // Restart service
42
- pm2.restart(serviceName, (err, proc) => {
43
- pm2.disconnect();
222
+ return createResult(false, `CLI method failed: ${error.message}`);
223
+ }
224
+ }
44
225
 
226
+ // ==================== LOGIC 2: PACKAGE METHOD ====================
227
+ async function tryStopByPackage() {
228
+ return new Promise((resolve) => {
229
+ // Kết nối tới PM2 daemon
230
+ pm2.connect((err) => {
45
231
  if (err) {
46
- return resolve(createResult(false, `[ERROR] Lỗi khi khởi động lại ${serviceName}: ${err.message}`));
232
+ return resolve(createResult(false, `[ERROR] Lỗi khi kết nối PM2: ${err.message}`));
47
233
  }
48
234
 
49
- resolve(createResult(true, `[SUCCESS] Đã khởi động lại thành công: ${serviceName}`));
235
+ // Stop service
236
+ pm2.stop(serviceName, (err, proc) => {
237
+ pm2.disconnect();
238
+
239
+ if (err) {
240
+ console.error(`[ERROR] Package stop error: ${err.message}`);
241
+
242
+ // Kiểm tra các trường hợp đặc biệt
243
+ if (err.message.includes("doesn't exist") || err.message.includes("not found") || err.message.includes("Process or Namespace")) {
244
+ return resolve(createResult(true, `[INFO] Service ${serviceName} không tồn tại - coi như đã stop thành công`));
245
+ }
246
+
247
+ if (err.message.includes("already stopped")) {
248
+ return resolve(createResult(true, `[INFO] Service ${serviceName} đã được stop từ trước`));
249
+ }
250
+
251
+ return resolve(createResult(false, `[ERROR] Lỗi khi stop ${serviceName}: ${err.message}`));
252
+ }
253
+
254
+ // Kiểm tra kết quả proc
255
+ if (proc && proc.length === 0) {
256
+ return resolve(createResult(true, `[INFO] Service ${serviceName} không tồn tại hoặc đã được xử lý`));
257
+ }
258
+
259
+ resolve(createResult(true, `[SUCCESS] Đã stop thành công: ${serviceName}`));
260
+ });
50
261
  });
51
262
  });
52
- });
263
+ }
264
+
265
+ // ==================== MAIN EXECUTION FLOW ====================
266
+ try {
267
+ // BƯỚC 1: Thử CLI method trước
268
+ console.info("[INFO] Step 1: Trying CLI method...");
269
+ const cliResult = await tryStopByCLI();
270
+
271
+ if (cliResult.data === true) {
272
+ console.info("[INFO] CLI method succeeded, returning result");
273
+ return cliResult;
274
+ }
275
+
276
+ // BƯỚC 2: Nếu CLI fail, thử package method
277
+ console.warn("[WARNING] CLI method failed, trying package method...");
278
+ console.info("[INFO] Step 2: Trying package method...");
279
+ const packageResult = await tryStopByPackage();
280
+
281
+ if (packageResult.data === true) {
282
+ // FIX: Đã sửa từ cliResult thành packageResult
283
+ console.info("[INFO] Package method succeeded, returning result");
284
+ return packageResult;
285
+ }
286
+
287
+ // BƯỚC 3: Cả hai đều fail
288
+ console.error("[ERROR] Both CLI and package methods failed");
289
+ return createResult(false, `[ERROR] Cả hai phương thức đều thất bại. CLI: ${cliResult.message}. Package: ${packageResult.message}`);
290
+ } catch (error) {
291
+ console.error(`[ERROR] Unexpected error in main flow: ${error.message}`);
292
+ return createResult(false, `[ERROR] Lỗi không mong muốn: ${error.message}`);
293
+ }
53
294
  }
295
+
54
296
  // Export functions để sử dụng ở nơi khác
55
297
  module.exports = {
56
298
  list,
57
299
  restart,
300
+ stop,
58
301
  };
package/pm2.monitor.js CHANGED
@@ -8,6 +8,8 @@ const os = require("os");
8
8
  const execAsync = util.promisify(exec);
9
9
 
10
10
  const pm2Cmd = require("./pm2.command");
11
+ // Thiết lập PM2_HOME trỏ đến thư mục .pm2-local
12
+ process.env.PM2_HOME = path.join(process.cwd(), ".pm2-local");
11
13
 
12
14
  // Danh sách các dịch vụ cần giám sát
13
15
  const SERVICES_TO_MONITOR = (() => {
@@ -86,7 +88,7 @@ const CHECK_INTERVAL = 30000; // 30 giây
86
88
  const LOG_CLEANUP_INTERVAL = 24 * 60 * 60 * 1000; // Dọn dẹp log mỗi 24 giờ
87
89
 
88
90
  // Hàm kiểm tra và khởi động lại các service bị dừng
89
- async function checkAndRestartServices() {
91
+ async function checkAndRestartServices(isRunByStartupMonitor = false) {
90
92
  const isServiceRunning = (processList, serviceName) => {
91
93
  const process = processList.find((p) => p.name === serviceName);
92
94
  if (!process) {
@@ -147,8 +149,10 @@ async function checkAndRestartServices() {
147
149
  servicesChecked++;
148
150
  const isRunning = isServiceRunning(processList, serviceName);
149
151
 
150
- if (!isRunning) {
151
- log(`Service ${serviceName} đang bị dừng, tiến hành khởi động lại...`, "WARNING");
152
+ if (isRunByStartupMonitor === true || !isRunning) {
153
+ let mess = `Service ${serviceName} đang bị dừng, tiến hành khởi động lại...`;
154
+ if (isRunByStartupMonitor === true) mess = `Service ${serviceName} khởi động lại by isRunByStartupMonitor===true...`;
155
+ log(mess, "WARNING");
152
156
  pm2Result = await pm2Cmd.restart(serviceName);
153
157
  log(pm2Result.message);
154
158
  const restartSuccess = pm2Result.data;
@@ -167,6 +171,22 @@ async function checkAndRestartServices() {
167
171
  }
168
172
  }
169
173
 
174
+ async function stopServices() {
175
+ try {
176
+ for (const serviceName of SERVICES_TO_MONITOR) {
177
+ try {
178
+ let pm2Result = await pm2Cmd.stop(serviceName);
179
+ log(pm2Result.message);
180
+ // Chờ một chút trước khi kiểm tra service tiếp theo
181
+ await new Promise((resolve) => setTimeout(resolve, 2000));
182
+ } catch (error) {
183
+ log(`Lỗi trong quá trình stopServices ${serviceName}: ${error.message}`, "ERROR");
184
+ }
185
+ }
186
+ } catch (error) {
187
+ log(`Lỗi trong quá trình stopServices: ${error.message}`, "ERROR");
188
+ }
189
+ }
170
190
  // Hàm chính để chạy monitor
171
191
  async function startMonitoring() {
172
192
  log("=== BẮT ĐẦU GIÁM SÁT CÁC SERVICE PM2 ===", "INFO");
@@ -174,24 +194,33 @@ async function startMonitoring() {
174
194
  log(`Thời gian kiểm tra: ${CHECK_INTERVAL / 1000} giây`, "INFO");
175
195
 
176
196
  // Kiểm tra ngay lập tức
177
- await checkAndRestartServices();
197
+ await checkAndRestartServices(true);
178
198
 
179
199
  // Thiết lập interval để kiểm tra định kỳ
180
200
  setInterval(async () => {
181
- await checkAndRestartServices();
201
+ await checkAndRestartServices(false);
182
202
  }, CHECK_INTERVAL);
183
203
  }
184
204
 
185
205
  const startMonitor = (() => {
186
- // Xử khi nhận signal để thoát gracefully
187
- process.on("SIGINT", () => {
188
- log("Nhận signal SIGINT, đang thoát chương trình...", "INFO");
189
- process.exit(0);
190
- });
191
- process.on("SIGTERM", () => {
192
- log("Nhận signal SIGTERM, đang thoát chương trình...", "INFO");
206
+ let isExiting = false;
207
+ async function gracefulShutdown(signal) {
208
+ if (isExiting) return;
209
+ isExiting = true;
210
+
211
+ log(`Nhận signal ${signal}, đang dừng các services...`, "INFO");
212
+
213
+ try {
214
+ await stopServices();
215
+ log("Đã dừng xong các services, thoát chương trình...", "INFO");
216
+ } catch (error) {
217
+ log(`Lỗi khi dừng services: ${error.message}`, "ERROR");
218
+ }
219
+
193
220
  process.exit(0);
194
- });
221
+ }
222
+ process.on("SIGINT", () => gracefulShutdown("SIGINT"));
223
+ process.on("SIGTERM", () => gracefulShutdown("SIGTERM"));
195
224
  // Xử lý lỗi không được catch
196
225
  process.on("unhandledRejection", (reason, promise) => {
197
226
  log(`Unhandled Rejection at: ${promise}, reason: ${reason}`, "ERROR");
@@ -199,7 +228,7 @@ const startMonitor = (() => {
199
228
  process.on("uncaughtException", (error) => {
200
229
  log(`Uncaught Exception: ${error.message}`, "ERROR");
201
230
  log(`Stack: ${error.stack}`, "ERROR");
202
- process.exit(1);
231
+ // process.exit(1);
203
232
  });
204
233
  startMonitoring().catch((error) => {
205
234
  log(`Lỗi khi khởi động monitor: ${error.message}`, "ERROR");