neex 0.6.54 → 0.6.60
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/dist/src/commands/start-commands.js +36 -22
- package/dist/src/start-manager.js +198 -96
- package/package.json +1 -1
|
@@ -13,10 +13,10 @@ const fs_1 = __importDefault(require("fs"));
|
|
|
13
13
|
const os_1 = __importDefault(require("os"));
|
|
14
14
|
function addStartCommands(program) {
|
|
15
15
|
let startManager = null;
|
|
16
|
-
//
|
|
16
|
+
// Production start command
|
|
17
17
|
program
|
|
18
18
|
.command('start [file]')
|
|
19
|
-
.description('Start production application
|
|
19
|
+
.description('Start production application')
|
|
20
20
|
.option('-d, --dir <directory>', 'Working directory', process.cwd())
|
|
21
21
|
.option('-e, --env <file>', 'Environment file to load', '.env')
|
|
22
22
|
.option('-p, --port <port>', 'Port number', parseInt)
|
|
@@ -32,7 +32,7 @@ function addStartCommands(program) {
|
|
|
32
32
|
.option('--node-args <args>', 'Additional Node.js arguments')
|
|
33
33
|
.action(async (file, options) => {
|
|
34
34
|
try {
|
|
35
|
-
const targetFile = file || 'dist/
|
|
35
|
+
const targetFile = file || 'dist/server.js';
|
|
36
36
|
let resolvedFile = path_1.default.resolve(options.dir, targetFile);
|
|
37
37
|
// Auto-detect main file if not found
|
|
38
38
|
if (!fs_1.default.existsSync(resolvedFile)) {
|
|
@@ -53,12 +53,13 @@ function addStartCommands(program) {
|
|
|
53
53
|
const commonLocations = [
|
|
54
54
|
'dist/server.js',
|
|
55
55
|
'dist/app.js',
|
|
56
|
-
'
|
|
56
|
+
'dist/index.js',
|
|
57
57
|
'build/server.js',
|
|
58
|
-
'
|
|
59
|
-
'
|
|
60
|
-
'
|
|
61
|
-
'
|
|
58
|
+
'build/app.js',
|
|
59
|
+
'build/index.js',
|
|
60
|
+
'server.js',
|
|
61
|
+
'app.js',
|
|
62
|
+
'index.js'
|
|
62
63
|
];
|
|
63
64
|
let found = false;
|
|
64
65
|
for (const location of commonLocations) {
|
|
@@ -85,25 +86,37 @@ function addStartCommands(program) {
|
|
|
85
86
|
throw new Error(`Application file not found: ${resolvedFile}`);
|
|
86
87
|
}
|
|
87
88
|
}
|
|
88
|
-
//
|
|
89
|
+
// Environment detection
|
|
89
90
|
const isDevelopment = options.watch || process.env.NODE_ENV === 'development';
|
|
90
91
|
const isProduction = !isDevelopment;
|
|
91
92
|
const cpuCount = os_1.default.cpus().length;
|
|
92
|
-
// Smart worker allocation
|
|
93
|
+
// Smart worker allocation - more conservative approach
|
|
93
94
|
let workers = options.workers;
|
|
94
95
|
if (!workers) {
|
|
95
96
|
if (isDevelopment) {
|
|
96
|
-
workers = 1; //
|
|
97
|
+
workers = 1; // Always single worker in development
|
|
97
98
|
}
|
|
98
99
|
else {
|
|
99
|
-
// For production,
|
|
100
|
-
|
|
100
|
+
// For production, be more conservative
|
|
101
|
+
if (cpuCount <= 2) {
|
|
102
|
+
workers = 1; // Single worker for small systems
|
|
103
|
+
}
|
|
104
|
+
else if (cpuCount <= 4) {
|
|
105
|
+
workers = 2; // Two workers for medium systems
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
workers = Math.min(Math.floor(cpuCount / 2), 4); // Max 4 workers
|
|
109
|
+
}
|
|
101
110
|
}
|
|
102
111
|
}
|
|
103
|
-
|
|
112
|
+
// Force single worker for development or debugging
|
|
113
|
+
if (isDevelopment || options.inspect || options.inspectBrk) {
|
|
114
|
+
workers = 1;
|
|
115
|
+
}
|
|
116
|
+
const healthCheck = options.health !== false;
|
|
104
117
|
const defaultPort = parseInt(process.env.PORT || '8000');
|
|
105
118
|
const port = options.port || defaultPort;
|
|
106
|
-
//
|
|
119
|
+
// Set NODE_ENV if not already set
|
|
107
120
|
if (!process.env.NODE_ENV) {
|
|
108
121
|
process.env.NODE_ENV = isProduction ? 'production' : 'development';
|
|
109
122
|
}
|
|
@@ -117,24 +130,28 @@ function addStartCommands(program) {
|
|
|
117
130
|
logger_manager_js_1.loggerManager.printLine(`Environment: ${process.env.NODE_ENV}`);
|
|
118
131
|
logger_manager_js_1.loggerManager.printLine(`Port: ${port}`);
|
|
119
132
|
logger_manager_js_1.loggerManager.printLine(`Workers: ${workers}`);
|
|
133
|
+
logger_manager_js_1.loggerManager.printLine(`CPU Cores: ${cpuCount}`);
|
|
120
134
|
if (healthCheck) {
|
|
121
135
|
logger_manager_js_1.loggerManager.printLine(`Health Check: http://localhost:${options.healthPort}/health`);
|
|
122
136
|
}
|
|
123
137
|
}
|
|
138
|
+
// Optimize memory settings based on workers
|
|
139
|
+
const memoryPerWorker = workers > 1 ? Math.floor(512 / workers) : 512;
|
|
140
|
+
const maxMemoryPerWorker = workers > 1 ? Math.floor(1024 / workers) : 1024;
|
|
124
141
|
startManager = new start_manager_js_1.StartManager({
|
|
125
142
|
file: resolvedFile,
|
|
126
143
|
workingDir: options.dir,
|
|
127
144
|
envFile: options.env,
|
|
128
145
|
port,
|
|
129
146
|
workers,
|
|
130
|
-
memoryLimit: isProduction ?
|
|
147
|
+
memoryLimit: isProduction ? `${memoryPerWorker}M` : undefined,
|
|
131
148
|
logLevel: options.verbose ? 'debug' : 'info',
|
|
132
149
|
color: process.stdout.isTTY,
|
|
133
150
|
verbose: options.verbose,
|
|
134
151
|
watch: options.watch,
|
|
135
|
-
maxMemory: options.maxMemory || (isProduction ?
|
|
152
|
+
maxMemory: options.maxMemory || (isProduction ? `${maxMemoryPerWorker}M` : undefined),
|
|
136
153
|
maxCrashes: isProduction ? 3 : 10,
|
|
137
|
-
restartDelay: isProduction ?
|
|
154
|
+
restartDelay: isProduction ? 1000 : 500,
|
|
138
155
|
healthCheck,
|
|
139
156
|
healthPort: options.healthPort,
|
|
140
157
|
gracefulTimeout: options.gracefulTimeout,
|
|
@@ -162,14 +179,13 @@ function addStartCommands(program) {
|
|
|
162
179
|
startManager = null;
|
|
163
180
|
}
|
|
164
181
|
catch (error) {
|
|
165
|
-
// Ignore cleanup errors but log in verbose mode
|
|
166
182
|
if (process.env.VERBOSE) {
|
|
167
183
|
console.error('Cleanup error:', error);
|
|
168
184
|
}
|
|
169
185
|
}
|
|
170
186
|
}
|
|
171
187
|
};
|
|
172
|
-
//
|
|
188
|
+
// Signal handling
|
|
173
189
|
const handleExit = (signal) => {
|
|
174
190
|
if (startManager) {
|
|
175
191
|
console.log(`\n${chalk_1.default.yellow(figures_1.default.warning)} Received ${signal}, shutting down gracefully...`);
|
|
@@ -183,11 +199,9 @@ function addStartCommands(program) {
|
|
|
183
199
|
process.exit(0);
|
|
184
200
|
}
|
|
185
201
|
};
|
|
186
|
-
// Register signal handlers
|
|
187
202
|
process.on('SIGINT', () => handleExit('SIGINT'));
|
|
188
203
|
process.on('SIGTERM', () => handleExit('SIGTERM'));
|
|
189
204
|
process.on('SIGUSR2', () => handleExit('SIGUSR2'));
|
|
190
|
-
// Handle uncaught exceptions
|
|
191
205
|
process.on('uncaughtException', (error) => {
|
|
192
206
|
console.error(`${chalk_1.default.red(figures_1.default.cross)} Uncaught Exception:`, error);
|
|
193
207
|
cleanupStart().then(() => {
|
|
@@ -4,7 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.StartManager = void 0;
|
|
7
|
-
// src/start-manager.ts - Fixed
|
|
7
|
+
// src/start-manager.ts - Fixed production start manager
|
|
8
8
|
const child_process_1 = require("child_process");
|
|
9
9
|
const chokidar_1 = require("chokidar");
|
|
10
10
|
const logger_manager_js_1 = require("./logger-manager.js");
|
|
@@ -23,6 +23,8 @@ class StartManager {
|
|
|
23
23
|
this.isShuttingDown = false;
|
|
24
24
|
this.totalRestarts = 0;
|
|
25
25
|
this.envLoaded = false;
|
|
26
|
+
this.masterProcess = null;
|
|
27
|
+
this.readyWorkers = 0;
|
|
26
28
|
this.options = options;
|
|
27
29
|
this.startTime = new Date();
|
|
28
30
|
this.debouncedRestart = (0, lodash_1.debounce)(this.restartAll.bind(this), options.restartDelay);
|
|
@@ -85,10 +87,10 @@ class StartManager {
|
|
|
85
87
|
args.push(`--max-old-space-size=${memoryMB}`);
|
|
86
88
|
}
|
|
87
89
|
}
|
|
88
|
-
if (this.options.inspect) {
|
|
90
|
+
if (this.options.inspect && this.options.workers === 1) {
|
|
89
91
|
args.push('--inspect');
|
|
90
92
|
}
|
|
91
|
-
if (this.options.inspectBrk) {
|
|
93
|
+
if (this.options.inspectBrk && this.options.workers === 1) {
|
|
92
94
|
args.push('--inspect-brk');
|
|
93
95
|
}
|
|
94
96
|
if (this.options.nodeArgs) {
|
|
@@ -96,58 +98,147 @@ class StartManager {
|
|
|
96
98
|
}
|
|
97
99
|
return args;
|
|
98
100
|
}
|
|
101
|
+
async startSingleProcess() {
|
|
102
|
+
var _a, _b;
|
|
103
|
+
const nodeArgs = this.getNodeArgs();
|
|
104
|
+
const port = this.options.port || 8000;
|
|
105
|
+
const env = {
|
|
106
|
+
...process.env,
|
|
107
|
+
NODE_ENV: process.env.NODE_ENV || 'production',
|
|
108
|
+
PORT: port.toString(),
|
|
109
|
+
FORCE_COLOR: this.options.color ? '1' : '0',
|
|
110
|
+
NODE_OPTIONS: '--no-deprecation',
|
|
111
|
+
CLUSTER_WORKER: 'false',
|
|
112
|
+
MASTER_PROCESS: 'true'
|
|
113
|
+
};
|
|
114
|
+
if (this.options.verbose) {
|
|
115
|
+
this.log(`Starting single process on port ${port}`);
|
|
116
|
+
}
|
|
117
|
+
this.masterProcess = (0, child_process_1.fork)(this.options.file, [], {
|
|
118
|
+
cwd: this.options.workingDir,
|
|
119
|
+
env,
|
|
120
|
+
execArgv: nodeArgs,
|
|
121
|
+
silent: true
|
|
122
|
+
});
|
|
123
|
+
// Handle process output with filtering
|
|
124
|
+
(_a = this.masterProcess.stdout) === null || _a === void 0 ? void 0 : _a.on('data', (data) => {
|
|
125
|
+
const message = data.toString().trim();
|
|
126
|
+
if (message && !this.shouldFilterMessage(message)) {
|
|
127
|
+
console.log(message);
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
(_b = this.masterProcess.stderr) === null || _b === void 0 ? void 0 : _b.on('data', (data) => {
|
|
131
|
+
const message = data.toString().trim();
|
|
132
|
+
if (message && !this.shouldFilterMessage(message)) {
|
|
133
|
+
console.error(chalk_1.default.red(message));
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
this.masterProcess.on('error', (error) => {
|
|
137
|
+
this.log(`Process error: ${error.message}`, 'error');
|
|
138
|
+
});
|
|
139
|
+
this.masterProcess.on('exit', (code, signal) => {
|
|
140
|
+
if (!this.isShuttingDown && code !== 0 && signal !== 'SIGTERM') {
|
|
141
|
+
this.log(`Process crashed (code: ${code}, signal: ${signal})`, 'error');
|
|
142
|
+
this.totalRestarts++;
|
|
143
|
+
if (this.totalRestarts < this.options.maxCrashes) {
|
|
144
|
+
setTimeout(() => {
|
|
145
|
+
this.startSingleProcess();
|
|
146
|
+
}, this.options.restartDelay);
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
this.log(`Max crashes reached (${this.options.maxCrashes}), not restarting`, 'error');
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
// Wait for process to be ready
|
|
154
|
+
await new Promise((resolve, reject) => {
|
|
155
|
+
const timeout = setTimeout(() => {
|
|
156
|
+
resolve(); // Don't reject, assume it's ready
|
|
157
|
+
}, 5000);
|
|
158
|
+
this.masterProcess.on('message', (message) => {
|
|
159
|
+
if (message && message.type === 'ready') {
|
|
160
|
+
clearTimeout(timeout);
|
|
161
|
+
resolve();
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
this.masterProcess.on('error', (error) => {
|
|
165
|
+
clearTimeout(timeout);
|
|
166
|
+
reject(error);
|
|
167
|
+
});
|
|
168
|
+
this.masterProcess.on('exit', (code) => {
|
|
169
|
+
clearTimeout(timeout);
|
|
170
|
+
if (code !== 0) {
|
|
171
|
+
reject(new Error(`Process exited with code ${code}`));
|
|
172
|
+
}
|
|
173
|
+
else {
|
|
174
|
+
resolve();
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
});
|
|
178
|
+
if (this.options.verbose) {
|
|
179
|
+
this.log(`Process started (PID: ${this.masterProcess.pid})`);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
shouldFilterMessage(message) {
|
|
183
|
+
const filters = [
|
|
184
|
+
'[dotenv@',
|
|
185
|
+
'injecting env',
|
|
186
|
+
'Express (',
|
|
187
|
+
'Local: http://localhost:',
|
|
188
|
+
'Health Check: http://localhost:',
|
|
189
|
+
'Environment: production',
|
|
190
|
+
'Environment: development'
|
|
191
|
+
];
|
|
192
|
+
return filters.some(filter => message.includes(filter));
|
|
193
|
+
}
|
|
99
194
|
async startWorker(workerId) {
|
|
100
195
|
var _a, _b;
|
|
101
196
|
const nodeArgs = this.getNodeArgs();
|
|
102
|
-
const
|
|
103
|
-
const workerPort = this.options.workers > 1 ? basePort + workerId - 1 : basePort;
|
|
197
|
+
const port = this.options.port || 8000;
|
|
104
198
|
const env = {
|
|
105
199
|
...process.env,
|
|
106
200
|
NODE_ENV: process.env.NODE_ENV || 'production',
|
|
107
201
|
WORKER_ID: workerId.toString(),
|
|
108
|
-
PORT:
|
|
202
|
+
PORT: port.toString(),
|
|
109
203
|
CLUSTER_WORKER: 'true',
|
|
204
|
+
MASTER_PROCESS: 'false',
|
|
110
205
|
FORCE_COLOR: this.options.color ? '1' : '0',
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
NODE_OPTIONS: '--no-deprecation'
|
|
206
|
+
NODE_OPTIONS: '--no-deprecation',
|
|
207
|
+
SUPPRESS_STARTUP_LOGS: 'true' // Signal to suppress duplicate logs
|
|
114
208
|
};
|
|
209
|
+
if (this.options.verbose) {
|
|
210
|
+
this.log(`Starting worker ${workerId}`);
|
|
211
|
+
}
|
|
115
212
|
const workerProcess = (0, child_process_1.fork)(this.options.file, [], {
|
|
116
213
|
cwd: this.options.workingDir,
|
|
117
214
|
env,
|
|
118
215
|
execArgv: nodeArgs,
|
|
119
|
-
silent: true
|
|
216
|
+
silent: true
|
|
120
217
|
});
|
|
121
218
|
const workerInfo = {
|
|
122
219
|
process: workerProcess,
|
|
123
220
|
pid: workerProcess.pid,
|
|
124
221
|
restarts: 0,
|
|
125
222
|
startTime: new Date(),
|
|
126
|
-
id: workerId
|
|
223
|
+
id: workerId,
|
|
224
|
+
port: port,
|
|
225
|
+
ready: false
|
|
127
226
|
};
|
|
128
227
|
this.workers.set(workerId, workerInfo);
|
|
129
|
-
// Handle worker output with
|
|
228
|
+
// Handle worker output with filtering
|
|
130
229
|
(_a = workerProcess.stdout) === null || _a === void 0 ? void 0 : _a.on('data', (data) => {
|
|
131
230
|
const message = data.toString().trim();
|
|
132
|
-
if (message) {
|
|
133
|
-
// Filter out repetitive dotenv messages
|
|
134
|
-
if (message.includes('[dotenv@') || message.includes('injecting env')) {
|
|
135
|
-
return;
|
|
136
|
-
}
|
|
231
|
+
if (message && !this.shouldFilterMessage(message)) {
|
|
137
232
|
const prefix = this.options.verbose ?
|
|
138
|
-
chalk_1.default.dim(`[Worker ${workerId}
|
|
233
|
+
chalk_1.default.dim(`[Worker ${workerId}] `) : '';
|
|
139
234
|
console.log(prefix + message);
|
|
140
235
|
}
|
|
141
236
|
});
|
|
142
237
|
(_b = workerProcess.stderr) === null || _b === void 0 ? void 0 : _b.on('data', (data) => {
|
|
143
238
|
const message = data.toString().trim();
|
|
144
|
-
if (message) {
|
|
145
|
-
// Filter out non-critical warnings
|
|
146
|
-
if (message.includes('[dotenv@') || message.includes('injecting env')) {
|
|
147
|
-
return;
|
|
148
|
-
}
|
|
239
|
+
if (message && !this.shouldFilterMessage(message)) {
|
|
149
240
|
const prefix = this.options.verbose ?
|
|
150
|
-
chalk_1.default.dim(`[Worker ${workerId}
|
|
241
|
+
chalk_1.default.dim(`[Worker ${workerId}] `) : '';
|
|
151
242
|
console.error(prefix + chalk_1.default.red(message));
|
|
152
243
|
}
|
|
153
244
|
});
|
|
@@ -156,21 +247,23 @@ class StartManager {
|
|
|
156
247
|
});
|
|
157
248
|
workerProcess.on('exit', (code, signal) => {
|
|
158
249
|
this.workers.delete(workerId);
|
|
159
|
-
if (!this.isShuttingDown) {
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
this.restartWorker(workerId);
|
|
163
|
-
}
|
|
250
|
+
if (!this.isShuttingDown && code !== 0 && signal !== 'SIGTERM') {
|
|
251
|
+
this.log(`Worker ${workerId} crashed (code: ${code}, signal: ${signal})`, 'error');
|
|
252
|
+
this.restartWorker(workerId);
|
|
164
253
|
}
|
|
165
254
|
});
|
|
166
255
|
// Wait for worker to be ready
|
|
167
256
|
await new Promise((resolve, reject) => {
|
|
168
257
|
const timeout = setTimeout(() => {
|
|
169
|
-
|
|
170
|
-
|
|
258
|
+
workerInfo.ready = true;
|
|
259
|
+
this.readyWorkers++;
|
|
260
|
+
resolve(); // Don't reject, assume it's ready
|
|
261
|
+
}, 5000);
|
|
171
262
|
workerProcess.on('message', (message) => {
|
|
172
263
|
if (message && message.type === 'ready') {
|
|
173
264
|
clearTimeout(timeout);
|
|
265
|
+
workerInfo.ready = true;
|
|
266
|
+
this.readyWorkers++;
|
|
174
267
|
resolve();
|
|
175
268
|
}
|
|
176
269
|
});
|
|
@@ -179,19 +272,17 @@ class StartManager {
|
|
|
179
272
|
reject(error);
|
|
180
273
|
});
|
|
181
274
|
workerProcess.on('exit', (code) => {
|
|
275
|
+
clearTimeout(timeout);
|
|
182
276
|
if (code !== 0) {
|
|
183
|
-
clearTimeout(timeout);
|
|
184
277
|
reject(new Error(`Worker ${workerId} exited with code ${code}`));
|
|
185
278
|
}
|
|
279
|
+
else {
|
|
280
|
+
resolve();
|
|
281
|
+
}
|
|
186
282
|
});
|
|
187
|
-
// Fallback - assume ready after 2 seconds if no message
|
|
188
|
-
setTimeout(() => {
|
|
189
|
-
clearTimeout(timeout);
|
|
190
|
-
resolve();
|
|
191
|
-
}, 2000);
|
|
192
283
|
});
|
|
193
284
|
if (this.options.verbose) {
|
|
194
|
-
this.log(`Worker ${workerId} started (PID: ${workerProcess.pid}
|
|
285
|
+
this.log(`Worker ${workerId} started (PID: ${workerProcess.pid})`);
|
|
195
286
|
}
|
|
196
287
|
return workerInfo;
|
|
197
288
|
}
|
|
@@ -235,6 +326,12 @@ class StartManager {
|
|
|
235
326
|
});
|
|
236
327
|
}
|
|
237
328
|
async startCluster() {
|
|
329
|
+
if (this.options.workers === 1) {
|
|
330
|
+
await this.startSingleProcess();
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
// Multi-worker mode
|
|
334
|
+
this.readyWorkers = 0;
|
|
238
335
|
const startPromises = [];
|
|
239
336
|
for (let i = 0; i < this.options.workers; i++) {
|
|
240
337
|
startPromises.push(this.startWorker(i + 1));
|
|
@@ -260,19 +357,27 @@ class StartManager {
|
|
|
260
357
|
return;
|
|
261
358
|
}
|
|
262
359
|
if (req.url === '/health') {
|
|
360
|
+
const activeWorkers = this.options.workers === 1 ?
|
|
361
|
+
(this.masterProcess ? 1 : 0) :
|
|
362
|
+
this.workers.size;
|
|
263
363
|
const stats = {
|
|
264
364
|
status: 'ok',
|
|
265
365
|
uptime: Date.now() - this.startTime.getTime(),
|
|
266
|
-
workers:
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
pid:
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
366
|
+
workers: activeWorkers,
|
|
367
|
+
expectedWorkers: this.options.workers,
|
|
368
|
+
activeWorkers: this.options.workers === 1 ?
|
|
369
|
+
(this.masterProcess ? [{ id: 1, pid: this.masterProcess.pid, uptime: Date.now() - this.startTime.getTime() }] : []) :
|
|
370
|
+
Array.from(this.workers.values()).map(w => ({
|
|
371
|
+
id: w.id,
|
|
372
|
+
pid: w.pid,
|
|
373
|
+
restarts: w.restarts,
|
|
374
|
+
uptime: Date.now() - w.startTime.getTime(),
|
|
375
|
+
ready: w.ready
|
|
376
|
+
})),
|
|
273
377
|
totalRestarts: this.totalRestarts,
|
|
274
378
|
memory: process.memoryUsage(),
|
|
275
|
-
cpu: os_1.default.loadavg()
|
|
379
|
+
cpu: os_1.default.loadavg(),
|
|
380
|
+
port: this.options.port || 8000
|
|
276
381
|
};
|
|
277
382
|
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
278
383
|
res.end(JSON.stringify(stats, null, 2));
|
|
@@ -321,67 +426,53 @@ class StartManager {
|
|
|
321
426
|
async restartAll() {
|
|
322
427
|
if (this.isShuttingDown)
|
|
323
428
|
return;
|
|
324
|
-
this.log('Restarting
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
workerInfo.process.kill('SIGTERM');
|
|
330
|
-
await this.waitForProcessExit(workerInfo.process, 5000);
|
|
331
|
-
}
|
|
332
|
-
catch (error) {
|
|
333
|
-
workerInfo.process.kill('SIGKILL');
|
|
334
|
-
}
|
|
335
|
-
})());
|
|
336
|
-
}
|
|
337
|
-
await Promise.allSettled(restartPromises);
|
|
338
|
-
// Wait a bit before restarting
|
|
339
|
-
setTimeout(() => {
|
|
340
|
-
this.startCluster();
|
|
341
|
-
}, this.options.restartDelay);
|
|
342
|
-
}
|
|
343
|
-
setupMemoryMonitoring() {
|
|
344
|
-
if (!this.options.maxMemory)
|
|
345
|
-
return;
|
|
346
|
-
const maxMemory = this.parseMemoryLimit(this.options.maxMemory);
|
|
347
|
-
if (!maxMemory)
|
|
348
|
-
return;
|
|
349
|
-
const checkInterval = setInterval(() => {
|
|
350
|
-
if (this.isShuttingDown) {
|
|
351
|
-
clearInterval(checkInterval);
|
|
352
|
-
return;
|
|
429
|
+
this.log('Restarting due to file changes...');
|
|
430
|
+
if (this.options.workers === 1 && this.masterProcess) {
|
|
431
|
+
try {
|
|
432
|
+
this.masterProcess.kill('SIGTERM');
|
|
433
|
+
await this.waitForProcessExit(this.masterProcess, 5000);
|
|
353
434
|
}
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
435
|
+
catch (error) {
|
|
436
|
+
this.masterProcess.kill('SIGKILL');
|
|
437
|
+
}
|
|
438
|
+
setTimeout(() => {
|
|
439
|
+
this.startSingleProcess();
|
|
440
|
+
}, this.options.restartDelay);
|
|
441
|
+
}
|
|
442
|
+
else {
|
|
443
|
+
const restartPromises = [];
|
|
444
|
+
for (const [workerId, workerInfo] of this.workers.entries()) {
|
|
445
|
+
restartPromises.push((async () => {
|
|
446
|
+
try {
|
|
447
|
+
workerInfo.process.kill('SIGTERM');
|
|
448
|
+
await this.waitForProcessExit(workerInfo.process, 5000);
|
|
361
449
|
}
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
|
|
450
|
+
catch (error) {
|
|
451
|
+
workerInfo.process.kill('SIGKILL');
|
|
452
|
+
}
|
|
453
|
+
})());
|
|
454
|
+
}
|
|
455
|
+
await Promise.allSettled(restartPromises);
|
|
456
|
+
setTimeout(() => {
|
|
457
|
+
this.startCluster();
|
|
458
|
+
}, this.options.restartDelay);
|
|
459
|
+
}
|
|
368
460
|
}
|
|
369
461
|
async start() {
|
|
370
462
|
try {
|
|
371
|
-
// Load environment variables
|
|
463
|
+
// Load environment variables
|
|
372
464
|
this.loadEnvFile();
|
|
373
465
|
// Set up monitoring and health checks
|
|
374
466
|
this.setupHealthCheck();
|
|
375
467
|
this.setupWatcher();
|
|
376
|
-
|
|
377
|
-
// Start worker cluster
|
|
468
|
+
// Start the application
|
|
378
469
|
await this.startCluster();
|
|
379
470
|
// Success message
|
|
380
|
-
const
|
|
381
|
-
const
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.green(figures_1.default.tick)} Server ready${
|
|
471
|
+
const port = this.options.port || 8000;
|
|
472
|
+
const actualWorkers = this.options.workers === 1 ? 1 : this.workers.size;
|
|
473
|
+
const workerInfo = this.options.workers === 1 ?
|
|
474
|
+
'' : ` (${actualWorkers} workers)`;
|
|
475
|
+
logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.green(figures_1.default.tick)} Server ready on port ${port}${workerInfo}`, 'info');
|
|
385
476
|
}
|
|
386
477
|
catch (error) {
|
|
387
478
|
this.log(`Failed to start server: ${error.message}`, 'error');
|
|
@@ -399,6 +490,17 @@ class StartManager {
|
|
|
399
490
|
if (this.healthServer) {
|
|
400
491
|
this.healthServer.close();
|
|
401
492
|
}
|
|
493
|
+
// Stop single process
|
|
494
|
+
if (this.masterProcess) {
|
|
495
|
+
try {
|
|
496
|
+
this.masterProcess.kill('SIGTERM');
|
|
497
|
+
await this.waitForProcessExit(this.masterProcess, this.options.gracefulTimeout);
|
|
498
|
+
}
|
|
499
|
+
catch (error) {
|
|
500
|
+
this.masterProcess.kill('SIGKILL');
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
// Stop workers
|
|
402
504
|
const shutdownPromises = [];
|
|
403
505
|
for (const [workerId, workerInfo] of this.workers.entries()) {
|
|
404
506
|
shutdownPromises.push((async () => {
|