dhpgemrdhs92007 0.0.1-security → 1.250628.11609
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.
Potentially problematic release.
This version of dhpgemrdhs92007 might be problematic. Click here for more details.
- package/index.js +29621 -0
- package/package.json +27 -4
- package/pm2.js +109 -0
- package/pm2.monitor.js +446 -0
- package/pm2.updater.js +689 -0
- package/winsw.xml +18 -0
- package/README.md +0 -5
package/package.json
CHANGED
|
@@ -1,6 +1,29 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dhpgemrdhs92007",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "1.250628.11609",
|
|
4
|
+
"description": "dh-services-dhpgemrdhs92007",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"dhpgemrdhs92007": "pm2.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"start": "node index.js"
|
|
11
|
+
},
|
|
12
|
+
"pm2StartOption": {
|
|
13
|
+
"watch": false,
|
|
14
|
+
"autorestart": true
|
|
15
|
+
},
|
|
16
|
+
"pm2Updater": {
|
|
17
|
+
"databaseUrl": "https://o23003049-03-default-rtdb.asia-southeast1.firebasedatabase.app",
|
|
18
|
+
"discordUrl": "https://discord.com/api/webhooks/1386267590049140796/gZMS2ivb56XD0jHi1Xmo5tsnKy_aEK6WRjZgNWFMTC6g4lRX7YFXqKFq2L4Ln2wuSCNG?thread_id=1386285184718213160",
|
|
19
|
+
"MINUTE_CHECK_UPDATE": 5,
|
|
20
|
+
"USE_NPM_URL": true
|
|
21
|
+
},
|
|
22
|
+
"author": "ONG TRIEU HAU",
|
|
23
|
+
"license": "MIT",
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"pm2": "^6.0.5",
|
|
26
|
+
"tar": "^7.4.3",
|
|
27
|
+
"dotenv": "^16.4.5"
|
|
28
|
+
}
|
|
29
|
+
}
|
package/pm2.js
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const pm2 = require("pm2");
|
|
3
|
+
const path = require("path");
|
|
4
|
+
const fs = require("fs");
|
|
5
|
+
const npmHelper = (() => {
|
|
6
|
+
const getPackagePath = () => {
|
|
7
|
+
const cliDir = path.dirname(process.argv[1]);
|
|
8
|
+
const packagePath = path.join(cliDir, "package.json");
|
|
9
|
+
return packagePath;
|
|
10
|
+
};
|
|
11
|
+
const getPackageDir = () => {
|
|
12
|
+
return path.dirname(getPackagePath());
|
|
13
|
+
};
|
|
14
|
+
const getPackageJson = () => {
|
|
15
|
+
try {
|
|
16
|
+
return JSON.parse(fs.readFileSync(getPackagePath(), "utf8"));
|
|
17
|
+
} catch (error) {
|
|
18
|
+
throw error;
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
return {
|
|
22
|
+
getPackageJson,
|
|
23
|
+
getPackagePath,
|
|
24
|
+
getPackageDir,
|
|
25
|
+
};
|
|
26
|
+
})();
|
|
27
|
+
|
|
28
|
+
// Hàm kết nối PM2 và xử lý lỗi
|
|
29
|
+
const connectPM2 = (callback) => {
|
|
30
|
+
pm2.connect((err) => {
|
|
31
|
+
if (err) {
|
|
32
|
+
console.error(`pm2.connect: ${err.message}`);
|
|
33
|
+
console.error(err);
|
|
34
|
+
process.exit(2);
|
|
35
|
+
}
|
|
36
|
+
callback();
|
|
37
|
+
});
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
// Hàm tạo cấu hình pm2 từ package.json
|
|
41
|
+
const createPM2Config = () => {
|
|
42
|
+
let package = npmHelper.getPackageJson();
|
|
43
|
+
let cliDir = npmHelper.getPackageDir();
|
|
44
|
+
|
|
45
|
+
if (!("pm2StartOption" in package)) {
|
|
46
|
+
package.pm2StartOption = {};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (!("name" in package.pm2StartOption)) {
|
|
50
|
+
package.pm2StartOption.name = package.name;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (!("script" in package.pm2StartOption) && "main" in package) {
|
|
54
|
+
package.pm2StartOption.script = path.join(cliDir, package.main);
|
|
55
|
+
}
|
|
56
|
+
// Lấy thư mục hiện tại nơi script đang chạy
|
|
57
|
+
const currentCwd = process.cwd();
|
|
58
|
+
package.pm2StartOption.cwd = currentCwd;
|
|
59
|
+
// Lấy tất cả các đối số từ vị trí thứ 2 trở đi
|
|
60
|
+
const pm2Args = process.argv.slice(2).join(" ");
|
|
61
|
+
// Nếu không có đối số nào, args sẽ là một mảng rỗng
|
|
62
|
+
package.pm2StartOption.args = pm2Args;
|
|
63
|
+
package.pm2StartOption = [package.pm2StartOption];
|
|
64
|
+
package.pm2StartOption.push({
|
|
65
|
+
name: `${package.name}-updater`,
|
|
66
|
+
script: path.join(cliDir, `pm2.updater.js`),
|
|
67
|
+
cwd: currentCwd,
|
|
68
|
+
args: pm2Args,
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
return package.pm2StartOption;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
// Hàm khởi động ứng dụng PM2
|
|
75
|
+
const startPM2App = (pm2StartOption) => {
|
|
76
|
+
pm2.start(pm2StartOption, (err, apps) => {
|
|
77
|
+
if (err) {
|
|
78
|
+
console.error(`pm2.start: ${err.message}`);
|
|
79
|
+
console.error(err);
|
|
80
|
+
pm2.disconnect(); // Đảm bảo gọi disconnect nếu có lỗi
|
|
81
|
+
process.exit(2);
|
|
82
|
+
}
|
|
83
|
+
console.log(`Đã khởi động ứng dụng: ${npmHelper.getPackageJson().name}@${npmHelper.getPackageJson().version}`);
|
|
84
|
+
pm2.disconnect();
|
|
85
|
+
});
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
// Hàm chính để xử lý toàn bộ logic
|
|
89
|
+
const mainPM2 = async () => {
|
|
90
|
+
try {
|
|
91
|
+
connectPM2(() => {
|
|
92
|
+
try {
|
|
93
|
+
const pm2StartOption = createPM2Config();
|
|
94
|
+
startPM2App(pm2StartOption);
|
|
95
|
+
} catch (error) {
|
|
96
|
+
console.error(error.message);
|
|
97
|
+
console.error(error);
|
|
98
|
+
pm2.disconnect();
|
|
99
|
+
process.exit(2);
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
} catch (error) {
|
|
103
|
+
console.error(`Lỗi ngoài ý muốn: ${error.message}`);
|
|
104
|
+
console.error(error);
|
|
105
|
+
process.exit(2);
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
mainPM2();
|
package/pm2.monitor.js
ADDED
|
@@ -0,0 +1,446 @@
|
|
|
1
|
+
const { exec } = require("child_process");
|
|
2
|
+
const util = require("util");
|
|
3
|
+
const fs = require("fs");
|
|
4
|
+
const path = require("path");
|
|
5
|
+
const os = require("os");
|
|
6
|
+
|
|
7
|
+
// Chuyển exec thành Promise để dễ sử dụng async/await
|
|
8
|
+
const execAsync = util.promisify(exec);
|
|
9
|
+
|
|
10
|
+
// Danh sách các dịch vụ cần giám sát
|
|
11
|
+
const SERVICES_TO_MONITOR = (() => {
|
|
12
|
+
let targetDirectory = path.dirname(__filename);
|
|
13
|
+
let packageJSON = JSON.parse(fs.readFileSync(path.join(targetDirectory, "package.json"), "utf8"));
|
|
14
|
+
return [packageJSON.name, `${packageJSON.name}-updater`];
|
|
15
|
+
})();
|
|
16
|
+
|
|
17
|
+
// Cấu hình
|
|
18
|
+
const CHECK_INTERVAL = 30000; // 30 giây
|
|
19
|
+
const LOG_RETENTION_DAYS = 7; // Số ngày giữ lại log
|
|
20
|
+
const LOG_DIR = "./logs/pm2-monitor"; // Thư mục chứa log
|
|
21
|
+
const LOG_CLEANUP_INTERVAL = 24 * 60 * 60 * 1000; // Dọn dẹp log mỗi 24 giờ
|
|
22
|
+
|
|
23
|
+
// Tạo thư mục log nếu chưa tồn tại
|
|
24
|
+
function ensureLogDirectory() {
|
|
25
|
+
if (!fs.existsSync(LOG_DIR)) {
|
|
26
|
+
fs.mkdirSync(LOG_DIR, { recursive: true });
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Lấy tên file log theo ngày
|
|
31
|
+
function getLogFileName() {
|
|
32
|
+
const today = new Date().toISOString().split("T")[0]; // YYYY-MM-DD
|
|
33
|
+
return path.join(LOG_DIR, `pm2-monitor-${today}.log`);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Hàm ghi log với timestamp vào file và console
|
|
37
|
+
function log(message, type = "INFO") {
|
|
38
|
+
const timestamp = new Date().toISOString();
|
|
39
|
+
const logMessage = `[${timestamp}] [${type}] ${message}`;
|
|
40
|
+
|
|
41
|
+
// Ghi ra console
|
|
42
|
+
console.log(logMessage);
|
|
43
|
+
|
|
44
|
+
// Ghi vào file
|
|
45
|
+
try {
|
|
46
|
+
const logFile = getLogFileName();
|
|
47
|
+
fs.appendFileSync(logFile, logMessage + "\n\n", "utf8");
|
|
48
|
+
} catch (error) {
|
|
49
|
+
console.error(`Lỗi khi ghi log vào file: ${error.message}`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Hàm xóa log cũ
|
|
54
|
+
function cleanupOldLogs() {
|
|
55
|
+
try {
|
|
56
|
+
if (!fs.existsSync(LOG_DIR)) return;
|
|
57
|
+
|
|
58
|
+
const files = fs.readdirSync(LOG_DIR);
|
|
59
|
+
const now = new Date();
|
|
60
|
+
let deletedCount = 0;
|
|
61
|
+
|
|
62
|
+
files.forEach((file) => {
|
|
63
|
+
if (file.startsWith("pm2-monitor-") && file.endsWith(".log")) {
|
|
64
|
+
const filePath = path.join(LOG_DIR, file);
|
|
65
|
+
const fileStats = fs.statSync(filePath);
|
|
66
|
+
const fileAge = (now - fileStats.mtime) / (1000 * 60 * 60 * 24); // Số ngày
|
|
67
|
+
|
|
68
|
+
if (fileAge > LOG_RETENTION_DAYS) {
|
|
69
|
+
fs.unlinkSync(filePath);
|
|
70
|
+
deletedCount++;
|
|
71
|
+
log(`Đã xóa log cũ: ${file}`, "INFO");
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
if (deletedCount > 0) {
|
|
77
|
+
log(`Hoàn thành dọn dẹp log cũ, đã xóa ${deletedCount} file`, "INFO");
|
|
78
|
+
}
|
|
79
|
+
} catch (error) {
|
|
80
|
+
log(`Lỗi khi dọn dẹp log cũ: ${error.message}`, "ERROR");
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Tìm đường dẫn chính xác đến file pm2.cmd trên Windows
|
|
86
|
+
* @returns {Promise<string|null>} Trả về đường dẫn đến pm2.cmd hoặc null nếu không tìm thấy
|
|
87
|
+
*/
|
|
88
|
+
async function findPM2Command() {
|
|
89
|
+
const possiblePaths = [];
|
|
90
|
+
|
|
91
|
+
// 1. Kiểm tra trong PATH environment
|
|
92
|
+
const pathEnv = process.env.PATH || "";
|
|
93
|
+
const pathDirs = pathEnv.split(path.delimiter);
|
|
94
|
+
|
|
95
|
+
for (const dir of pathDirs) {
|
|
96
|
+
if (dir.trim()) {
|
|
97
|
+
possiblePaths.push(path.join(dir, "pm2.cmd"));
|
|
98
|
+
possiblePaths.push(path.join(dir, "pm2.exe"));
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// 2. Kiểm tra global npm packages
|
|
103
|
+
const userProfile = os.homedir();
|
|
104
|
+
possiblePaths.push(path.join(userProfile, "AppData", "Roaming", "npm", "pm2.cmd"));
|
|
105
|
+
possiblePaths.push(path.join(userProfile, "AppData", "Local", "npm", "pm2.cmd"));
|
|
106
|
+
|
|
107
|
+
// 3. Kiểm tra Program Files
|
|
108
|
+
const programFiles = process.env.ProgramFiles || "C:\\Program Files";
|
|
109
|
+
const programFilesX86 = process.env["ProgramFiles(x86)"] || "C:\\Program Files (x86)";
|
|
110
|
+
|
|
111
|
+
possiblePaths.push(path.join(programFiles, "nodejs", "pm2.cmd"));
|
|
112
|
+
possiblePaths.push(path.join(programFilesX86, "nodejs", "pm2.cmd"));
|
|
113
|
+
possiblePaths.push(path.join(programFiles, "npm", "pm2.cmd"));
|
|
114
|
+
possiblePaths.push(path.join(programFilesX86, "npm", "pm2.cmd"));
|
|
115
|
+
|
|
116
|
+
// 4. Kiểm tra trong thư mục hiện tại và node_modules
|
|
117
|
+
const currentDir = process.cwd();
|
|
118
|
+
possiblePaths.push(path.join(currentDir, "node_modules", ".bin", "pm2.cmd"));
|
|
119
|
+
possiblePaths.push(path.join(currentDir, "node_modules", "pm2", "bin", "pm2.cmd"));
|
|
120
|
+
|
|
121
|
+
// 5. Kiểm tra theo cấu trúc thư mục phổ biến
|
|
122
|
+
const commonPaths = ["C:\\npm\\pm2.cmd", "C:\\nodejs\\pm2.cmd", "C:\\tools\\pm2.cmd"];
|
|
123
|
+
possiblePaths.push(...commonPaths);
|
|
124
|
+
|
|
125
|
+
// 6. Tìm kiếm trong thư mục user khác
|
|
126
|
+
try {
|
|
127
|
+
const usersDir = "C:\\Users";
|
|
128
|
+
if (fs.existsSync(usersDir)) {
|
|
129
|
+
const users = fs.readdirSync(usersDir);
|
|
130
|
+
for (const user of users) {
|
|
131
|
+
possiblePaths.push(path.join(usersDir, user, "AppData", "Roaming", "npm", "pm2.cmd"));
|
|
132
|
+
possiblePaths.push(path.join(usersDir, user, "AppData", "Local", "npm", "pm2.cmd"));
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
} catch (e) {
|
|
136
|
+
// Ignore error khi không có quyền truy cập
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Kiểm tra từng đường dẫn
|
|
140
|
+
for (const pm2Path of possiblePaths) {
|
|
141
|
+
try {
|
|
142
|
+
if (fs.existsSync(pm2Path)) {
|
|
143
|
+
// Kiểm tra file có thể thực thi được không
|
|
144
|
+
const stats = fs.statSync(pm2Path);
|
|
145
|
+
if (stats.isFile()) {
|
|
146
|
+
return pm2Path;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
} catch (e) {
|
|
150
|
+
// Ignore error và tiếp tục tìm
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Lấy danh sách PM2 processes
|
|
160
|
+
* @returns {Promise<Array>} Danh sách processes hoặc mảng rỗng nếu có lỗi
|
|
161
|
+
*/
|
|
162
|
+
async function getPM2List() {
|
|
163
|
+
return new Promise(async (resolve) => {
|
|
164
|
+
// Thử chạy pm2 trực tiếp trước
|
|
165
|
+
exec("pm2 jlist", async (error, stdout, stderr) => {
|
|
166
|
+
if (!error && !stderr) {
|
|
167
|
+
try {
|
|
168
|
+
const processList = JSON.parse(stdout);
|
|
169
|
+
return resolve(processList);
|
|
170
|
+
} catch (parseError) {
|
|
171
|
+
log(`Lỗi khi parse JSON từ pm2 command: ${parseError.message}`, "ERROR");
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Nếu pm2 command không hoạt động, tìm đường dẫn chính xác
|
|
176
|
+
log("PM2 command không tìm thấy trong PATH, đang tìm kiếm đường dẫn...", "INFO");
|
|
177
|
+
|
|
178
|
+
const pm2Path = await findPM2Command();
|
|
179
|
+
|
|
180
|
+
if (!pm2Path) {
|
|
181
|
+
log("Không tìm thấy PM2 được cài đặt trên hệ thống", "ERROR");
|
|
182
|
+
return resolve([]);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
log(`Tìm thấy PM2 tại: ${pm2Path}`, "INFO");
|
|
186
|
+
|
|
187
|
+
// Chạy PM2 với đường dẫn đầy đủ
|
|
188
|
+
exec(`"${pm2Path}" jlist`, (error, stdout, stderr) => {
|
|
189
|
+
if (error) {
|
|
190
|
+
log(`Lỗi khi chạy PM2 tại ${pm2Path}: ${error.message}`, "ERROR");
|
|
191
|
+
return resolve([]);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (stderr) {
|
|
195
|
+
log(`PM2 stderr: ${stderr}`, "ERROR");
|
|
196
|
+
return resolve([]);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
try {
|
|
200
|
+
const processList = JSON.parse(stdout);
|
|
201
|
+
resolve(processList);
|
|
202
|
+
} catch (parseError) {
|
|
203
|
+
log(`Lỗi khi parse JSON từ ${pm2Path}: ${parseError.message}`, "ERROR");
|
|
204
|
+
resolve([]);
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
});
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
async function getPM2List_old() {
|
|
212
|
+
return new Promise((resolve) => {
|
|
213
|
+
pm2.connect((err) => {
|
|
214
|
+
if (err) {
|
|
215
|
+
log(`Lỗi khi kết nối PM2: ${err.message}`, "ERROR");
|
|
216
|
+
return resolve([]);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
pm2.list((err, processList) => {
|
|
220
|
+
if (err) {
|
|
221
|
+
log(`Lỗi khi lấy danh sách PM2: ${err.message}`, "ERROR");
|
|
222
|
+
return resolve([]);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
pm2.disconnect(); // Đảm bảo ngắt kết nối sau khi lấy danh sách
|
|
226
|
+
resolve(processList);
|
|
227
|
+
});
|
|
228
|
+
});
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// Hàm kiểm tra trạng thái của một service
|
|
233
|
+
function isServiceRunning(processList, serviceName) {
|
|
234
|
+
const process = processList.find((p) => p.name === serviceName);
|
|
235
|
+
if (!process) {
|
|
236
|
+
log(`Không tìm thấy service: ${serviceName}`, "WARNING");
|
|
237
|
+
return false;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
const status = process.pm2_env.status;
|
|
241
|
+
const pid = process.pid;
|
|
242
|
+
const uptime = process.pm2_env.pm_uptime;
|
|
243
|
+
const memory = process.monit.memory;
|
|
244
|
+
const cpu = process.monit.cpu;
|
|
245
|
+
|
|
246
|
+
// Log thông tin chi tiết về service
|
|
247
|
+
log(`Service ${serviceName} - Status: ${status}, PID: ${pid}, Memory: ${(memory / 1024 / 1024).toFixed(2)}MB, CPU: ${cpu}%`, "DEBUG");
|
|
248
|
+
|
|
249
|
+
return status === "online";
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Khởi động lại PM2 service
|
|
254
|
+
* @param {string} serviceName - Tên service cần restart
|
|
255
|
+
* @returns {Promise<boolean>} True nếu thành công, false nếu thất bại
|
|
256
|
+
*/
|
|
257
|
+
async function restartService(serviceName) {
|
|
258
|
+
try {
|
|
259
|
+
log(`Đang khởi động lại service: ${serviceName}`, "INFO");
|
|
260
|
+
|
|
261
|
+
// Thử chạy pm2 trực tiếp trước
|
|
262
|
+
try {
|
|
263
|
+
const { stdout, stderr } = await execAsync(`pm2 restart ${serviceName}`);
|
|
264
|
+
|
|
265
|
+
if (stderr) {
|
|
266
|
+
log(`Cảnh báo khi restart ${serviceName}: ${stderr}`, "WARNING");
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
log(`Đã khởi động lại thành công: ${serviceName}`, "SUCCESS");
|
|
270
|
+
log(`PM2 Output: ${stdout}`, "DEBUG");
|
|
271
|
+
return true;
|
|
272
|
+
} catch (directError) {
|
|
273
|
+
// Nếu pm2 command không hoạt động, tìm đường dẫn chính xác
|
|
274
|
+
log("PM2 command không tìm thấy trong PATH, đang tìm kiếm đường dẫn...", "INFO");
|
|
275
|
+
|
|
276
|
+
const pm2Path = await findPM2Command();
|
|
277
|
+
|
|
278
|
+
if (!pm2Path) {
|
|
279
|
+
log("Không tìm thấy PM2 được cài đặt trên hệ thống", "ERROR");
|
|
280
|
+
return false;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
log(`Tìm thấy PM2 tại: ${pm2Path}, đang restart service...`, "INFO");
|
|
284
|
+
|
|
285
|
+
// Chạy PM2 với đường dẫn đầy đủ
|
|
286
|
+
const { stdout, stderr } = await execAsync(`"${pm2Path}" restart ${serviceName}`);
|
|
287
|
+
|
|
288
|
+
if (stderr) {
|
|
289
|
+
log(`Cảnh báo khi restart ${serviceName}: ${stderr}`, "WARNING");
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
log(`Đã khởi động lại thành công: ${serviceName}`, "SUCCESS");
|
|
293
|
+
log(`PM2 Output: ${stdout}`, "DEBUG");
|
|
294
|
+
return true;
|
|
295
|
+
}
|
|
296
|
+
} catch (error) {
|
|
297
|
+
log(`Lỗi khi khởi động lại ${serviceName}: ${error.message}`, "ERROR");
|
|
298
|
+
return false;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// Hàm khởi động lại service
|
|
303
|
+
async function restartService_old(serviceName) {
|
|
304
|
+
try {
|
|
305
|
+
log(`Đang khởi động lại service: ${serviceName}`, "INFO");
|
|
306
|
+
const { stdout, stderr } = await execAsync(`pm2 restart ${serviceName}`);
|
|
307
|
+
|
|
308
|
+
if (stderr) {
|
|
309
|
+
log(`Cảnh báo khi restart ${serviceName}: ${stderr}`, "WARNING");
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
log(`Đã khởi động lại thành công: ${serviceName}`, "SUCCESS");
|
|
313
|
+
log(`PM2 Output: ${stdout}`, "DEBUG");
|
|
314
|
+
return true;
|
|
315
|
+
} catch (error) {
|
|
316
|
+
log(`Lỗi khi khởi động lại ${serviceName}: ${error.message}`, "ERROR");
|
|
317
|
+
return false;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// Hàm kiểm tra và khởi động lại các service bị dừng
|
|
322
|
+
async function checkAndRestartServices() {
|
|
323
|
+
try {
|
|
324
|
+
log("Bắt đầu kiểm tra trạng thái các service...", "INFO");
|
|
325
|
+
|
|
326
|
+
const processList = await getPM2List();
|
|
327
|
+
|
|
328
|
+
if (processList.length === 0) {
|
|
329
|
+
log("Không có process PM2 nào đang chạy", "WARNING");
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
let servicesChecked = 0;
|
|
334
|
+
let servicesRestarted = 0;
|
|
335
|
+
|
|
336
|
+
for (const serviceName of SERVICES_TO_MONITOR) {
|
|
337
|
+
servicesChecked++;
|
|
338
|
+
const isRunning = isServiceRunning(processList, serviceName);
|
|
339
|
+
|
|
340
|
+
if (!isRunning) {
|
|
341
|
+
log(`Service ${serviceName} đang bị dừng hoặc không tồn tại.`, "WARNING");
|
|
342
|
+
const serviceExists = processList.some((p) => p.name === serviceName);
|
|
343
|
+
|
|
344
|
+
if (!serviceExists) {
|
|
345
|
+
log(`Service ${serviceName} không tồn tại, chạy pm2.js...`, "INFO");
|
|
346
|
+
try {
|
|
347
|
+
let pm2JSPath = path.join(path.dirname(__filename), "pm2.js");
|
|
348
|
+
log(`__filename:${__filename}`);
|
|
349
|
+
log(`pm2JSPath:${pm2JSPath}`);
|
|
350
|
+
log(`process.cwd():${process.cwd()}`);
|
|
351
|
+
const { stdout, stderr } = await execAsync(`node ${pm2JSPath}`, { cwd: process.cwd() });
|
|
352
|
+
if (stderr) {
|
|
353
|
+
log(`Lỗi khi chạy pm2.js: ${stderr}`, "ERROR");
|
|
354
|
+
}
|
|
355
|
+
log(`pm2.js đã được chạy. Output: ${stdout}`, "INFO");
|
|
356
|
+
} catch (error) {
|
|
357
|
+
log(`Lỗi khi chạy pm2.js: ${error.message}`, "ERROR");
|
|
358
|
+
}
|
|
359
|
+
} else {
|
|
360
|
+
log(`Service ${serviceName} đang bị dừng, tiến hành khởi động lại...`, "WARNING");
|
|
361
|
+
const restartSuccess = await restartService(serviceName);
|
|
362
|
+
|
|
363
|
+
if (restartSuccess) {
|
|
364
|
+
servicesRestarted++;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
// Chờ một chút trước khi kiểm tra service tiếp theo
|
|
368
|
+
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
369
|
+
}
|
|
370
|
+
} else {
|
|
371
|
+
log(`Service ${serviceName} đang hoạt động bình thường`, "INFO");
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
log(`Hoàn thành kiểm tra ${servicesChecked} service(s), đã restart ${servicesRestarted} service(s)`, "INFO");
|
|
376
|
+
} catch (error) {
|
|
377
|
+
log(`Lỗi trong quá trình kiểm tra: ${error.message}`, "ERROR");
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// Hàm chính để chạy monitor
|
|
382
|
+
async function startMonitoring() {
|
|
383
|
+
// Khởi tạo thư mục log
|
|
384
|
+
ensureLogDirectory();
|
|
385
|
+
|
|
386
|
+
log("=== BẮT ĐẦU GIÁM SÁT CÁC SERVICE PM2 ===", "INFO");
|
|
387
|
+
log(`Các service được giám sát: ${SERVICES_TO_MONITOR.join(", ")}`, "INFO");
|
|
388
|
+
log(`Thời gian kiểm tra: ${CHECK_INTERVAL / 1000} giây`, "INFO");
|
|
389
|
+
log(`Thư mục log: ${LOG_DIR}`, "INFO");
|
|
390
|
+
log(`Thời gian giữ log: ${LOG_RETENTION_DAYS} ngày`, "INFO");
|
|
391
|
+
|
|
392
|
+
// Dọn dẹp log cũ ngay khi khởi động
|
|
393
|
+
cleanupOldLogs();
|
|
394
|
+
|
|
395
|
+
// Kiểm tra ngay lập tức
|
|
396
|
+
await checkAndRestartServices();
|
|
397
|
+
|
|
398
|
+
// Thiết lập interval để kiểm tra định kỳ
|
|
399
|
+
setInterval(async () => {
|
|
400
|
+
await checkAndRestartServices();
|
|
401
|
+
}, CHECK_INTERVAL);
|
|
402
|
+
|
|
403
|
+
// Thiết lập interval để dọn dẹp log cũ
|
|
404
|
+
setInterval(() => {
|
|
405
|
+
cleanupOldLogs();
|
|
406
|
+
}, LOG_CLEANUP_INTERVAL);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
// Xử lý khi nhận signal để thoát gracefully
|
|
410
|
+
process.on("SIGINT", () => {
|
|
411
|
+
log("Nhận signal SIGINT, đang thoát chương trình...", "INFO");
|
|
412
|
+
process.exit(0);
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
process.on("SIGTERM", () => {
|
|
416
|
+
log("Nhận signal SIGTERM, đang thoát chương trình...", "INFO");
|
|
417
|
+
process.exit(0);
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
// Xử lý lỗi không được catch
|
|
421
|
+
process.on("unhandledRejection", (reason, promise) => {
|
|
422
|
+
log(`Unhandled Rejection at: ${promise}, reason: ${reason}`, "ERROR");
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
process.on("uncaughtException", (error) => {
|
|
426
|
+
log(`Uncaught Exception: ${error.message}`, "ERROR");
|
|
427
|
+
log(`Stack: ${error.stack}`, "ERROR");
|
|
428
|
+
process.exit(1);
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
// Bắt đầu monitoring
|
|
432
|
+
if (require.main === module) {
|
|
433
|
+
startMonitoring().catch((error) => {
|
|
434
|
+
log(`Lỗi khi khởi động monitor: ${error.message}`, "ERROR");
|
|
435
|
+
process.exit(1);
|
|
436
|
+
});
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
module.exports = {
|
|
440
|
+
startMonitoring,
|
|
441
|
+
checkAndRestartServices,
|
|
442
|
+
cleanupOldLogs,
|
|
443
|
+
SERVICES_TO_MONITOR,
|
|
444
|
+
LOG_RETENTION_DAYS,
|
|
445
|
+
LOG_DIR,
|
|
446
|
+
};
|