badmfck-api-server 4.1.57 → 4.1.58
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/apiServer/deployment/DeployerService.d.ts +1 -0
- package/dist/apiServer/deployment/DeployerService.js +18 -1
- package/dist/apiServer/monitor/Monitor.d.ts +8 -1
- package/dist/apiServer/monitor/Monitor.js +23 -2
- package/dist/apiServer/monitor/ProcessMonitor.d.ts +16 -0
- package/dist/apiServer/monitor/ProcessMonitor.js +13 -0
- package/dist/apiServer/monitor/index.html +51 -51
- package/dist/index.d.ts +3 -2
- package/dist/index.js +4 -1
- package/package.json +1 -1
|
@@ -122,6 +122,7 @@ export declare class DeployerService extends BaseService {
|
|
|
122
122
|
private static _extractPort;
|
|
123
123
|
private _renderNginx;
|
|
124
124
|
runPM2(found: IConfig, projectState?: IProjectState | null): Promise<IError | null>;
|
|
125
|
+
private _verifyAutorestart;
|
|
125
126
|
private static readonly SKIP_DIRS;
|
|
126
127
|
private static readonly TEMPLATE_EXTENSIONS;
|
|
127
128
|
applyConfig(dir: string, config: Record<string, string>): Promise<void>;
|
|
@@ -496,9 +496,12 @@ class DeployerService extends BaseService_1.BaseService {
|
|
|
496
496
|
return new Promise((resolve) => {
|
|
497
497
|
try {
|
|
498
498
|
(0, child_process_1.execFileSync)("pm2", ["--version"]);
|
|
499
|
-
const
|
|
499
|
+
const rawExtra = Array.isArray(pm2Attrs)
|
|
500
500
|
? pm2Attrs.filter(s => typeof s === "string" && s.length > 0)
|
|
501
501
|
: [];
|
|
502
|
+
const extra = rawExtra.filter(a => !/^--no-autorestart(=.*)?$/.test(a));
|
|
503
|
+
if (extra.length !== rawExtra.length)
|
|
504
|
+
(0, LogService_1.logWarn)(`Ignored "--no-autorestart" in pm2 attributes for "${found.name}" — autorestart is force-kept ON so the slot self-recovers.`);
|
|
502
505
|
const ecoJs = path_1.default.resolve(found.destination, "ecosystem.config.js");
|
|
503
506
|
const ecoCjs = path_1.default.resolve(found.destination, "ecosystem.config.cjs");
|
|
504
507
|
const ecosystem = fs_1.default.existsSync(ecoJs) ? ecoJs
|
|
@@ -506,6 +509,7 @@ class DeployerService extends BaseService_1.BaseService {
|
|
|
506
509
|
: null;
|
|
507
510
|
if (ecosystem) {
|
|
508
511
|
(0, child_process_1.execFileSync)("pm2", ["startOrReload", ecosystem, "--update-env", ...extra]);
|
|
512
|
+
this._verifyAutorestart(found.name);
|
|
509
513
|
resolve(null);
|
|
510
514
|
return;
|
|
511
515
|
}
|
|
@@ -530,6 +534,7 @@ class DeployerService extends BaseService_1.BaseService {
|
|
|
530
534
|
catch { }
|
|
531
535
|
}
|
|
532
536
|
(0, child_process_1.execFileSync)("pm2", ["start", found.destination + main, "--name", found.name, ...extra]);
|
|
537
|
+
this._verifyAutorestart(found.name);
|
|
533
538
|
resolve(null);
|
|
534
539
|
}
|
|
535
540
|
catch (e) {
|
|
@@ -537,6 +542,18 @@ class DeployerService extends BaseService_1.BaseService {
|
|
|
537
542
|
}
|
|
538
543
|
});
|
|
539
544
|
}
|
|
545
|
+
_verifyAutorestart(name) {
|
|
546
|
+
try {
|
|
547
|
+
const list = JSON.parse((0, child_process_1.execFileSync)("pm2", ["jlist"]).toString());
|
|
548
|
+
const proc = Array.isArray(list) ? list.find((p) => p?.name === name) : null;
|
|
549
|
+
if (proc?.pm2_env && proc.pm2_env.autorestart === false) {
|
|
550
|
+
(0, LogService_1.logWarn)(`pm2 autorestart is DISABLED for "${name}" — the slot will NOT respawn after a restart/exit. Check the project's ecosystem.config (autorestart:false).`);
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
catch (e) {
|
|
554
|
+
(0, LogService_1.logWarn)(`Could not verify pm2 autorestart for "${name}": ${e?.message ?? e}`);
|
|
555
|
+
}
|
|
556
|
+
}
|
|
540
557
|
static SKIP_DIRS = new Set(["node_modules", ".git", "__tmp"]);
|
|
541
558
|
static TEMPLATE_EXTENSIONS = [".js", ".ts", ".json", ".env"];
|
|
542
559
|
async applyConfig(dir, config) {
|
|
@@ -6,6 +6,12 @@ export interface IUserAction {
|
|
|
6
6
|
action: string;
|
|
7
7
|
}
|
|
8
8
|
export declare const S_MONITOR_REGISTRATE_ACTION: Signal<IUserAction>;
|
|
9
|
+
export interface IRestartRequest {
|
|
10
|
+
by: string;
|
|
11
|
+
name: string | null;
|
|
12
|
+
pmId: string | null;
|
|
13
|
+
}
|
|
14
|
+
export declare const S_MONITOR_RESTART_REQUESTED: Signal<IRestartRequest>;
|
|
9
15
|
export declare class Monitor extends BaseEndpoint {
|
|
10
16
|
ignoreHttpLogging: boolean;
|
|
11
17
|
ignoreInDocumentation: boolean;
|
|
@@ -24,10 +30,11 @@ export declare class Monitor extends BaseEndpoint {
|
|
|
24
30
|
sysinfo(req: HTTPRequestVO): Promise<TransferPacketVO>;
|
|
25
31
|
procinfo(req: HTTPRequestVO): Promise<TransferPacketVO>;
|
|
26
32
|
fdreport(req: HTTPRequestVO): Promise<TransferPacketVO>;
|
|
33
|
+
restart(req: HTTPRequestVO): Promise<TransferPacketVO>;
|
|
27
34
|
httpstats(req: HTTPRequestVO): Promise<TransferPacketVO>;
|
|
28
35
|
inventory(req: HTTPRequestVO): Promise<TransferPacketVO>;
|
|
29
36
|
metrics(req: HTTPRequestVO): Promise<TransferPacketVO>;
|
|
30
37
|
connections(req: HTTPRequestVO): Promise<TransferPacketVO>;
|
|
31
38
|
db(req: HTTPRequestVO): Promise<TransferPacketVO>;
|
|
32
|
-
checkAuthentication(req: HTTPRequestVO): Promise<
|
|
39
|
+
checkAuthentication(req: HTTPRequestVO): Promise<string>;
|
|
33
40
|
}
|
|
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.Monitor = exports.S_MONITOR_REGISTRATE_ACTION = void 0;
|
|
6
|
+
exports.Monitor = exports.S_MONITOR_RESTART_REQUESTED = exports.S_MONITOR_REGISTRATE_ACTION = void 0;
|
|
7
7
|
const badmfck_signal_1 = require("badmfck-signal");
|
|
8
8
|
const APIService_1 = require("../APIService");
|
|
9
9
|
const BaseEndpoint_1 = require("../BaseEndpoint");
|
|
@@ -19,6 +19,7 @@ const DBService_1 = require("../DBService");
|
|
|
19
19
|
const SystemInfo_1 = require("./SystemInfo");
|
|
20
20
|
const ProcessMonitor_1 = require("./ProcessMonitor");
|
|
21
21
|
exports.S_MONITOR_REGISTRATE_ACTION = new badmfck_signal_1.Signal();
|
|
22
|
+
exports.S_MONITOR_RESTART_REQUESTED = new badmfck_signal_1.Signal();
|
|
22
23
|
const logMap = {
|
|
23
24
|
ALL: 10,
|
|
24
25
|
API: 19,
|
|
@@ -46,6 +47,9 @@ class Monitor extends BaseEndpoint_1.BaseEndpoint {
|
|
|
46
47
|
{ ignoreInterceptor: true, endpoint: "sysinfo", handler: this.sysinfo },
|
|
47
48
|
{ ignoreInterceptor: true, endpoint: "procinfo", handler: this.procinfo },
|
|
48
49
|
{ ignoreInterceptor: true, endpoint: "fdreport", handler: this.fdreport },
|
|
50
|
+
{ ignoreInterceptor: true, endpoint: "restart", handler: this.restart, validationModel: {
|
|
51
|
+
confirm: ""
|
|
52
|
+
} },
|
|
49
53
|
{ ignoreInterceptor: true, endpoint: "inventory", handler: this.inventory },
|
|
50
54
|
{ ignoreInterceptor: true, endpoint: "httpstats", handler: this.httpstats },
|
|
51
55
|
{ ignoreInterceptor: true, endpoint: "metrics", handler: this.metrics, validationModel: {
|
|
@@ -145,6 +149,23 @@ class Monitor extends BaseEndpoint_1.BaseEndpoint {
|
|
|
145
149
|
await this.checkAuthentication(req);
|
|
146
150
|
return { data: await ProcessMonitor_1.ProcessMonitor.getFdReport() };
|
|
147
151
|
}
|
|
152
|
+
async restart(req) {
|
|
153
|
+
const login = await this.checkAuthentication(req);
|
|
154
|
+
const id = ProcessMonitor_1.ProcessMonitor.getPm2Identity();
|
|
155
|
+
if (!id.managed)
|
|
156
|
+
throw { ...DefaultErrors_1.default.BAD_REQUEST, details: "Process is not managed by pm2 (no pm_id in env) — refusing to exit, nothing would restart it." };
|
|
157
|
+
if (req.data.confirm !== id.name)
|
|
158
|
+
throw { ...DefaultErrors_1.default.BAD_REQUEST, details: `Confirmation mismatch. To restart send { "confirm": "${id.name}" }.` };
|
|
159
|
+
exports.S_MONITOR_RESTART_REQUESTED.invoke({ by: login, name: id.name, pmId: id.pmId });
|
|
160
|
+
exports.S_MONITOR_REGISTRATE_ACTION.invoke({ action: `restart requested by "${login}" for pm2 "${id.name}" (id ${id.pmId})` });
|
|
161
|
+
(0, LogService_1.logInfo)(`Restart requested via monitor by "${login}" for pm2 process "${id.name}" (id ${id.pmId})`, "Monitor");
|
|
162
|
+
const graceMs = 750;
|
|
163
|
+
setTimeout(() => {
|
|
164
|
+
(0, LogService_1.logInfo)(`Exiting now for pm2 restart (requested by "${login}")`, "Monitor");
|
|
165
|
+
process.exit(0);
|
|
166
|
+
}, graceMs);
|
|
167
|
+
return { data: { restarting: true, name: id.name, pmId: id.pmId, by: login, graceMs } };
|
|
168
|
+
}
|
|
148
169
|
async httpstats(req) {
|
|
149
170
|
await this.checkAuthentication(req);
|
|
150
171
|
return { data: Http_1.Http.getHttpStats() };
|
|
@@ -201,7 +222,7 @@ class Monitor extends BaseEndpoint_1.BaseEndpoint {
|
|
|
201
222
|
for (let i of users) {
|
|
202
223
|
const hash = crypto_1.default.createHash("sha256").update(i.login + ":" + i.password).digest("hex");
|
|
203
224
|
if (hash === header) {
|
|
204
|
-
return;
|
|
225
|
+
return i.login;
|
|
205
226
|
}
|
|
206
227
|
}
|
|
207
228
|
throw { ...DefaultErrors_1.default.UNAUTHORIZED, details: "Invalid user" };
|
|
@@ -37,6 +37,14 @@ export declare class ProcessMonitor {
|
|
|
37
37
|
startedAtUnix: number;
|
|
38
38
|
uptimeSec: number;
|
|
39
39
|
availableParallelism: number;
|
|
40
|
+
pm2: {
|
|
41
|
+
managed: boolean;
|
|
42
|
+
name: string | null;
|
|
43
|
+
pmId: string | null;
|
|
44
|
+
instanceId: string | null;
|
|
45
|
+
execPath: string | null;
|
|
46
|
+
cwd: string | null;
|
|
47
|
+
};
|
|
40
48
|
};
|
|
41
49
|
memory: {
|
|
42
50
|
rss: number;
|
|
@@ -117,6 +125,14 @@ export declare class ProcessMonitor {
|
|
|
117
125
|
unit: string;
|
|
118
126
|
}> | null;
|
|
119
127
|
}>;
|
|
128
|
+
static getPm2Identity(): {
|
|
129
|
+
managed: boolean;
|
|
130
|
+
name: string | null;
|
|
131
|
+
pmId: string | null;
|
|
132
|
+
instanceId: string | null;
|
|
133
|
+
execPath: string | null;
|
|
134
|
+
cwd: string | null;
|
|
135
|
+
};
|
|
120
136
|
static getFdReport(): Promise<{
|
|
121
137
|
now: {
|
|
122
138
|
ts: number;
|
|
@@ -198,6 +198,7 @@ class ProcessMonitor {
|
|
|
198
198
|
availableParallelism: typeof os_1.default.availableParallelism === "function"
|
|
199
199
|
? os_1.default.availableParallelism()
|
|
200
200
|
: os_1.default.cpus().length,
|
|
201
|
+
pm2: this.getPm2Identity(),
|
|
201
202
|
},
|
|
202
203
|
memory: {
|
|
203
204
|
rss: mem.rss,
|
|
@@ -243,6 +244,18 @@ class ProcessMonitor {
|
|
|
243
244
|
limits: limitsParsed,
|
|
244
245
|
};
|
|
245
246
|
}
|
|
247
|
+
static getPm2Identity() {
|
|
248
|
+
const env = process.env;
|
|
249
|
+
const pmId = env.pm_id ?? null;
|
|
250
|
+
return {
|
|
251
|
+
managed: pmId !== null && pmId !== "",
|
|
252
|
+
name: env.name ?? null,
|
|
253
|
+
pmId,
|
|
254
|
+
instanceId: env.NODE_APP_INSTANCE ?? null,
|
|
255
|
+
execPath: env.pm_exec_path ?? null,
|
|
256
|
+
cwd: env.pm_cwd ?? null,
|
|
257
|
+
};
|
|
258
|
+
}
|
|
246
259
|
static async getFdReport() {
|
|
247
260
|
const [sockets, limits, fdOpen] = await Promise.all([
|
|
248
261
|
this._readSocketBreakdown(),
|