fix(server): refine error handling and PM2 connection logic
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:
2026-08-23 08:45:26 +07:00
parent 69312c9080
commit dd98421b08
3 changed files with 68 additions and 20 deletions
+2 -1
View File
@@ -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",
+21 -18
View File
@@ -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) {
}) log.error({ err }, "failed to start control");
.catch((err) => { process.exit(1);
log.error({ err }, "failed to start control"); }
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);
}
+45 -1
View File
@@ -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",