From 862364d4d89613c3f78d064fe1aa5e59e8c1e4e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=9Cmit=20Tun=C3=A7?= Date: Mon, 30 Mar 2026 07:00:59 +0300 Subject: [PATCH] feat: implement IPC handlers for service management and add ProcessManager for background process control --- src/main/ipc/index.ts | 7 +++- src/main/services/ProcessManager.ts | 59 +++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 2 deletions(-) diff --git a/src/main/ipc/index.ts b/src/main/ipc/index.ts index 67044ad..6003a1f 100644 --- a/src/main/ipc/index.ts +++ b/src/main/ipc/index.ts @@ -168,10 +168,14 @@ export function registerIpcHandlers(): void { if (!binPath) return { success: false, message: 'MySQL executable bulunamadı. Lütfen indirin.' } + const mysqlPort = mysqlId ? (settings.mysqlPorts?.[mysqlId] || '3306') : settings.mysqlPort + // Force kill this specific version if running or generic mysql - // Actually processManager handles by serviceName await processManager.forceKillAll(serviceName) + // Also kill anything else listening on the same port + await processManager.killByPort(serviceName, mysqlPort) + // Initialize if data directory is empty if (!fs.existsSync(dataDir) || fs.readdirSync(dataDir).length === 0) { if (!fs.existsSync(dataDir)) fs.mkdirSync(dataDir, { recursive: true }) @@ -194,7 +198,6 @@ export function registerIpcHandlers(): void { } } - const mysqlPort = mysqlId ? (settings.mysqlPorts?.[mysqlId] || '3306') : settings.mysqlPort const sanitizedServiceName = serviceName.replace(/:/g, '_') const logFile = path.join(configService.getBasePath(), 'logs', `${sanitizedServiceName}_error.log`) diff --git a/src/main/services/ProcessManager.ts b/src/main/services/ProcessManager.ts index 6b63ad3..cc6b8ae 100644 --- a/src/main/services/ProcessManager.ts +++ b/src/main/services/ProcessManager.ts @@ -222,6 +222,65 @@ export class ProcessManager { }) } + async killByPort(name: string, port: string | number): Promise { + return new Promise((resolve) => { + if (!port) return resolve(true) + + this.addLog(name, `Checking if port ${port} is in use...`) + // findstr will return lines containing the port and "LISTENING" + const cmd = `netstat -ano | findstr :${port} | findstr LISTENING` + const netstat = spawn('cmd.exe', ['/c', cmd], { windowsHide: true, shell: false }) + + let output = '' + netstat.stdout?.on('data', (data) => { + output += data.toString() + }) + + netstat.on('close', () => { + const lines = output.split('\n').filter(l => l.trim()) + if (lines.length === 0) { + this.addLog(name, `Port ${port} is free.`) + return resolve(true) + } + + // netstat output format: TCP 0.0.0.0:3306 0.0.0.0:0 LISTENING 1234 + // We want the last part, which is the PID + const pids = new Set() + lines.forEach(line => { + const parts = line.trim().split(/\s+/) + const pid = parts[parts.length - 1] + if (pid && !isNaN(parseInt(pid)) && parseInt(pid) > 0) { + pids.add(pid) + } + }) + + if (pids.size === 0) { + this.addLog(name, `Port ${port} in use but PID not found.`) + return resolve(true) + } + + this.addLog(name, `Port ${port} is used by PIDs: ${Array.from(pids).join(', ')}. Terminating...`) + + const killProcesses = Array.from(pids).map(pid => { + return new Promise((res) => { + const tk = spawn('taskkill', ['/F', '/PID', pid, '/T'], { windowsHide: true, shell: false }) + tk.on('close', () => res()) + }) + }) + + Promise.all(killProcesses).then(() => { + this.addLog(name, `Successfully cleared port ${port}.`) + resolve(true) + }) + }) + + netstat.on('error', (err) => { + this.addLog(name, `Port check error: ${err.message}`) + resolve(true) + }) + }) + } + isServiceRunning(name: string): boolean { return this.processes.has(name) }