Skip to content

Commit 5ccc92c

Browse files
authored
Child processes (#115)
* wip * build fixes * bump deps * cleanup * fix Native helper * fix * fix stdin * Strip websockets integration * Remove superfluous routes file * Build without websockets * Remove native module for now * code style * bump deps * Don't close stdin * updates - allow for persistent (self-restarting) processes - improve data stored in state (keep settings and pid in there too) - add get, all, and restart * code style * Pass pid back in spawn event
1 parent f654261 commit 5ccc92c

27 files changed

+1343
-1087
lines changed

resources/js/electron-plugin/dist/index.js

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { app } from "electron";
1111
import { autoUpdater } from "electron-updater";
1212
import state from "./server/state";
1313
import { electronApp, optimizer } from "@electron-toolkit/utils";
14-
import { retrieveNativePHPConfig, retrievePhpIniSettings, runScheduler, startAPI, startPhpApp, startQueue, startWebsockets, } from "./server";
14+
import { retrieveNativePHPConfig, retrievePhpIniSettings, runScheduler, startAPI, startPhpApp, startQueue, } from "./server";
1515
import { notifyLaravel } from "./server/utils";
1616
import { resolve } from "path";
1717
import ps from "ps-node";
@@ -74,7 +74,6 @@ class NativePHP {
7474
state.phpIni = yield this.loadPhpIni();
7575
yield this.startPhpApp();
7676
yield this.startQueueWorker();
77-
yield this.startWebsockets();
7877
this.startScheduler();
7978
yield notifyLaravel("booted");
8079
});
@@ -152,11 +151,6 @@ class NativePHP {
152151
this.processes.push(yield startQueue());
153152
});
154153
}
155-
startWebsockets() {
156-
return __awaiter(this, void 0, void 0, function* () {
157-
this.processes.push(yield startWebsockets());
158-
});
159-
}
160154
startScheduler() {
161155
const now = new Date();
162156
const delay = (60 - now.getSeconds()) * 1000 + (1000 - now.getMilliseconds());

resources/js/electron-plugin/dist/preload/index.js

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,16 @@
1-
import { ipcRenderer } from 'electron';
2-
import * as remote from '@electron/remote';
3-
import Native from './native';
1+
const { contextBridge, ipcRenderer } = require('electron');
2+
const remote = require('@electron/remote');
3+
const Native = {
4+
on: (event, callback) => {
5+
ipcRenderer.on('native-event', (_, data) => {
6+
event = event.replace(/^(\\)+/, '');
7+
data.event = data.event.replace(/^(\\)+/, '');
8+
if (event === data.event) {
9+
return callback(data.payload, event);
10+
}
11+
});
12+
}
13+
};
414
window.Native = Native;
515
window.remote = remote;
616
ipcRenderer.on('log', (event, { level, message, context }) => {

resources/js/electron-plugin/dist/server/api.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import settingsRoutes from "./api/settings";
3030
import shellRoutes from "./api/shell";
3131
import progressBarRoutes from "./api/progressBar";
3232
import powerMonitorRoutes from "./api/powerMonitor";
33+
import childProcessRoutes from "./api/childProcess";
3334
function startAPIServer(randomSecret) {
3435
return __awaiter(this, void 0, void 0, function* () {
3536
const port = yield getPort({
@@ -56,6 +57,7 @@ function startAPIServer(randomSecret) {
5657
httpServer.use("/api/menu-bar", menuBarRoutes);
5758
httpServer.use("/api/progress-bar", progressBarRoutes);
5859
httpServer.use("/api/power-monitor", powerMonitorRoutes);
60+
httpServer.use("/api/child-process", childProcessRoutes);
5961
httpServer.use("/api/broadcast", broadcastingRoutes);
6062
if (process.env.NODE_ENV === "development") {
6163
httpServer.use("/api/debug", debugRoutes);
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
import express from 'express';
2+
import { utilityProcess } from 'electron';
3+
import state from '../state';
4+
import { notifyLaravel } from "../utils";
5+
import { join } from 'path';
6+
const router = express.Router();
7+
function startProcess(settings) {
8+
const { alias, cmd, cwd, env, persistent } = settings;
9+
if (getProcess(alias) !== undefined) {
10+
return state.processes[alias];
11+
}
12+
const proc = utilityProcess.fork(join(__dirname, '../../electron-plugin/dist/server/childProcess.js'), cmd, {
13+
cwd,
14+
serviceName: alias,
15+
stdio: 'pipe',
16+
env: Object.assign(Object.assign({}, process.env), env)
17+
});
18+
proc.stdout.on('data', (data) => {
19+
notifyLaravel('events', {
20+
event: 'Native\\Laravel\\Events\\ChildProcess\\MessageReceived',
21+
payload: {
22+
alias,
23+
data: data.toString(),
24+
}
25+
});
26+
});
27+
proc.stderr.on('data', (data) => {
28+
console.error('Error received from process [' + alias + ']:', data.toString());
29+
notifyLaravel('events', {
30+
event: 'Native\\Laravel\\Events\\ChildProcess\\ErrorReceived',
31+
payload: {
32+
alias,
33+
data: data.toString(),
34+
}
35+
});
36+
});
37+
proc.on('spawn', () => {
38+
console.log('Process [' + alias + '] spawned!');
39+
state.processes[alias] = {
40+
pid: proc.pid,
41+
proc,
42+
settings
43+
};
44+
notifyLaravel('events', {
45+
event: 'Native\\Laravel\\Events\\ChildProcess\\ProcessSpawned',
46+
payload: [alias, proc.pid]
47+
});
48+
});
49+
proc.on('exit', (code) => {
50+
console.log(`Process [${alias}] exited with code [${code}].`);
51+
notifyLaravel('events', {
52+
event: 'Native\\Laravel\\Events\\ChildProcess\\ProcessExited',
53+
payload: {
54+
alias,
55+
code,
56+
}
57+
});
58+
delete state.processes[alias];
59+
if (persistent) {
60+
startProcess(settings);
61+
}
62+
});
63+
return {
64+
pid: null,
65+
proc,
66+
settings
67+
};
68+
}
69+
function stopProcess(alias) {
70+
const proc = getProcess(alias);
71+
if (proc === undefined) {
72+
return;
73+
}
74+
if (proc.kill()) {
75+
delete state.processes[alias];
76+
}
77+
}
78+
function getProcess(alias) {
79+
var _a;
80+
return (_a = state.processes[alias]) === null || _a === void 0 ? void 0 : _a.proc;
81+
}
82+
function getSettings(alias) {
83+
var _a;
84+
return (_a = state.processes[alias]) === null || _a === void 0 ? void 0 : _a.settings;
85+
}
86+
router.post('/start', (req, res) => {
87+
const proc = startProcess(req.body);
88+
res.json(proc);
89+
});
90+
router.post('/stop', (req, res) => {
91+
const { alias } = req.body;
92+
stopProcess(alias);
93+
res.sendStatus(200);
94+
});
95+
router.post('/restart', (req, res) => {
96+
const { alias } = req.body;
97+
const settings = getSettings(alias);
98+
stopProcess(alias);
99+
if (settings === undefined) {
100+
res.sendStatus(410);
101+
return;
102+
}
103+
const proc = startProcess(settings);
104+
res.json(proc);
105+
});
106+
router.get('/get/:alias', (req, res) => {
107+
const { alias } = req.params;
108+
const proc = state.processes[alias];
109+
if (proc === undefined) {
110+
res.sendStatus(410);
111+
return;
112+
}
113+
res.json(proc);
114+
});
115+
router.get('/', (req, res) => {
116+
res.json(state.processes);
117+
});
118+
router.post('/message', (req, res) => {
119+
const { alias, message } = req.body;
120+
const proc = getProcess(alias);
121+
if (proc === undefined) {
122+
res.sendStatus(200);
123+
return;
124+
}
125+
proc.postMessage(message);
126+
res.sendStatus(200);
127+
});
128+
export default router;

resources/js/electron-plugin/dist/server/api/screen.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,8 @@ router.get('/primary-display', (req, res) => {
1414
router.get('/cursor-position', (req, res) => {
1515
res.json(screen.getCursorScreenPoint());
1616
});
17+
router.get('/active', (req, res) => {
18+
const cursor = screen.getCursorScreenPoint();
19+
res.json(screen.getDisplayNearestPoint(cursor));
20+
});
1721
export default router;

resources/js/electron-plugin/dist/server/api/window.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ router.post('/open', (req, res) => {
7676
state.windows[id].focus();
7777
return res.sendStatus(200);
7878
}
79-
let preloadPath = join(__dirname, '../../preload/index.js');
79+
let preloadPath = join(__dirname, '../../electron-plugin/dist/preload/index.js');
8080
let windowState = undefined;
8181
if (req.body.rememberState === true) {
8282
windowState = windowStateKeeper({
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
const { spawn } = require('child_process');
2+
const proc = spawn(process.argv[2], process.argv.slice(3), {});
3+
process.parentPort.on('message', (message) => {
4+
proc.stdin.write(message.data);
5+
});
6+
proc.stdout.on('data', (data) => {
7+
console.log(data.toString());
8+
});
9+
proc.stderr.on('data', (data) => {
10+
console.error(data.toString());
11+
});
12+
proc.on('close', (code) => {
13+
process.exit(code);
14+
});

resources/js/electron-plugin/dist/server/index.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
77
step((generator = generator.apply(thisArg, _arguments || [])).next());
88
});
99
};
10-
import startWebsockets from "./websockets";
1110
import startAPIServer from "./api";
1211
import { retrieveNativePHPConfig, retrievePhpIniSettings, serveApp, startQueueWorker, startScheduler, } from "./php";
1312
import { appendCookie } from "./utils";
@@ -34,4 +33,4 @@ export function runScheduler() {
3433
export function startAPI() {
3534
return startAPIServer(state.randomSecret);
3635
}
37-
export { startWebsockets, retrieveNativePHPConfig, retrievePhpIniSettings };
36+
export { retrieveNativePHPConfig, retrievePhpIniSettings };

resources/js/electron-plugin/dist/server/php.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ function getArgumentEnv() {
8282
return env;
8383
}
8484
function getAppPath() {
85-
let appPath = join(__dirname, '../../resources/app/').replace('app.asar', 'app.asar.unpacked');
85+
let appPath = join(__dirname, '../../../../../resources/app/').replace('app.asar', 'app.asar.unpacked');
8686
if (process.env.NODE_ENV === 'development' || argumentEnv.TESTING == 1) {
8787
appPath = process.env.APP_PATH || argumentEnv.APP_PATH;
8888
}

resources/js/electron-plugin/dist/server/state.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ export default {
3232
icon: null,
3333
store: settingsStore,
3434
randomSecret: generateRandomString(32),
35+
processes: {},
3536
windows: {},
3637
findWindow(id) {
3738
return this.windows[id] || null;

resources/js/electron-plugin/src/index.ts

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import {
1010
startAPI,
1111
startPhpApp,
1212
startQueue,
13-
startWebsockets,
1413
} from "./server";
1514
import { notifyLaravel } from "./server/utils";
1615
import { resolve } from "path";
@@ -99,7 +98,6 @@ class NativePHP {
9998

10099
await this.startPhpApp();
101100
await this.startQueueWorker();
102-
await this.startWebsockets();
103101
this.startScheduler();
104102

105103
await notifyLaravel("booted");
@@ -186,10 +184,6 @@ class NativePHP {
186184
this.processes.push(await startQueue());
187185
}
188186

189-
private async startWebsockets() {
190-
this.processes.push(await startWebsockets());
191-
}
192-
193187
private startScheduler() {
194188
const now = new Date();
195189
const delay =

resources/js/electron-plugin/src/preload/index.ts

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,18 @@
1-
import { contextBridge, ipcRenderer } from 'electron'
2-
import * as remote from '@electron/remote'
1+
const { contextBridge, ipcRenderer } = require('electron')
2+
const remote = require('@electron/remote')
3+
const Native = {
4+
on: (event, callback) => {
5+
ipcRenderer.on('native-event', (_, data) => {
6+
// Strip leading slashes
7+
event = event.replace(/^(\\)+/, '');
8+
data.event = data.event.replace(/^(\\)+/, '');
39

4-
import Native from './native';
10+
if (event === data.event) {
11+
return callback(data.payload, event);
12+
}
13+
})
14+
}
15+
};
516

617
// @ts-ignore
718
window.Native = Native;

resources/js/electron-plugin/src/preload/native.ts

Lines changed: 0 additions & 16 deletions
This file was deleted.

resources/js/electron-plugin/src/server/api.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import settingsRoutes from "./api/settings";
2222
import shellRoutes from "./api/shell";
2323
import progressBarRoutes from "./api/progressBar";
2424
import powerMonitorRoutes from "./api/powerMonitor";
25+
import childProcessRoutes from "./api/childProcess";
2526
import { Server } from "net";
2627

2728
export interface APIProcess {
@@ -55,6 +56,7 @@ async function startAPIServer(randomSecret: string): Promise<APIProcess> {
5556
httpServer.use("/api/menu-bar", menuBarRoutes);
5657
httpServer.use("/api/progress-bar", progressBarRoutes);
5758
httpServer.use("/api/power-monitor", powerMonitorRoutes);
59+
httpServer.use("/api/child-process", childProcessRoutes);
5860
httpServer.use("/api/broadcast", broadcastingRoutes);
5961

6062
if (process.env.NODE_ENV === "development") {

0 commit comments

Comments
 (0)