fix(server): refine error handling and PM2 connection logic
Deploy to Raspberry Pi / deploy (push) Canceled after 1m16s
Deploy to Raspberry Pi / deploy (push) Canceled after 1m16s
- Reduced the maximum restarts for PM2 processes from 20 to 8 and added a restart delay of 2000ms for better stability. - Improved error handling during server startup and PM2 connection, ensuring clearer logging and graceful exits on failure. - Introduced a utility function to filter out PM2 metadata from environment variables, preventing conflicts during process management.
This commit is contained in:
@@ -48,7 +48,8 @@ module.exports = {
|
|||||||
instances: 1,
|
instances: 1,
|
||||||
exec_mode: "fork",
|
exec_mode: "fork",
|
||||||
autorestart: true,
|
autorestart: true,
|
||||||
max_restarts: 20,
|
max_restarts: 8,
|
||||||
|
restart_delay: 2000,
|
||||||
env: {
|
env: {
|
||||||
...env,
|
...env,
|
||||||
JFLOW_CONTROL_PORT: env.JFLOW_CONTROL_PORT ?? "8600",
|
JFLOW_CONTROL_PORT: env.JFLOW_CONTROL_PORT ?? "8600",
|
||||||
|
|||||||
+18
-15
@@ -64,13 +64,6 @@ try {
|
|||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
|
||||||
await connectPm2();
|
|
||||||
} catch (err) {
|
|
||||||
log.error({ err }, "failed to connect to PM2 — is pm2 installed?");
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function applyDesiredState() {
|
async function applyDesiredState() {
|
||||||
const state = readControlState();
|
const state = readControlState();
|
||||||
await ensureHttp({
|
await ensureHttp({
|
||||||
@@ -98,8 +91,6 @@ async function applyDesiredState() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
await applyDesiredState();
|
|
||||||
|
|
||||||
const server = fastify({ loggerInstance: log });
|
const server = fastify({ loggerInstance: log });
|
||||||
await server.register(cookie);
|
await server.register(cookie);
|
||||||
await server.register(jwt, {
|
await server.register(jwt, {
|
||||||
@@ -484,12 +475,24 @@ process.on("SIGINT", shutdown);
|
|||||||
process.on("SIGTERM", shutdown);
|
process.on("SIGTERM", shutdown);
|
||||||
|
|
||||||
const port = Number(process.env.JFLOW_CONTROL_PORT ?? process.env.PORT ?? 8600);
|
const port = Number(process.env.JFLOW_CONTROL_PORT ?? process.env.PORT ?? 8600);
|
||||||
server
|
try {
|
||||||
.listen({ host: "0.0.0.0", port })
|
await server.listen({ host: "0.0.0.0", port });
|
||||||
.then(() => {
|
|
||||||
log.info(`Control is running on port ${port}`);
|
log.info(`Control is running on port ${port}`);
|
||||||
})
|
} catch (err) {
|
||||||
.catch((err) => {
|
|
||||||
log.error({ err }, "failed to start control");
|
log.error({ err }, "failed to start control");
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
});
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await connectPm2();
|
||||||
|
} catch (err) {
|
||||||
|
log.error({ err }, "failed to connect to PM2 — is pm2 installed?");
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await applyDesiredState();
|
||||||
|
} catch (err) {
|
||||||
|
log.error({ err }, "failed to apply desired state");
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|||||||
@@ -164,13 +164,57 @@ export async function restartPm2Process(pmId) {
|
|||||||
return { name: proc.name, pmId: id };
|
return { name: proc.name, pmId: id };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PM2 injects these into process.env of a managed app. Spreading them into
|
||||||
|
* `pm2.start({ env })` overwrites `name` / `pm_exec_path` so God restarts
|
||||||
|
* jflow-control instead of launching http/worker (EADDRINUSE :8600 loop).
|
||||||
|
*/
|
||||||
|
const PM2_META_KEYS = new Set([
|
||||||
|
"name",
|
||||||
|
"namespace",
|
||||||
|
"exec_mode",
|
||||||
|
"exec_interpreter",
|
||||||
|
"instances",
|
||||||
|
"instance_var",
|
||||||
|
"node_app_instance",
|
||||||
|
"unique_id",
|
||||||
|
"status",
|
||||||
|
"username",
|
||||||
|
"windowsHide",
|
||||||
|
"merge_logs",
|
||||||
|
"vizion",
|
||||||
|
"vizion_running",
|
||||||
|
"autostart",
|
||||||
|
"autorestart",
|
||||||
|
"automation",
|
||||||
|
"km_link",
|
||||||
|
]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {NodeJS.ProcessEnv} env
|
||||||
|
* @returns {NodeJS.ProcessEnv}
|
||||||
|
*/
|
||||||
|
export function withoutPm2Meta(env) {
|
||||||
|
/** @type {NodeJS.ProcessEnv} */
|
||||||
|
const out = {};
|
||||||
|
for (const [key, val] of Object.entries(env)) {
|
||||||
|
if (val == null) continue;
|
||||||
|
if (PM2_META_KEYS.has(key)) continue;
|
||||||
|
if (key.startsWith("pm_") || key.startsWith("axm_") || key.startsWith("PM2_")) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
out[key] = val;
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shared env for child processes.
|
* Shared env for child processes.
|
||||||
* @param {{ generation: number }} opts
|
* @param {{ generation: number }} opts
|
||||||
*/
|
*/
|
||||||
export function childEnv(opts) {
|
export function childEnv(opts) {
|
||||||
return {
|
return {
|
||||||
...process.env,
|
...withoutPm2Meta(process.env),
|
||||||
JFLOW_CONFIG_GENERATION: String(opts.generation),
|
JFLOW_CONFIG_GENERATION: String(opts.generation),
|
||||||
JFLOW_CORS_ORIGIN: process.env.JFLOW_CORS_ORIGIN ?? "http://localhost:8500",
|
JFLOW_CORS_ORIGIN: process.env.JFLOW_CORS_ORIGIN ?? "http://localhost:8500",
|
||||||
PORT: process.env.JFLOW_HTTP_PORT ?? "8700",
|
PORT: process.env.JFLOW_HTTP_PORT ?? "8700",
|
||||||
|
|||||||
Reference in New Issue
Block a user