diff --git a/resources/js/electron-plugin/dist/index.js b/resources/js/electron-plugin/dist/index.js index b4a5746d..ec24f5b8 100644 --- a/resources/js/electron-plugin/dist/index.js +++ b/resources/js/electron-plugin/dist/index.js @@ -14,6 +14,7 @@ import { electronApp, optimizer } from "@electron-toolkit/utils"; import { retrieveNativePHPConfig, retrievePhpIniSettings, runScheduler, startAPI, startPhpApp, startQueue, } from "./server"; import { notifyLaravel } from "./server/utils"; import { resolve } from "path"; +import { stopAllProcesses } from "./server/api/childProcess"; import ps from "ps-node"; class NativePHP { constructor() { @@ -50,6 +51,7 @@ class NativePHP { if (this.schedulerInterval) { clearInterval(this.schedulerInterval); } + stopAllProcesses(); this.killChildProcesses(); }); app.on("browser-window-created", (_, window) => { diff --git a/resources/js/electron-plugin/dist/server/api/childProcess.js b/resources/js/electron-plugin/dist/server/api/childProcess.js index 8505d449..9f2228c5 100644 --- a/resources/js/electron-plugin/dist/server/api/childProcess.js +++ b/resources/js/electron-plugin/dist/server/api/childProcess.js @@ -1,9 +1,19 @@ +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; import express from 'express'; import { utilityProcess } from 'electron'; import state from '../state'; import { notifyLaravel } from "../utils"; import { join } from 'path'; const router = express.Router(); +const killSync = require('kill-sync'); function startProcess(settings) { const { alias, cmd, cwd, env, persistent } = settings; if (getProcess(alias) !== undefined) { @@ -55,8 +65,10 @@ function startProcess(settings) { code, } }); + const settings = Object.assign({}, getSettings(alias)); delete state.processes[alias]; - if (persistent) { + if (settings.persistent) { + console.log('Process [' + alias + '] wathchdog restarting...'); startProcess(settings); } }); @@ -71,8 +83,14 @@ function stopProcess(alias) { if (proc === undefined) { return; } - if (proc.kill()) { - delete state.processes[alias]; + state.processes[alias].settings.persistent = false; + console.log('Process [' + alias + '] stopping with PID [' + proc.pid + '].'); + killSync(proc.pid, 'SIGTERM', true); + proc.kill(); +} +export function stopAllProcesses() { + for (const alias in state.processes) { + stopProcess(alias); } } function getProcess(alias) { @@ -92,17 +110,28 @@ router.post('/stop', (req, res) => { stopProcess(alias); res.sendStatus(200); }); -router.post('/restart', (req, res) => { +router.post('/restart', (req, res) => __awaiter(void 0, void 0, void 0, function* () { const { alias } = req.body; - const settings = getSettings(alias); + const settings = Object.assign({}, getSettings(alias)); stopProcess(alias); if (settings === undefined) { res.sendStatus(410); return; } + const waitForProcessDeletion = (timeout, retry) => __awaiter(void 0, void 0, void 0, function* () { + const start = Date.now(); + while (state.processes[alias] !== undefined) { + if (Date.now() - start > timeout) { + return; + } + yield new Promise(resolve => setTimeout(resolve, retry)); + } + }); + yield waitForProcessDeletion(5000, 100); + console.log('Process [' + alias + '] restarting...'); const proc = startProcess(settings); res.json(proc); -}); +})); router.get('/get/:alias', (req, res) => { const { alias } = req.params; const proc = state.processes[alias]; diff --git a/resources/js/electron-plugin/src/index.ts b/resources/js/electron-plugin/src/index.ts index 3187c354..2daccdca 100644 --- a/resources/js/electron-plugin/src/index.ts +++ b/resources/js/electron-plugin/src/index.ts @@ -13,6 +13,7 @@ import { } from "./server"; import { notifyLaravel } from "./server/utils"; import { resolve } from "path"; +import { stopAllProcesses } from "./server/api/childProcess"; import ps from "ps-node"; class NativePHP { @@ -58,9 +59,12 @@ class NativePHP { app.on("before-quit", () => { if (this.schedulerInterval) { - clearInterval(this.schedulerInterval); + clearInterval(this.schedulerInterval); } + // close all child processes from the app + stopAllProcesses(); + this.killChildProcesses(); }); diff --git a/resources/js/electron-plugin/src/server/api/childProcess.ts b/resources/js/electron-plugin/src/server/api/childProcess.ts index dcc8382b..0edf064a 100644 --- a/resources/js/electron-plugin/src/server/api/childProcess.ts +++ b/resources/js/electron-plugin/src/server/api/childProcess.ts @@ -5,6 +5,7 @@ import { notifyLaravel } from "../utils"; import { join } from 'path'; const router = express.Router(); +const killSync = require('kill-sync'); function startProcess(settings) { const {alias, cmd, cwd, env, persistent} = settings; @@ -75,9 +76,12 @@ function startProcess(settings) { } }); + const settings = {...getSettings(alias)}; + delete state.processes[alias]; - if (persistent) { + if (settings.persistent) { + console.log('Process [' + alias + '] wathchdog restarting...'); startProcess(settings); } }); @@ -96,8 +100,18 @@ function stopProcess(alias) { return; } - if (proc.kill()) { - delete state.processes[alias]; + // Set persistent to false to prevent the process from restarting. + state.processes[alias].settings.persistent = false; + + console.log('Process [' + alias + '] stopping with PID [' + proc.pid + '].'); + + killSync(proc.pid, 'SIGTERM', true); // Kill tree + proc.kill(); // Does not work but just in case. (do not put before killSync) +} + +export function stopAllProcesses() { + for (const alias in state.processes) { + stopProcess(alias); } } @@ -123,10 +137,10 @@ router.post('/stop', (req, res) => { res.sendStatus(200); }); -router.post('/restart', (req, res) => { +router.post('/restart', async (req, res) => { const {alias} = req.body; - const settings = getSettings(alias); + const settings = {...getSettings(alias)}; stopProcess(alias); @@ -135,8 +149,21 @@ router.post('/restart', (req, res) => { return; } - const proc = startProcess(settings); + // Wait for the process to stop with a timeout of 5s + const waitForProcessDeletion = async (timeout, retry) => { + const start = Date.now(); + while (state.processes[alias] !== undefined) { + if (Date.now() - start > timeout) { + return; + } + await new Promise(resolve => setTimeout(resolve, retry)); + } + }; + + await waitForProcessDeletion(5000, 100); + console.log('Process [' + alias + '] restarting...'); + const proc = startProcess(settings); res.json(proc); }); diff --git a/resources/js/package-lock.json b/resources/js/package-lock.json index d58938a5..13143da4 100644 --- a/resources/js/package-lock.json +++ b/resources/js/package-lock.json @@ -23,6 +23,7 @@ "express": "^4.18.2", "fs-extra": "^11.1.1", "get-port": "^5.1.1", + "kill-sync": "^1.0.3", "menubar": "^9.4.0", "ps-node": "^0.1.6", "tree-kill": "^1.2.2", @@ -10926,6 +10927,12 @@ "json-buffer": "3.0.1" } }, + "node_modules/kill-sync": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/kill-sync/-/kill-sync-1.0.3.tgz", + "integrity": "sha512-3UKwyIpWxg2WGrYyN3f+Fh68QZ4WApTHWwEnyeDK96HSRy9In9UQvanCwIsaLQV7gOzczotBeoX8jgPNZ1g2Zw==", + "license": "MIT" + }, "node_modules/kind-of": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", diff --git a/resources/js/package.json b/resources/js/package.json index 73a4bac3..a593ba83 100644 --- a/resources/js/package.json +++ b/resources/js/package.json @@ -46,6 +46,7 @@ "express": "^4.18.2", "fs-extra": "^11.1.1", "get-port": "^5.1.1", + "kill-sync": "^1.0.3", "menubar": "^9.4.0", "ps-node": "^0.1.6", "tree-kill": "^1.2.2", diff --git a/resources/js/yarn.lock b/resources/js/yarn.lock index 74e72e13..40dcc540 100644 --- a/resources/js/yarn.lock +++ b/resources/js/yarn.lock @@ -1103,15 +1103,15 @@ minimatch "^3.0.4" plist "^3.0.4" -"@esbuild/darwin-arm64@0.18.20": +"@esbuild/darwin-x64@0.18.20": version "0.18.20" - resolved "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz" - integrity sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA== + resolved "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz" + integrity sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ== -"@esbuild/darwin-arm64@0.21.5": +"@esbuild/darwin-x64@0.21.5": version "0.21.5" - resolved "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz" - integrity sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ== + resolved "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz" + integrity sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw== "@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0": version "4.4.1" @@ -4940,6 +4940,11 @@ keyv@^4.0.0, keyv@^4.5.3: dependencies: json-buffer "3.0.1" +kill-sync@^1.0.3: + version "1.0.3" + resolved "https://registry.npmjs.org/kill-sync/-/kill-sync-1.0.3.tgz" + integrity sha512-3UKwyIpWxg2WGrYyN3f+Fh68QZ4WApTHWwEnyeDK96HSRy9In9UQvanCwIsaLQV7gOzczotBeoX8jgPNZ1g2Zw== + kind-of@^6.0.2, kind-of@^6.0.3: version "6.0.3" resolved "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz"