diff --git a/src/main/ipc/index.ts b/src/main/ipc/index.ts index 37602b1..d03b149 100644 --- a/src/main/ipc/index.ts +++ b/src/main/ipc/index.ts @@ -115,32 +115,43 @@ export function registerIpcHandlers(): void { return { success, message: success ? `${phpVersion || ''} PHP başlatıldı.` : `${phpVersion || ''} PHP devreye alınamadı.` } } - if (serviceName === 'mysql') { - const dataDir = path.join(app.getPath('userData'), 'mysql_data') - const binPath = findExecutable(binDir, 'mysqld.exe') + if (serviceName.startsWith('mysql')) { + const versionMatch = serviceName.match(/^mysql:(.+)$/) + const mysqlVersion = versionMatch ? versionMatch[1] : null + const mysqlId = mysqlVersion ? `mysql-${mysqlVersion}` : null + + const dataDir = mysqlId + ? path.join(app.getPath('userData'), `mysql_data_${mysqlId}`) + : path.join(app.getPath('userData'), 'mysql_data') + + const mysqlDir = mysqlId ? path.join(binDir, mysqlId) : binDir + const binPath = findExecutable(mysqlDir, 'mysqld.exe') if (!binPath) return { success: false, message: 'MySQL executable bulunamadı. Lütfen indirin.' } - // Force kill any existing instances before starting - await processManager.forceKillAll('mysql') + // Force kill this specific version if running or generic mysql + // Actually processManager handles by serviceName + await processManager.forceKillAll(serviceName) // Initialize if data directory is empty if (!fs.existsSync(dataDir) || fs.readdirSync(dataDir).length === 0) { if (!fs.existsSync(dataDir)) fs.mkdirSync(dataDir, { recursive: true }) - console.log('Initializing MySQL data directory...') + console.log(`Initializing MySQL data directory for ${serviceName}...`) await processManager.runOnce(binPath, ['--initialize-insecure', `--datadir=${dataDir.replace(/\\/g, '/')}`]) } - const logFile = path.join(app.getPath('userData'), 'logs', 'mysql_error.log') - const configPath = await configService.generateConfig('my.ini.template', 'my.ini', { - PORT: settings.mysqlPort, + const mysqlPort = mysqlId ? (settings.mysqlPorts?.[mysqlId] || '3306') : settings.mysqlPort + const logFile = path.join(app.getPath('userData'), 'logs', `${serviceName}_error.log`) + + const configPath = await configService.generateConfig('my.ini.template', `my_${mysqlId || 'default'}.ini`, { + PORT: mysqlPort, DATADIR: dataDir.replace(/\\/g, '/'), - SOCKET: '/tmp/mysql.sock', + SOCKET: `/tmp/${serviceName}.sock`, LOG_FILE: logFile.replace(/\\/g, '/') }) - const success = await processManager.startService('mysql', binPath, ['--defaults-file=' + configPath], false) - return { success, message: success ? 'MySQL başlatıldı.' : 'MySQL devreye alınamadı (Hata loglarını kontrol edin).' } + const success = await processManager.startService(serviceName, binPath, ['--defaults-file=' + configPath], false) + return { success, message: success ? `${mysqlVersion || ''} MySQL başlatıldı.` : `${mysqlVersion || ''} MySQL devreye alınamadı.` } } return { success: false, message: 'Bilinmeyen servis.' } diff --git a/src/main/services/ConfigService.ts b/src/main/services/ConfigService.ts index b9e2eb2..ef14871 100644 --- a/src/main/services/ConfigService.ts +++ b/src/main/services/ConfigService.ts @@ -10,6 +10,7 @@ export class ConfigService { nginxPort: '80', phpPort: '9001', mysqlPort: '3306', + mysqlPorts: {} as Record, language: 'tr' } diff --git a/src/main/services/MySqlManagerService.ts b/src/main/services/MySqlManagerService.ts index f21c27f..b53778f 100644 --- a/src/main/services/MySqlManagerService.ts +++ b/src/main/services/MySqlManagerService.ts @@ -1,6 +1,7 @@ import fs from 'fs' import path from 'path' import { app } from 'electron' +import { configService } from './ConfigService' export interface MySqlVersion { id: string @@ -9,6 +10,7 @@ export interface MySqlVersion { url: string status: 'available' | 'downloading' | 'installed' progress: number + port: string description?: string } @@ -55,6 +57,9 @@ export class MySqlManagerService { // Working CDN Pattern: https://cdn.mysql.com/archives/mysql-MAJOR.MINOR/mysql-VERSION-winx64.zip const url = `https://cdn.mysql.com/archives/mysql-${majorMinor}/mysql-${version}-winx64.zip` + const settings = configService.getSettings() + const customPort = settings.mysqlPorts?.[`mysql-${version}`] || '3306' + return { id: `mysql-${version}`, version, @@ -62,6 +67,7 @@ export class MySqlManagerService { url, status: 'available', progress: 0, + port: customPort, description: v.status } }) diff --git a/src/renderer/src/App.tsx b/src/renderer/src/App.tsx index 68b538f..556239b 100644 --- a/src/renderer/src/App.tsx +++ b/src/renderer/src/App.tsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect, Fragment } from 'react' +import React, { useState, useEffect } from 'react' import logo from './assets/logo.svg' import { Box, @@ -79,15 +79,14 @@ declare global { const drawerWidth = 240 interface ServiceStatus { - nginx: string - php: string - mysql: string + [key: string]: string } interface AppSettings { nginxPort: string phpPort: string mysqlPort: string + mysqlPorts?: Record language: string } @@ -330,20 +329,25 @@ function App(): JSX.Element { const handleStartAll = async () => { if (!window.api) return - const criticalServices = ['nginx', 'mysql'] + const criticalServices = ['nginx'] for (const s of criticalServices) { if (status[s] === 'stopped') setStatus(prev => ({ ...prev, [s]: 'starting' })) } - // Also starting each installed PHP version const phpToStart = phpVersions.filter(v => v.status === 'installed').map(v => `php:${v.version}`) for (const s of phpToStart) { if ((status[s] || 'stopped') === 'stopped') setStatus(prev => ({ ...prev, [s]: 'starting' })) } + const mysqlToStart = mySqlVersions.filter(v => v.status === 'installed').map(v => `mysql:${v.version}`) + for (const s of mysqlToStart) { + if ((status[s] || 'stopped') === 'stopped') setStatus(prev => ({ ...prev, [s]: 'starting' })) + } + await Promise.all([ ...criticalServices.filter(s => status[s] !== 'running').map(s => window.api?.startService(s)), - ...phpToStart.filter(s => (status[s] || 'stopped') !== 'running').map(s => window.api?.startService(s)) + ...phpToStart.filter(s => (status[s] || 'stopped') !== 'running').map(s => window.api?.startService(s)), + ...mysqlToStart.filter(s => (status[s] || 'stopped') !== 'running').map(s => window.api?.startService(s)) ]) fetchStatus() @@ -352,12 +356,15 @@ function App(): JSX.Element { const handleStopAll = async () => { if (!window.api) return - const criticalServices = ['nginx', 'mysql'] + const criticalServices = ['nginx'] const phpToStop = Object.keys(status).filter(s => s.startsWith('php:') && (status[s] === 'running' || status[s] === 'starting')) + const mysqlToStop = Object.keys(status).filter(s => s.startsWith('mysql:') && (status[s] === 'running' || status[s] === 'starting')) await Promise.all([ ...criticalServices.filter(s => status[s] === 'running' || status[s] === 'starting').map(s => window.api?.stopService(s)), - ...phpToStop.map(s => window.api?.stopService(s)) + ...phpToStop.map(s => window.api?.stopService(s)), + ...mysqlToStop.map(s => window.api?.stopService(s)), + window.api?.stopService('mysql') // Also stop legacy if any ]) fetchStatus() @@ -610,20 +617,35 @@ function App(): JSX.Element { secondary={v.status === 'downloading' ? `İndiriliyor... %${Math.round(v.progress)}` : v.status === 'installed' ? 'Kurulu' : 'İndirilebilir'} sx={{ flexGrow: 1 }} /> - - {v.status === 'available' && ( - - )} - {v.status === 'downloading' && ( - - - - )} + {v.status === 'installed' && ( - + { + const newPorts = { ...(settings.mysqlPorts || {}), [v.id]: e.target.value } + setSettings({ ...settings, mysqlPorts: newPorts }) + }} + variant="outlined" + size="small" + sx={{ width: 80 }} + /> )} + + {v.status === 'available' && ( + + )} + {v.status === 'downloading' && ( + + + + )} + {v.status === 'installed' && ( + + )} + ))} @@ -715,64 +737,99 @@ function App(): JSX.Element { Hızlı Bakış - - - - - - - handleOpenLogs('nginx')} title="Logları Görüntüle"> + + + {/* Nginx Box */} + + + + + {getStatusChip(status.nginx)} + + Nginx + Port: {settings.nginxPort} + + handleOpenLogs('nginx')} size="small" sx={{ bgcolor: 'rgba(255,255,255,0.05)' }}> - {getStatusChip(status.nginx)} handleToggleService('nginx')} disabled={status.nginx === 'starting'} /> - - {installedPhpVersions.map(v => ( - - - - - + + + {/* PHP Boxes */} + {installedPhpVersions.map(v => ( + + + + + {getStatusChip(status[`php:${v.version}`] || 'stopped')} + + PHP {v.version} + Port: {v.port || '?'} + + handleOpenLogs(`php:${v.version}`)} size="small" sx={{ bgcolor: 'rgba(255,255,255,0.05)' }}> + + + handleToggleService(`php:${v.version}`)} + disabled={status[`php:${v.version}`] === 'starting'} /> - - handleOpenLogs(`php:${v.version}`)} title="Logları Görüntüle"> - - - {getStatusChip(status[`php:${v.version}`] || 'stopped')} - handleToggleService(`php:${v.version}`)} - disabled={status[`php:${v.version}`] === 'starting'} - /> - - - - ))} - - - - - - handleOpenLogs('mysql')} title="Logları Görüntüle"> - - - {getStatusChip(status.mysql)} - handleToggleService('mysql')} - disabled={status.mysql === 'starting'} - /> - - - - + + + + ))} + + {/* MySQL Boxes */} + {mySqlVersions.filter(v => v.status === 'installed').map(v => ( + + + + + {getStatusChip(status[`mysql:${v.version}`] || 'stopped')} + + MySQL {v.version} + Port: {settings.mysqlPorts?.[v.id] || '3306'} + + handleOpenLogs(`mysql:${v.version}`)} size="small" sx={{ bgcolor: 'rgba(255,255,255,0.05)' }}> + + + handleToggleService(`mysql:${v.version}`)} + disabled={status[`mysql:${v.version}`] === 'starting'} + /> + + + + ))} +