forked from imbytecat/fullstack-starter
fix(desktop): guard shutdown race and kill sidecar process tree
This commit is contained in:
@@ -2,11 +2,13 @@ import { spawn } from 'node:child_process'
|
||||
import { createServer } from 'node:net'
|
||||
import { join } from 'node:path'
|
||||
import { app, BrowserWindow, shell } from 'electron'
|
||||
import killProcessTree from 'tree-kill'
|
||||
|
||||
const DEV_SERVER_URL = 'http://localhost:3000'
|
||||
|
||||
let mainWindow: BrowserWindow | null = null
|
||||
let serverProcess: ReturnType<typeof spawn> | null = null
|
||||
let isQuitting = false
|
||||
|
||||
const getAvailablePort = (): Promise<number> =>
|
||||
new Promise((resolve, reject) => {
|
||||
@@ -37,7 +39,7 @@ const waitForServer = async (
|
||||
timeoutMs = 15_000,
|
||||
): Promise<boolean> => {
|
||||
const start = Date.now()
|
||||
while (Date.now() - start < timeoutMs) {
|
||||
while (Date.now() - start < timeoutMs && !isQuitting) {
|
||||
if (await isServerReady(url)) return true
|
||||
await new Promise<void>((resolve) => setTimeout(resolve, 200))
|
||||
}
|
||||
@@ -56,7 +58,11 @@ const stopServerProcess = () => {
|
||||
return
|
||||
}
|
||||
|
||||
runningServer.kill()
|
||||
killProcessTree(runningServer.pid, (error?: Error) => {
|
||||
if (error) {
|
||||
console.error('Failed to stop server process:', error)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const spawnServer = (port: number): string => {
|
||||
@@ -84,11 +90,17 @@ const spawnServer = (port: number): string => {
|
||||
return `http://127.0.0.1:${port}`
|
||||
}
|
||||
|
||||
const getServerUrl = async (): Promise<string> => {
|
||||
const getServerUrl = async (): Promise<string | null> => {
|
||||
if (!app.isPackaged) {
|
||||
return DEV_SERVER_URL
|
||||
}
|
||||
|
||||
const port = await getAvailablePort()
|
||||
|
||||
if (isQuitting) {
|
||||
return null
|
||||
}
|
||||
|
||||
return spawnServer(port)
|
||||
}
|
||||
|
||||
@@ -120,9 +132,18 @@ const createWindow = async () => {
|
||||
mainWindow.show()
|
||||
|
||||
const serverUrl = await getServerUrl()
|
||||
if (!serverUrl || isQuitting || !mainWindow || mainWindow.isDestroyed()) {
|
||||
stopServerProcess()
|
||||
return
|
||||
}
|
||||
|
||||
console.log(`Waiting for server at ${serverUrl}...`)
|
||||
const ready = await waitForServer(serverUrl)
|
||||
if (isQuitting || !mainWindow || mainWindow.isDestroyed()) {
|
||||
stopServerProcess()
|
||||
return
|
||||
}
|
||||
|
||||
if (!ready) {
|
||||
console.error(
|
||||
app.isPackaged
|
||||
@@ -144,21 +165,19 @@ app.whenReady().then(createWindow)
|
||||
|
||||
app.on('window-all-closed', () => {
|
||||
if (process.platform !== 'darwin') {
|
||||
isQuitting = true
|
||||
stopServerProcess()
|
||||
if (process.platform === 'win32') {
|
||||
app.exit(0)
|
||||
return
|
||||
}
|
||||
app.quit()
|
||||
}
|
||||
})
|
||||
|
||||
app.on('activate', () => {
|
||||
if (BrowserWindow.getAllWindows().length === 0) {
|
||||
if (!isQuitting && BrowserWindow.getAllWindows().length === 0) {
|
||||
createWindow()
|
||||
}
|
||||
})
|
||||
|
||||
app.on('before-quit', () => {
|
||||
isQuitting = true
|
||||
stopServerProcess()
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user