ezpm2gui 1.0.0 → 1.2.0
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/README.md +202 -14
- package/bin/ezpm2gui.js +40 -44
- package/bin/ezpm2gui.ts +51 -0
- package/bin/generate-ecosystem.js +24 -22
- package/bin/generate-ecosystem.ts +56 -0
- package/dist/server/config/project-configs.json +236 -0
- package/dist/server/index.js +64 -19
- package/dist/server/logs/deployment.log +12 -0
- package/dist/server/routes/clusterManagement.d.ts +3 -0
- package/dist/server/routes/clusterManagement.js +152 -0
- package/dist/server/routes/deployApplication.d.ts +3 -0
- package/dist/server/routes/deployApplication.js +310 -0
- package/dist/server/routes/logStreaming.d.ts +5 -0
- package/dist/server/routes/logStreaming.js +276 -0
- package/dist/server/routes/modules.d.ts +3 -0
- package/dist/server/routes/modules.js +106 -0
- package/dist/server/routes/processConfig.d.ts +3 -0
- package/dist/server/routes/processConfig.js +118 -0
- package/dist/server/routes/remoteConnections.d.ts +3 -0
- package/dist/server/routes/remoteConnections.js +634 -0
- package/dist/server/services/ProjectSetupService.d.ts +72 -0
- package/dist/server/services/ProjectSetupService.js +327 -0
- package/dist/server/utils/dialog.d.ts +1 -0
- package/dist/server/utils/dialog.js +16 -0
- package/dist/server/utils/encryption.d.ts +12 -0
- package/dist/server/utils/encryption.js +72 -0
- package/dist/server/utils/pm2-connection.d.ts +16 -0
- package/dist/server/utils/pm2-connection.js +141 -0
- package/dist/server/utils/remote-connection.d.ts +152 -0
- package/dist/server/utils/remote-connection.js +590 -0
- package/dist/server/utils/upload.d.ts +3 -0
- package/dist/server/utils/upload.js +39 -0
- package/package.json +65 -49
- package/src/client/build/asset-manifest.json +6 -6
- package/src/client/build/favicon.ico +2 -0
- package/src/client/build/index.html +1 -1
- package/src/client/build/logo192.svg +7 -0
- package/src/client/build/logo512.svg +7 -0
- package/src/client/build/manifest.json +5 -6
- package/src/client/build/static/css/main.672b8f26.css +2 -0
- package/src/client/build/static/css/main.672b8f26.css.map +1 -0
- package/src/client/build/static/js/main.31323a04.js +156 -0
- package/src/client/build/static/js/{main.dde30e92.js.LICENSE.txt → main.31323a04.js.LICENSE.txt} +19 -0
- package/src/client/build/static/js/main.31323a04.js.map +1 -0
- package/ .npmignore +0 -39
- package/install.bat +0 -22
- package/install.sh +0 -23
- package/src/client/build/static/css/main.c1cbda3a.css +0 -2
- package/src/client/build/static/css/main.c1cbda3a.css.map +0 -1
- package/src/client/build/static/js/main.dde30e92.js +0 -3
- package/src/client/build/static/js/main.dde30e92.js.map +0 -1
- package/src/client/package-lock.json +0 -16192
- package/src/client/package.json +0 -39
- package/src/client/public/index.html +0 -20
- package/src/client/public/manifest.json +0 -25
- package/src/index.ts +0 -24
- package/src/server/index.ts +0 -240
- package/tsconfig.json +0 -18
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
{
|
|
2
|
+
"projectTypes": {
|
|
3
|
+
"node": {
|
|
4
|
+
"name": "Node.js",
|
|
5
|
+
"detection": {
|
|
6
|
+
"files": ["package.json"],
|
|
7
|
+
"extensions": [".js", ".ts", ".jsx", ".tsx"]
|
|
8
|
+
},
|
|
9
|
+
"setup": {
|
|
10
|
+
"steps": [
|
|
11
|
+
{
|
|
12
|
+
"name": "Check Node.js version",
|
|
13
|
+
"command": "node --version",
|
|
14
|
+
"description": "Verifying Node.js installation",
|
|
15
|
+
"required": true
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"name": "Install dependencies",
|
|
19
|
+
"command": "npm install",
|
|
20
|
+
"description": "Installing project dependencies",
|
|
21
|
+
"required": true,
|
|
22
|
+
"workingDirectory": "project"
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"name": "Build project (if build script exists)",
|
|
26
|
+
"command": "npm run build",
|
|
27
|
+
"description": "Building the project",
|
|
28
|
+
"required": false,
|
|
29
|
+
"conditional": "build_script_exists",
|
|
30
|
+
"workingDirectory": "project"
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"name": "Run tests (if test script exists)",
|
|
34
|
+
"command": "npm test",
|
|
35
|
+
"description": "Running tests",
|
|
36
|
+
"required": false,
|
|
37
|
+
"conditional": "test_script_exists",
|
|
38
|
+
"workingDirectory": "project"
|
|
39
|
+
}
|
|
40
|
+
],
|
|
41
|
+
"environment": {
|
|
42
|
+
"NODE_ENV": "production"
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
"validation": {
|
|
46
|
+
"checks": [
|
|
47
|
+
{
|
|
48
|
+
"name": "package.json exists",
|
|
49
|
+
"file": "package.json"
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"name": "node_modules exists after install",
|
|
53
|
+
"directory": "node_modules"
|
|
54
|
+
}
|
|
55
|
+
]
|
|
56
|
+
},
|
|
57
|
+
"defaultConfig": {
|
|
58
|
+
"interpreter": "node",
|
|
59
|
+
"execMode": "fork",
|
|
60
|
+
"supportsCluster": true,
|
|
61
|
+
"startScript": "npm start"
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
"python": {
|
|
65
|
+
"name": "Python",
|
|
66
|
+
"detection": {
|
|
67
|
+
"files": ["requirements.txt", "pyproject.toml", "setup.py", "Pipfile"],
|
|
68
|
+
"extensions": [".py"]
|
|
69
|
+
},
|
|
70
|
+
"setup": {
|
|
71
|
+
"steps": [
|
|
72
|
+
{
|
|
73
|
+
"name": "Check Python version",
|
|
74
|
+
"command": "python --version",
|
|
75
|
+
"description": "Verifying Python installation",
|
|
76
|
+
"required": true
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
"name": "Create virtual environment",
|
|
80
|
+
"command": "python -m venv venv",
|
|
81
|
+
"description": "Creating Python virtual environment",
|
|
82
|
+
"required": true,
|
|
83
|
+
"workingDirectory": "project"
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
"name": "Activate virtual environment (Windows)",
|
|
87
|
+
"command": ".\\venv\\Scripts\\Activate.ps1",
|
|
88
|
+
"description": "Activating virtual environment",
|
|
89
|
+
"required": true,
|
|
90
|
+
"platform": "win32",
|
|
91
|
+
"workingDirectory": "project"
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
"name": "Activate virtual environment (Unix)",
|
|
95
|
+
"command": "source venv/bin/activate",
|
|
96
|
+
"description": "Activating virtual environment",
|
|
97
|
+
"required": true,
|
|
98
|
+
"platform": "unix",
|
|
99
|
+
"workingDirectory": "project"
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
"name": "Upgrade pip",
|
|
103
|
+
"command": "python -m pip install --upgrade pip",
|
|
104
|
+
"description": "Upgrading pip",
|
|
105
|
+
"required": true,
|
|
106
|
+
"workingDirectory": "project",
|
|
107
|
+
"useVenv": true
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
"name": "Install requirements",
|
|
111
|
+
"command": "pip install -r requirements.txt",
|
|
112
|
+
"description": "Installing Python dependencies",
|
|
113
|
+
"required": true,
|
|
114
|
+
"conditional": "requirements_exists",
|
|
115
|
+
"workingDirectory": "project",
|
|
116
|
+
"useVenv": true
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
"name": "Install from pyproject.toml",
|
|
120
|
+
"command": "pip install -e .",
|
|
121
|
+
"description": "Installing from pyproject.toml",
|
|
122
|
+
"required": true,
|
|
123
|
+
"conditional": "pyproject_exists",
|
|
124
|
+
"workingDirectory": "project",
|
|
125
|
+
"useVenv": true
|
|
126
|
+
}
|
|
127
|
+
],
|
|
128
|
+
"environment": {
|
|
129
|
+
"PYTHONPATH": ".",
|
|
130
|
+
"PYTHON_UNBUFFERED": "1"
|
|
131
|
+
}
|
|
132
|
+
},
|
|
133
|
+
"validation": {
|
|
134
|
+
"checks": [
|
|
135
|
+
{
|
|
136
|
+
"name": "Virtual environment created",
|
|
137
|
+
"directory": "venv"
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
"name": "Requirements file exists",
|
|
141
|
+
"file": "requirements.txt",
|
|
142
|
+
"optional": true
|
|
143
|
+
}
|
|
144
|
+
]
|
|
145
|
+
},
|
|
146
|
+
"defaultConfig": {
|
|
147
|
+
"interpreter": "python",
|
|
148
|
+
"execMode": "fork",
|
|
149
|
+
"supportsCluster": false,
|
|
150
|
+
"interpreterPath": "venv/Scripts/python.exe"
|
|
151
|
+
}
|
|
152
|
+
},
|
|
153
|
+
"dotnet": {
|
|
154
|
+
"name": ".NET",
|
|
155
|
+
"detection": {
|
|
156
|
+
"files": ["*.csproj", "*.fsproj", "*.vbproj", "*.sln"],
|
|
157
|
+
"extensions": [".cs", ".fs", ".vb"]
|
|
158
|
+
},
|
|
159
|
+
"setup": {
|
|
160
|
+
"steps": [
|
|
161
|
+
{
|
|
162
|
+
"name": "Check .NET version",
|
|
163
|
+
"command": "dotnet --version",
|
|
164
|
+
"description": "Verifying .NET installation",
|
|
165
|
+
"required": true
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
"name": "Restore packages",
|
|
169
|
+
"command": "dotnet restore",
|
|
170
|
+
"description": "Restoring NuGet packages",
|
|
171
|
+
"required": true,
|
|
172
|
+
"workingDirectory": "project"
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
"name": "Build project",
|
|
176
|
+
"command": "dotnet build --configuration Release",
|
|
177
|
+
"description": "Building .NET project",
|
|
178
|
+
"required": true,
|
|
179
|
+
"workingDirectory": "project"
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
"name": "Publish project",
|
|
183
|
+
"command": "dotnet publish --configuration Release --output ./publish",
|
|
184
|
+
"description": "Publishing .NET project",
|
|
185
|
+
"required": true,
|
|
186
|
+
"workingDirectory": "project"
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
"name": "Run tests",
|
|
190
|
+
"command": "dotnet test",
|
|
191
|
+
"description": "Running .NET tests",
|
|
192
|
+
"required": false,
|
|
193
|
+
"conditional": "test_project_exists",
|
|
194
|
+
"workingDirectory": "project"
|
|
195
|
+
}
|
|
196
|
+
],
|
|
197
|
+
"environment": {
|
|
198
|
+
"DOTNET_ENVIRONMENT": "Production",
|
|
199
|
+
"ASPNETCORE_ENVIRONMENT": "Production"
|
|
200
|
+
}
|
|
201
|
+
},
|
|
202
|
+
"validation": {
|
|
203
|
+
"checks": [
|
|
204
|
+
{
|
|
205
|
+
"name": "Project file exists",
|
|
206
|
+
"pattern": "*.csproj"
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
"name": "Publish directory exists",
|
|
210
|
+
"directory": "publish"
|
|
211
|
+
}
|
|
212
|
+
]
|
|
213
|
+
},
|
|
214
|
+
"defaultConfig": {
|
|
215
|
+
"interpreter": "dotnet",
|
|
216
|
+
"execMode": "fork",
|
|
217
|
+
"supportsCluster": false,
|
|
218
|
+
"startCommand": "dotnet run"
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
},
|
|
222
|
+
"global": {
|
|
223
|
+
"timeouts": {
|
|
224
|
+
"setup": 300000,
|
|
225
|
+
"validation": 30000
|
|
226
|
+
},
|
|
227
|
+
"retries": {
|
|
228
|
+
"setup": 2,
|
|
229
|
+
"validation": 1
|
|
230
|
+
},
|
|
231
|
+
"logging": {
|
|
232
|
+
"level": "info",
|
|
233
|
+
"file": "deployment.log"
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
package/dist/server/index.js
CHANGED
|
@@ -9,6 +9,13 @@ const http_1 = __importDefault(require("http"));
|
|
|
9
9
|
const socket_io_1 = require("socket.io");
|
|
10
10
|
const pm2_1 = __importDefault(require("pm2"));
|
|
11
11
|
const os_1 = __importDefault(require("os"));
|
|
12
|
+
const processConfig_1 = __importDefault(require("./routes/processConfig"));
|
|
13
|
+
const deployApplication_1 = __importDefault(require("./routes/deployApplication"));
|
|
14
|
+
const modules_1 = __importDefault(require("./routes/modules"));
|
|
15
|
+
const remoteConnections_1 = __importDefault(require("./routes/remoteConnections"));
|
|
16
|
+
const logStreaming_1 = require("./routes/logStreaming");
|
|
17
|
+
const pm2_connection_1 = require("./utils/pm2-connection");
|
|
18
|
+
const remote_connection_1 = require("./utils/remote-connection");
|
|
12
19
|
/**
|
|
13
20
|
* Create and configure the express server
|
|
14
21
|
*/
|
|
@@ -21,7 +28,10 @@ function createServer() {
|
|
|
21
28
|
origin: '*',
|
|
22
29
|
methods: ['GET', 'POST']
|
|
23
30
|
}
|
|
24
|
-
});
|
|
31
|
+
});
|
|
32
|
+
// Configure middleware
|
|
33
|
+
app.use(express_1.default.json());
|
|
34
|
+
// Serve static files from the React app build directory
|
|
25
35
|
const staticPath = 'D:/Personal/ezpm2gui/src/client/build';
|
|
26
36
|
console.log('Serving static files from:', staticPath);
|
|
27
37
|
const fs = require('fs');
|
|
@@ -31,24 +41,33 @@ function createServer() {
|
|
|
31
41
|
else {
|
|
32
42
|
console.error('Static files directory not found at:', staticPath);
|
|
33
43
|
}
|
|
34
|
-
//
|
|
35
|
-
app.
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
console.error(err);
|
|
46
|
-
res.status(500).json({ error: 'Failed to get PM2 processes' });
|
|
47
|
-
return;
|
|
48
|
-
}
|
|
49
|
-
res.json(processList);
|
|
44
|
+
// Register routes app.use('/api/cluster', clusterManagementRoutes);
|
|
45
|
+
app.use('/api/config', processConfig_1.default);
|
|
46
|
+
app.use('/api/deploy', deployApplication_1.default);
|
|
47
|
+
app.use('/api/modules', modules_1.default);
|
|
48
|
+
app.use('/api/remote', remoteConnections_1.default);
|
|
49
|
+
// Setup log streaming with Socket.IO
|
|
50
|
+
(0, logStreaming_1.setupLogStreaming)(io); // PM2 API endpoints
|
|
51
|
+
app.get('/api/processes', async (req, res) => {
|
|
52
|
+
try {
|
|
53
|
+
const processList = await (0, pm2_connection_1.executePM2Command)((callback) => {
|
|
54
|
+
pm2_1.default.list(callback);
|
|
50
55
|
});
|
|
51
|
-
|
|
56
|
+
res.json(processList);
|
|
57
|
+
}
|
|
58
|
+
catch (err) {
|
|
59
|
+
console.error('Failed to get PM2 processes:', err);
|
|
60
|
+
// Check if error is about PM2 not being installed
|
|
61
|
+
if (err instanceof Error && err.message.includes('PM2 is not installed')) {
|
|
62
|
+
res.status(500).json({
|
|
63
|
+
error: 'PM2 is not installed. Please install PM2 globally using: npm install -g pm2',
|
|
64
|
+
pmNotInstalled: true
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
res.status(500).json({ error: 'Failed to get PM2 processes' });
|
|
69
|
+
}
|
|
70
|
+
}
|
|
52
71
|
});
|
|
53
72
|
// Action endpoints (start, stop, restart, delete)
|
|
54
73
|
app.post('/api/process/:id/:action', (req, res) => {
|
|
@@ -192,7 +211,8 @@ function createServer() {
|
|
|
192
211
|
clearInterval(processInterval);
|
|
193
212
|
clearInterval(metricsInterval);
|
|
194
213
|
});
|
|
195
|
-
});
|
|
214
|
+
});
|
|
215
|
+
// Catch-all route to return the React app
|
|
196
216
|
app.get('*', (req, res) => {
|
|
197
217
|
const indexPath = 'D:/Personal/ezpm2gui/src/client/build/index.html';
|
|
198
218
|
console.log('Trying to serve index.html from:', indexPath);
|
|
@@ -216,4 +236,29 @@ if (require.main === module) {
|
|
|
216
236
|
server.listen(PORT, () => {
|
|
217
237
|
console.log(`Server running on http://${HOST}:${PORT}`);
|
|
218
238
|
});
|
|
239
|
+
// Handle shutdown gracefully
|
|
240
|
+
process.on('SIGINT', async () => {
|
|
241
|
+
console.log('\nGracefully shutting down...');
|
|
242
|
+
try {
|
|
243
|
+
await (0, pm2_connection_1.disconnectFromPM2)();
|
|
244
|
+
await remote_connection_1.remoteConnectionManager.closeAllConnections();
|
|
245
|
+
}
|
|
246
|
+
catch (error) {
|
|
247
|
+
console.error('Error during shutdown:', error);
|
|
248
|
+
}
|
|
249
|
+
server.close();
|
|
250
|
+
process.exit(0);
|
|
251
|
+
});
|
|
252
|
+
process.on('SIGTERM', async () => {
|
|
253
|
+
console.log('\nGracefully shutting down...');
|
|
254
|
+
try {
|
|
255
|
+
await (0, pm2_connection_1.disconnectFromPM2)();
|
|
256
|
+
await remote_connection_1.remoteConnectionManager.closeAllConnections();
|
|
257
|
+
}
|
|
258
|
+
catch (error) {
|
|
259
|
+
console.error('Error during shutdown:', error);
|
|
260
|
+
}
|
|
261
|
+
server.close();
|
|
262
|
+
process.exit(0);
|
|
263
|
+
});
|
|
219
264
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
[2025-05-25T18:02:41.395Z] Starting setup for python project at: D:\Projects\FaceDetection
|
|
2
|
+
[2025-05-25T18:02:41.396Z] Executing step: Check Python version
|
|
3
|
+
[2025-05-25T18:02:41.954Z] Step "Check Python version" completed with exit code: 0
|
|
4
|
+
[2025-05-25T18:02:41.955Z] Executing step: Create virtual environment
|
|
5
|
+
[2025-05-25T18:03:20.998Z] Step "Create virtual environment" completed with exit code: 0
|
|
6
|
+
[2025-05-25T18:03:20.999Z] Executing step: Activate virtual environment (Windows)
|
|
7
|
+
[2025-05-25T18:03:21.176Z] Step "Activate virtual environment (Windows)" completed with exit code: 0
|
|
8
|
+
[2025-05-25T18:03:21.176Z] Executing step: Upgrade pip
|
|
9
|
+
[2025-05-25T18:03:48.644Z] Step "Upgrade pip" completed with exit code: 0
|
|
10
|
+
[2025-05-25T18:03:48.645Z] Executing step: Install requirements
|
|
11
|
+
[2025-05-25T18:04:56.318Z] Step "Install requirements" completed with exit code: 0
|
|
12
|
+
[2025-05-25T18:04:56.322Z] Setup completed. Success: true
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const express_1 = require("express");
|
|
7
|
+
const pm2_1 = __importDefault(require("pm2"));
|
|
8
|
+
const router = (0, express_1.Router)();
|
|
9
|
+
// Get cluster information for a specific process
|
|
10
|
+
router.get('/:id', (req, res) => {
|
|
11
|
+
const { id } = req.params;
|
|
12
|
+
pm2_1.default.connect((err) => {
|
|
13
|
+
if (err) {
|
|
14
|
+
console.error(err);
|
|
15
|
+
res.status(500).json({ error: 'Failed to connect to PM2' });
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
pm2_1.default.describe(id, (err, processDesc) => {
|
|
19
|
+
pm2_1.default.disconnect();
|
|
20
|
+
if (err || !processDesc || processDesc.length === 0) {
|
|
21
|
+
res.status(404).json({ error: 'Process not found' });
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
const process = processDesc[0];
|
|
25
|
+
const clusterInfo = {
|
|
26
|
+
pm_id: process.pm_id,
|
|
27
|
+
name: process.name,
|
|
28
|
+
instances: process.pm2_env.instances,
|
|
29
|
+
exec_mode: process.pm2_env.exec_mode,
|
|
30
|
+
isCluster: process.pm2_env.exec_mode === 'cluster_mode'
|
|
31
|
+
};
|
|
32
|
+
res.json(clusterInfo);
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
// Scale process (change number of instances)
|
|
37
|
+
router.post('/:id/scale', (req, res) => {
|
|
38
|
+
const { id } = req.params;
|
|
39
|
+
const { instances } = req.body;
|
|
40
|
+
if (!instances || isNaN(parseInt(instances))) {
|
|
41
|
+
res.status(400).json({ error: 'Valid number of instances is required' });
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
pm2_1.default.connect((err) => {
|
|
45
|
+
if (err) {
|
|
46
|
+
console.error(err);
|
|
47
|
+
res.status(500).json({ error: 'Failed to connect to PM2' });
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
// Using PM2 API to scale a process
|
|
51
|
+
// TypeScript doesn't recognize 'scale' method, so we're using it with a type assertion
|
|
52
|
+
pm2_1.default.scale(id, parseInt(instances), (err) => {
|
|
53
|
+
pm2_1.default.disconnect();
|
|
54
|
+
if (err) {
|
|
55
|
+
console.error(err);
|
|
56
|
+
res.status(500).json({ error: 'Failed to scale process' });
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
res.json({ success: true, message: `Process ${id} scaled to ${instances} instances` });
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
// Reload instances (zero-downtime reload)
|
|
64
|
+
router.post('/:id/reload', (req, res) => {
|
|
65
|
+
const { id } = req.params;
|
|
66
|
+
pm2_1.default.connect((err) => {
|
|
67
|
+
if (err) {
|
|
68
|
+
console.error(err);
|
|
69
|
+
res.status(500).json({ error: 'Failed to connect to PM2' });
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
pm2_1.default.reload(id, (err) => {
|
|
73
|
+
pm2_1.default.disconnect();
|
|
74
|
+
if (err) {
|
|
75
|
+
console.error(err);
|
|
76
|
+
res.status(500).json({ error: 'Failed to reload process' });
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
res.json({ success: true, message: `Process ${id} reloaded with zero-downtime` });
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
// Change execution mode between 'fork' and 'cluster'
|
|
84
|
+
router.post('/:id/exec-mode', (req, res) => {
|
|
85
|
+
const { id } = req.params;
|
|
86
|
+
const { mode } = req.body;
|
|
87
|
+
if (!mode || (mode !== 'fork' && mode !== 'cluster')) {
|
|
88
|
+
res.status(400).json({ error: 'Valid execution mode (fork or cluster) is required' });
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
pm2_1.default.connect((err) => {
|
|
92
|
+
if (err) {
|
|
93
|
+
console.error(err);
|
|
94
|
+
res.status(500).json({ error: 'Failed to connect to PM2' });
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
// First get the current process information to preserve settings
|
|
98
|
+
pm2_1.default.describe(id, (err, processDesc) => {
|
|
99
|
+
if (err || !processDesc || processDesc.length === 0) {
|
|
100
|
+
pm2_1.default.disconnect();
|
|
101
|
+
res.status(404).json({ error: 'Process not found' });
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
const process = processDesc[0];
|
|
105
|
+
const pm2Env = process.pm2_env;
|
|
106
|
+
// Update the execution mode
|
|
107
|
+
const updateOptions = {
|
|
108
|
+
script: pm2Env.pm_exec_path,
|
|
109
|
+
name: process.name,
|
|
110
|
+
instances: pm2Env.instances || 1,
|
|
111
|
+
exec_mode: mode === 'cluster' ? 'cluster_mode' : 'fork_mode',
|
|
112
|
+
// Preserve other settings
|
|
113
|
+
cwd: pm2Env.pm_cwd,
|
|
114
|
+
watch: pm2Env.watch || false,
|
|
115
|
+
ignore_watch: pm2Env.ignore_watch || [],
|
|
116
|
+
env: pm2Env.env || {}
|
|
117
|
+
};
|
|
118
|
+
// Stop the existing process
|
|
119
|
+
pm2_1.default.stop(id, (stopErr) => {
|
|
120
|
+
if (stopErr) {
|
|
121
|
+
pm2_1.default.disconnect();
|
|
122
|
+
console.error(stopErr);
|
|
123
|
+
res.status(500).json({ error: 'Failed to stop process for exec mode change' });
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
// Delete the existing process
|
|
127
|
+
pm2_1.default.del(id, (delErr) => {
|
|
128
|
+
if (delErr) {
|
|
129
|
+
pm2_1.default.disconnect();
|
|
130
|
+
console.error(delErr);
|
|
131
|
+
res.status(500).json({ error: 'Failed to delete process for exec mode change' });
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
// Start with new settings
|
|
135
|
+
pm2_1.default.start(updateOptions, (startErr) => {
|
|
136
|
+
pm2_1.default.disconnect();
|
|
137
|
+
if (startErr) {
|
|
138
|
+
console.error(startErr);
|
|
139
|
+
res.status(500).json({ error: 'Failed to restart process with new exec mode' });
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
res.json({
|
|
143
|
+
success: true,
|
|
144
|
+
message: `Process ${process.name} execution mode changed to ${mode}`
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
exports.default = router;
|