diff --git a/config/my.ini.template b/config/mariadb.ini.template similarity index 100% rename from config/my.ini.template rename to config/mariadb.ini.template diff --git a/src/main/ipc/index.ts b/src/main/ipc/index.ts index b3ba1aa..c36c4f3 100644 --- a/src/main/ipc/index.ts +++ b/src/main/ipc/index.ts @@ -5,12 +5,12 @@ import { configService } from '../services/ConfigService' import { downloadService } from '../services/DownloadService' import { projectService } from '../services/ProjectService' import { phpManagerService } from '../services/PhpManagerService' -import { mySqlManagerService } from '../services/MySqlManagerService' +import { mariaDbManagerService } from '../services/MariaDbManagerService' import { nginxManagerService } from '../services/NginxManagerService' import { phpMyAdminService } from '../services/PhpMyAdminService' import { portMappingService } from '../services/PortMappingService' import path from 'path' -import { mySqlOperationService } from '../services/MySqlOperationService' +import { mariaDbOperationService } from '../services/MariaDbOperationService' function findExecutable(dir: string, name: string): string | null { if (!fs.existsSync(dir)) return null @@ -37,7 +37,8 @@ async function rebuildNginxConfig(forceProjects: boolean = false): Promise<{ bin if (!activeVersion) throw new Error('Nginx sürümü bulunamadı.') const nginxPrefix = path.join(nginxDir, activeVersion) - const binPath = path.join(nginxPrefix, 'nginx.exe') + const isWindows = process.platform === 'win32'; + const binPath = path.join(nginxPrefix, isWindows ? 'nginx.exe' : 'nginx') const confDir = path.join(nginxPrefix, 'conf') const logDir = path.join(configService.getBasePath(), 'logs') const rootPath = path.join(app.getAppPath(), 'www') @@ -167,8 +168,10 @@ export function registerIpcHandlers(): void { const phpVersion = versionMatch ? versionMatch[1] : null const phpId = phpVersion ? `php-${phpVersion}` : null + const isWindows = process.platform === 'win32'; const phpDir = phpId ? path.join(binDir, phpId) : binDir - const binPath = findExecutable(phpDir, 'php-cgi.exe') + const phpBinary = isWindows ? 'php-cgi.exe' : 'php-cgi'; + const binPath = findExecutable(phpDir, phpBinary) if (!binPath) return { success: false, message: `${phpVersion || ''} PHP executable bulunamadı. Lütfen indirin.` } @@ -199,48 +202,47 @@ export function registerIpcHandlers(): void { return { success, message: success ? `${phpVersion || ''} PHP başlatıldı.` : `${phpVersion || ''} PHP devreye alınamadı.` } } - if (serviceName.startsWith('mysql')) { - const versionMatch = serviceName.match(/^mysql:(.+)$/) - const mysqlVersion = versionMatch ? versionMatch[1] : null - const mysqlId = mysqlVersion ? `mysql-${mysqlVersion}` : null + if (serviceName.startsWith('mariadb') || serviceName.startsWith('mysql')) { + const versionMatch = serviceName.match(/^(?:mariadb|mysql):(.+)$/) + const dbVersion = versionMatch ? versionMatch[1] : null + const dbId = dbVersion ? `mariadb-${dbVersion}` : 'mariadb-default' - const dataDir = mysqlId - ? path.join(configService.getBasePath(), 'data', mysqlId) - : path.join(configService.getBasePath(), 'data', 'default') + const dataDir = path.join(configService.getBasePath(), 'data', dbId) + const dbBinDir = dbVersion ? path.join(binDir, `mariadb-${dbVersion}`) : binDir - const mysqlDir = mysqlId ? path.join(binDir, mysqlId) : binDir - const binPath = findExecutable(mysqlDir, 'mysqld.exe') + const isWindows = process.platform === 'win32'; + const dbBinary = isWindows ? 'mariadbd.exe' : 'mariadbd'; + let binPath = findExecutable(dbBinDir, dbBinary) || findExecutable(dbBinDir, isWindows ? 'mysqld.exe' : 'mysqld'); - if (!binPath) return { success: false, message: 'MySQL executable bulunamadı. Lütfen indirin.' } + if (!binPath) return { success: false, message: 'MariaDB executable bulunamadı. Lütfen indirin.' } - const mysqlVersions = await mySqlManagerService.getVersions() - const mysqlInfo = mysqlVersions.find(v => v.id === mysqlId) - const mysqlPort = mysqlInfo?.port || (mysqlId ? '3306' : settings.mysqlPort) + const dbVersions = await mariaDbManagerService.getVersions() + const dbInfo = dbVersions.find(v => v.id === `mariadb-${dbVersion}`) + const dbPort = dbInfo?.port || (dbVersion ? portMappingService.getMariaDbPort(dbVersion).toString() : settings.mariaDbPort) // Force kill this specific version if running or generic mysql await processManager.forceKillAll(serviceName) // Also kill anything else listening on the same port - await processManager.killByPort(serviceName, mysqlPort) + await processManager.killByPort(serviceName, dbPort) // 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 for ${serviceName}...`) + console.log(`Initializing MariaDB data directory for ${serviceName}...`) try { - // Try modern initialization first (5.7+) + // Try MariaDB/Modern MySQL initialization first (5.7+/10.x+) await processManager.runOnce(binPath, ['--initialize-insecure', `--datadir=${dataDir.replace(/\\/g, '/')}`]) } catch (err) { console.log(`Modern init failed, trying template copy for ${serviceName}...`) - // Fallback for 5.6: Copy default 'data' folder from binary dir if it exists - const templateDataDir = path.join(mysqlDir, 'data') + // Fallback: Copy default 'data' folder from binary dir if it exists + const templateDataDir = path.join(dbBinDir, 'data') if (fs.existsSync(templateDataDir)) { - // Recursively copy template data (mysql, performance_schema, etc.) fs.cpSync(templateDataDir, dataDir, { recursive: true }) console.log(`Successfully copied template data for ${serviceName}`) } else { - throw new Error(`MySQL initialization failed and no template data found at ${templateDataDir}`) + throw new Error(`MariaDB initialization failed and no template data found at ${templateDataDir}`) } } } @@ -248,8 +250,8 @@ export function registerIpcHandlers(): void { const sanitizedServiceName = serviceName.replace(/:/g, '_') const logFile = path.join(configService.getBasePath(), 'logs', `${sanitizedServiceName}_error.log`) - const configPath = await configService.generateConfig('my.ini.template', `my_${mysqlId || 'default'}.ini`, { - PORT: mysqlPort, + const configPath = await configService.generateConfig('mariadb.ini.template', `mariadb_${dbId || 'default'}.ini`, { + PORT: dbPort, DATADIR: dataDir.replace(/\\/g, '/'), SOCKET: `/tmp/${sanitizedServiceName}.sock`, LOG_FILE: logFile.replace(/\\/g, '/') @@ -258,18 +260,18 @@ export function registerIpcHandlers(): void { const success = await processManager.startService(serviceName, binPath, ['--defaults-file=' + configPath], false) if (success) { - // Quick health check for MySQL + // Quick health check await new Promise(r => setTimeout(r, 1500)) if (!processManager.isServiceRunning(serviceName)) { const errorLogs = await processManager.getLastErrorLogs(serviceName) - return { success: false, message: `${mysqlVersion || ''} MySQL başlatıldı ancak çalışma hatası verdi (ExitCode: 1).\n\n${errorLogs.length > 0 ? 'Önemli Hata Logları:\n' + errorLogs.join('\n') : 'Lütfen log sekmesini kontrol edin.'}` } + return { success: false, message: `${dbVersion || ''} MariaDB başlatıldı ancak çalışma hatası verdi (ExitCode: 1).\n\n${errorLogs.length > 0 ? 'Önemli Hata Logları:\n' + errorLogs.join('\n') : 'Lütfen log sekmesini kontrol edin.'}` } } } else { const errorLogs = await processManager.getLastErrorLogs(serviceName) - return { success: false, message: `${mysqlVersion || ''} MySQL başlatılamadı.\n\n${errorLogs.join('\n')}` } + return { success: false, message: `${dbVersion || ''} MariaDB başlatılamadı.\n\n${errorLogs.join('\n')}` } } - return { success, message: success ? `${mysqlVersion || ''} MySQL başlatıldı.` : `${mysqlVersion || ''} MySQL devreye alınamadı.` } + return { success, message: success ? `${dbVersion || ''} MariaDB başlatıldı.` : `${dbVersion || ''} MariaDB devreye alınamadı.` } } return { success: false, message: 'Bilinmeyen servis.' } @@ -320,30 +322,30 @@ export function registerIpcHandlers(): void { } }) - // MySQL Versions - ipcMain.handle('mysql:versions', async () => { - return await mySqlManagerService.getVersions() + // MariaDB Versions + ipcMain.handle('mariadb:versions', async () => { + return await mariaDbManagerService.getVersions() }) - ipcMain.handle('mysql:download', async (event, id: string) => { - const versions = await mySqlManagerService.getVersions() + ipcMain.handle('mariadb:download', async (event, id: string) => { + const versions = await mariaDbManagerService.getVersions() const version = versions.find(v => v.id === id) - if (!version) return { success: false, message: 'Geçersiz MySQL sürümü.' } + if (!version) return { success: false, message: 'Geçersiz MariaDB sürümü.' } try { - mySqlManagerService.updateProgress(id, 0, 'downloading') - event.sender.send('mysql:progress', { id, progress: 0 }) + mariaDbManagerService.updateProgress(id, 0, 'downloading') + event.sender.send('mariadb:progress', { id, progress: 0 }) await downloadService.downloadAndExtract(version.url, id, (p) => { - mySqlManagerService.updateProgress(id, p) - event.sender.send('mysql:progress', { id, progress: p }) + mariaDbManagerService.updateProgress(id, p) + event.sender.send('mariadb:progress', { id, progress: p }) }) - mySqlManagerService.updateProgress(id, 100, 'installed') - return { success: true, message: 'MySQL indirme tamamlandı.' } + mariaDbManagerService.updateProgress(id, 100, 'installed') + return { success: true, message: 'MariaDB indirme tamamlandı.' } } catch (error: any) { - mySqlManagerService.updateProgress(id, 0, 'available') - return { success: false, message: `MySQL hatası: ${error.message}` } + mariaDbManagerService.updateProgress(id, 0, 'available') + return { success: false, message: `MariaDB hatası: ${error.message}` } } }) @@ -565,8 +567,8 @@ export function registerIpcHandlers(): void { return await phpMyAdminService.isInstalled() }) - ipcMain.handle('pma:setup', async (event, mysqlPort: string) => { - return await phpMyAdminService.setup(mysqlPort, (p) => { + ipcMain.handle('pma:setup', async (event, mariaDbPort: string) => { + return await phpMyAdminService.setup(mariaDbPort, (p) => { event.sender.send('pma:progress', p) }) }) @@ -575,13 +577,13 @@ export function registerIpcHandlers(): void { try { await processManager.forceKillAll(serviceName) - const versionMatch = serviceName.match(/^mysql:(.+)$/) - const mysqlVersion = versionMatch ? versionMatch[1] : null - const mysqlId = mysqlVersion ? `mysql-${mysqlVersion}` : null + const versionMatch = serviceName.match(/^(?:mariadb|mysql):(.+)$/) + const dbVersion = versionMatch ? versionMatch[1] : null + const dbId = dbVersion ? `mariadb-${dbVersion}` : null - if (!mysqlId) return { success: false, message: 'Geçersiz servis adı.' } + if (!dbId) return { success: false, message: 'Geçersiz servis adı.' } - const dataDir = path.join(configService.getBasePath(), 'data', mysqlId) + const dataDir = path.join(configService.getBasePath(), 'data', dbId) if (fs.existsSync(dataDir)) { fs.rmSync(dataDir, { recursive: true, force: true }) } @@ -591,16 +593,18 @@ export function registerIpcHandlers(): void { } }) - ipcMain.handle('mysql:import-wizard:check-db', async (_event, { versionId, dbName, rootPass }) => { - const versions = await mySqlManagerService.getVersions() + // MariaDB Operations + ipcMain.handle('mariadb:import-wizard:check-db', async (_event, { versionId, dbName, rootPass }) => { + const versions = await mariaDbManagerService.getVersions() const v = versions.find(v => v.id === versionId) - if (!v) return { exists: false, error: 'MySQL version not found' } + if (!v) return { exists: false, error: 'MariaDB version not found' } const binDir = path.join(configService.getBasePath(), 'bin', versionId) - const binPath = findExecutable(binDir, 'mysql.exe') - if (!binPath) return { exists: false, error: 'mysql.exe not found' } + const isWindows = process.platform === 'win32' + const binPath = findExecutable(binDir, isWindows ? 'mariadb.exe' : 'mariadb') || findExecutable(binDir, isWindows ? 'mysql.exe' : 'mysql') + if (!binPath) return { exists: false, error: 'mariadb/mysql executable not found' } - const exists = await mySqlOperationService.checkDatabaseExists({ + const exists = await mariaDbOperationService.checkDatabaseExists({ binPath, host: '127.0.0.1', port: v.port, @@ -611,33 +615,34 @@ export function registerIpcHandlers(): void { return { exists } }) - ipcMain.handle('mysql:import-wizard:start', async (event, { versionId, filePath, dbName, user, pass, rootPass, overwrite }) => { - const versions = await mySqlManagerService.getVersions() + ipcMain.handle('mariadb:import-wizard:start', async (event, { versionId, filePath, dbName, user, pass, rootPass, overwrite }) => { + const versions = await mariaDbManagerService.getVersions() const v = versions.find(v => v.id === versionId) - if (!v) return { success: false, message: 'MySQL version not found' } + if (!v) return { success: false, message: 'MariaDB version not found' } const binDir = path.join(configService.getBasePath(), 'bin', versionId) - const mysqlBin = findExecutable(binDir, 'mysql.exe') - if (!mysqlBin) return { success: false, message: 'mysql.exe not found' } + const isWindows = process.platform === 'win32' + const dbBin = findExecutable(binDir, isWindows ? 'mariadb.exe' : 'mariadb') || findExecutable(binDir, isWindows ? 'mysql.exe' : 'mysql') + if (!dbBin) return { success: false, message: 'mariadb/mysql executable not found' } const config = { - binPath: mysqlBin, + binPath: dbBin, host: '127.0.0.1', port: v.port, rootUser: 'root', rootPass: rootPass || '' } - const log = (msg: string) => event.sender.send('mysql:import:log', msg) + const log = (msg: string) => event.sender.send('mariadb:import:log', msg) try { if (overwrite) { log(`Dropping existing database: ${dbName}`) - await mySqlOperationService.dropDatabase(config, dbName) + await mariaDbOperationService.dropDatabase(config, dbName) } log(`Creating database and user: ${dbName} / ${user}`) - const createRes = await mySqlOperationService.createDatabaseAndUser(config, dbName, user, pass) + const createRes = await mariaDbOperationService.createDatabaseAndUser(config, dbName, user, pass) if (!createRes.success) return createRes let sqlFiles: string[] = [] @@ -645,14 +650,14 @@ export function registerIpcHandlers(): void { sqlFiles = [filePath] } else { log(`Extracting archive: ${path.basename(filePath)}`) - const extractDir = path.join(app.getPath('temp'), `mysql_import_${Date.now()}`) - sqlFiles = await mySqlOperationService.extractArchive(filePath, extractDir) + const extractDir = path.join(app.getPath('temp'), `mariadb_import_${Date.now()}`) + sqlFiles = await mariaDbOperationService.extractArchive(filePath, extractDir) if (sqlFiles.length === 0) return { success: false, message: 'No .sql files found in archive.' } } for (const sqlFile of sqlFiles) { log(`Importing: ${path.basename(sqlFile)}`) - const importRes = await mySqlOperationService.importSql(config, dbName, sqlFile, (msg) => log(msg)) + const importRes = await mariaDbOperationService.importSql(config, dbName, sqlFile, (msg) => log(msg)) if (!importRes.success) return importRes } @@ -662,17 +667,18 @@ export function registerIpcHandlers(): void { } }) - ipcMain.handle('mysql:export-wizard:list-dbs', async (_event, { versionId, rootPass }) => { - const versions = await mySqlManagerService.getVersions() + ipcMain.handle('mariadb:export-wizard:list-dbs', async (_event, { versionId, rootPass }) => { + const versions = await mariaDbManagerService.getVersions() const v = versions.find(v => v.id === versionId) if (!v) return [] const binDir = path.join(configService.getBasePath(), 'bin', versionId) - const mysqlBin = findExecutable(binDir, 'mysql.exe') - if (!mysqlBin) return [] + const isWindows = process.platform === 'win32' + const dbBin = findExecutable(binDir, isWindows ? 'mariadb.exe' : 'mariadb') || findExecutable(binDir, isWindows ? 'mysql.exe' : 'mysql') + if (!dbBin) return [] - return await mySqlOperationService.listDatabases({ - binPath: mysqlBin, + return await mariaDbOperationService.listDatabases({ + binPath: dbBin, host: '127.0.0.1', port: v.port, rootUser: 'root', @@ -680,19 +686,20 @@ export function registerIpcHandlers(): void { }) }) - ipcMain.handle('mysql:export-wizard:start', async (event, { versionId, dbName, savePath, compress, rootPass }) => { - const versions = await mySqlManagerService.getVersions() + ipcMain.handle('mariadb:export-wizard:start', async (event, { versionId, dbName, savePath, compress, rootPass }) => { + const versions = await mariaDbManagerService.getVersions() const v = versions.find(v => v.id === versionId) - if (!v) return { success: false, message: 'MySQL version not found' } + if (!v) return { success: false, message: 'MariaDB version not found' } const binDir = path.join(configService.getBasePath(), 'bin', versionId) - const mysqlBin = findExecutable(binDir, 'mysql.exe') - if (!mysqlBin) return { success: false, message: 'mysql.exe not found' } + const isWindows = process.platform === 'win32' + const dbBin = findExecutable(binDir, isWindows ? 'mariadb.exe' : 'mariadb') || findExecutable(binDir, isWindows ? 'mysql.exe' : 'mysql') + if (!dbBin) return { success: false, message: 'mariadb/mysql executable not found' } - const log = (msg: string) => event.sender.send('mysql:export:log', msg) + const log = (msg: string) => event.sender.send('mariadb:export:log', msg) - return await mySqlOperationService.exportSql({ - binPath: mysqlBin, + return await mariaDbOperationService.exportSql({ + binPath: dbBin, host: '127.0.0.1', port: v.port, rootUser: 'root', diff --git a/src/main/resources/mariadb_catalog.json b/src/main/resources/mariadb_catalog.json new file mode 100644 index 0000000..6081f2c --- /dev/null +++ b/src/main/resources/mariadb_catalog.json @@ -0,0 +1,41 @@ +{ + "mariadb_release_catalog": { + "generated_date": "2026-03-30", + "provider": "MariaDB Foundation", + "releases": [ + { + "series": "11.4 (LTS)", + "version": "11.4.2", + "status": "Stable (LTS)", + "platforms": { + "win32": { + "x64": "https://archive.mariadb.org/mariadb-11.4.2/winx64-packages/mariadb-11.4.2-winx64.zip" + }, + "darwin": { + "x64": "https://archive.mariadb.org/mariadb-11.4.2/bintar-macosx-x86_64/mariadb-11.4.2-macosx-x86_64.tar.gz", + "arm64": "https://archive.mariadb.org/mariadb-11.4.2/bintar-macosx-arm64/mariadb-11.4.2-macosx-arm64.tar.gz" + }, + "linux": { + "x64": "https://archive.mariadb.org/mariadb-11.4.2/bintar-linux-systemd-x86_64/mariadb-11.4.2-linux-systemd-x86_64.tar.gz" + } + } + }, + { + "series": "10.11 (LTS)", + "version": "10.11.8", + "status": "Stable (LTS)", + "platforms": { + "win32": { + "x64": "https://archive.mariadb.org/mariadb-10.11.8/winx64-packages/mariadb-10.11.8-winx64.zip" + }, + "darwin": { + "x64": "https://archive.mariadb.org/mariadb-10.11.8/bintar-macosx-x86_64/mariadb-10.11.8-macosx-x86_64.tar.gz" + }, + "linux": { + "x64": "https://archive.mariadb.org/mariadb-10.11.8/bintar-linux-systemd-x86_64/mariadb-10.11.8-linux-systemd-x86_64.tar.gz" + } + } + } + ] + } +} \ No newline at end of file diff --git a/src/main/resources/mysql_catalog.json b/src/main/resources/mysql_catalog.json deleted file mode 100644 index 34d2ad1..0000000 --- a/src/main/resources/mysql_catalog.json +++ /dev/null @@ -1,61 +0,0 @@ -{ - "mysql_release_catalog": { - "generated_date": "2026-03-28", - "provider": "Oracle MySQL", - "releases": [ - { - "series": "9.x (Innovation)", - "version": "9.2.0", - "status": "Latest Innovation", - "php_min_recommended": "PHP 8.2+", - "download_url": "https://dev.mysql.com/downloads/mysql/", - "is_archive": false - }, - { - "series": "8.4 (LTS)", - "version": "8.4.2", - "status": "Current LTS", - "php_min_recommended": "PHP 8.1+", - "download_url": "https://dev.mysql.com/downloads/mysql/8.4.html", - "is_archive": false - }, - { - "series": "8.0", - "version": "8.0.40", - "status": "End of Life (April 2026)", - "php_min_recommended": "PHP 7.4 - 8.3", - "download_url": "https://downloads.mysql.com/archives/community/?product=community&version=8.0.40", - "is_archive": true - }, - { - "series": "5.7", - "version": "5.7.44", - "status": "End of Life (2023)", - "php_min_recommended": "PHP 5.6 - 7.4", - "download_url": "https://downloads.mysql.com/archives/community/?product=community&version=5.7.44", - "is_archive": true - }, - { - "series": "5.6", - "version": "5.6.51", - "status": "Legacy / End of Life", - "php_min_recommended": "PHP 5.5 - 5.6", - "download_url": "https://downloads.mysql.com/archives/community/?product=community&version=5.6.51", - "is_archive": true - }, - { - "series": "5.5", - "version": "5.5.62", - "status": "Legacy / End of Life", - "php_min_recommended": "PHP 5.4 - 5.6", - "download_url": "https://downloads.mysql.com/archives/community/?product=community&version=5.5.62", - "is_archive": true - } - ], - "useful_links": { - "full_archive": "https://downloads.mysql.com/archives/community/", - "yum_repo": "https://dev.mysql.com/downloads/repo/yum/", - "apt_repo": "https://dev.mysql.com/downloads/repo/apt/" - } - } -} \ No newline at end of file diff --git a/src/main/services/ConfigService.ts b/src/main/services/ConfigService.ts index dd5d251..5b3565a 100644 --- a/src/main/services/ConfigService.ts +++ b/src/main/services/ConfigService.ts @@ -9,8 +9,8 @@ export class ConfigService { private defaultSettings = { nginxPort: '80', phpPort: '9001', - mysqlPort: '3306', - mysqlPorts: {} as Record, + mariaDbPort: '3306', + mariaDbPorts: {} as Record, language: 'tr' } diff --git a/src/main/services/DownloadService.ts b/src/main/services/DownloadService.ts index c61a553..b7f5b7a 100644 --- a/src/main/services/DownloadService.ts +++ b/src/main/services/DownloadService.ts @@ -17,7 +17,9 @@ export class DownloadService { } async downloadAndExtract(url: string, targetName: string, onProgress?: (p: number) => void, customTargetDir?: string): Promise { - const tempFile = path.join(app.getPath('temp'), `${targetName}.zip`) + const isTarGz = url.toLowerCase().endsWith('.tar.gz') || url.toLowerCase().endsWith('.tgz'); + const extension = isTarGz ? '.tar.gz' : '.zip'; + const tempFile = path.join(app.getPath('temp'), `${targetName}${extension}`) const targetDir = customTargetDir || path.join(this.binDir, targetName) // Cleanup failed/previous attempts if we want a fresh start @@ -70,7 +72,9 @@ export class DownloadService { } getBinPath(service: string, executable: string): string { - return path.join(this.binDir, service, executable) + const isWindows = process.platform === 'win32'; + const fullExecutable = isWindows ? (executable.endsWith('.exe') ? executable : `${executable}.exe`) : executable; + return path.join(this.binDir, service, fullExecutable) } } diff --git a/src/main/services/MySqlManagerService.ts b/src/main/services/MariaDbManagerService.ts similarity index 52% rename from src/main/services/MySqlManagerService.ts rename to src/main/services/MariaDbManagerService.ts index 7c6fb14..9f6a581 100644 --- a/src/main/services/MySqlManagerService.ts +++ b/src/main/services/MariaDbManagerService.ts @@ -3,7 +3,7 @@ import path from 'path' import { app } from 'electron' import { configService } from './ConfigService' -export interface MySqlVersion { +export interface MariaDbVersion { id: string version: string cycle: string @@ -14,16 +14,16 @@ export interface MySqlVersion { description?: string } -export class MySqlManagerService { - private versions: MySqlVersion[] = [] +export class MariaDbManagerService { + private versions: MariaDbVersion[] = [] private binDir: string private catalogPath: string constructor() { this.binDir = path.join(configService.getBasePath(), 'bin') - this.catalogPath = path.join(app.getAppPath(), 'src/main/resources/mysql_catalog.json') + this.catalogPath = path.join(app.getAppPath(), 'src/main/resources/mariadb_catalog.json') if (!fs.existsSync(this.catalogPath)) { - this.catalogPath = path.join(process.cwd(), 'src/main/resources/mysql_catalog.json') + this.catalogPath = path.join(process.cwd(), 'src/main/resources/mariadb_catalog.json') } this.init() } @@ -34,54 +34,60 @@ export class MySqlManagerService { } } - async fetchAvailableVersions(): Promise { - console.log('Loading MySQL versions from local catalog:', this.catalogPath) + async fetchAvailableVersions(): Promise { + console.log('Loading MariaDB versions from local catalog:', this.catalogPath) try { if (!fs.existsSync(this.catalogPath)) { - console.error('MySQL Catalog not found at:', this.catalogPath) + console.error('MariaDB Catalog not found at:', this.catalogPath) return this.versions } const rawData = fs.readFileSync(this.catalogPath, 'utf8') const catalog = JSON.parse(rawData) - const releases = catalog.mysql_release_catalog.releases as any[] + const releases = catalog.mariadb_release_catalog.releases as any[] - const dynamicVersions: MySqlVersion[] = releases.map((v, index) => { + const platform = process.platform; // win32, darwin, linux + const arch = process.arch; // x64, arm64 + + const dynamicVersions: MariaDbVersion[] = releases.map((v, index) => { const version = v.version const cycle = v.series - // Extract MAJOR.MINOR (e.g. 8.0 from 8.0.40) - const versionParts = version.split('.') - const majorMinor = `${versionParts[0]}.${versionParts[1]}` + // Resolve URL based on platform and architecture + let url = ''; + if (v.platforms && v.platforms[platform]) { + url = v.platforms[platform][arch] || v.platforms[platform]['x64'] || ''; + } - // 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` + if (!url) { + console.warn(`No MariaDB URL found for platform ${platform}/${arch} in version ${version}`); + } const settings = configService.getSettings() const defaultPort = (3306 + index).toString() - const customPort = settings.mysqlPorts?.[`mysql-${version}`] || defaultPort + const customPort = settings.mariaDbPorts?.[`mariadb-${version}`] || defaultPort return { - id: `mysql-${version}`, + id: `mariadb-${version}`, version, cycle, url, - status: 'available', + status: 'available' as MariaDbVersion['status'], progress: 0, port: customPort, description: v.status } - }) + }).filter(v => v.url !== '') // Only include versions available for this platform this.updateVersionsList(dynamicVersions) return this.versions } catch (e: any) { - console.error('Failed to load MySQL catalog:', e.message) + console.error('Failed to load MariaDB catalog:', e.message) return this.versions } } - private updateVersionsList(newList: MySqlVersion[]) { + private updateVersionsList(newList: MariaDbVersion[]) { this.versions = newList.map(dv => { const current = this.versions.find(cv => cv.id === dv.id) if (current && current.status === 'downloading') return current @@ -92,14 +98,19 @@ export class MySqlManagerService { } private refreshInstalledStatus() { + const isWindows = process.platform === 'win32'; + const binaryName = isWindows ? 'mariadbd.exe' : 'mariadbd'; + this.versions = this.versions.map(v => { const targetDir = path.join(this.binDir, v.id) - const possiblePaths = [ - path.join(targetDir, 'bin', 'mysqld.exe'), - path.join(targetDir, `mysql-${v.version}-winx64`, 'bin', 'mysqld.exe') - ] + const possibleBinPaths = [ + path.join(targetDir, 'bin', binaryName), + // Some MariaDB distributions have nested folders after zip/tar extraction + path.join(targetDir, `mariadb-${v.version}-${isWindows ? 'winx64' : (process.arch === 'arm64' ? 'macosx-arm64' : 'macosx-x86_64')}`, 'bin', binaryName), + path.join(targetDir, `mariadb-${v.version}-${isWindows ? 'winx64' : 'linux-systemd-x86_64'}`, 'bin', binaryName) + ]; - const isInstalled = possiblePaths.some(p => fs.existsSync(p)) + const isInstalled = possibleBinPaths.some(p => fs.existsSync(p)); if (isInstalled) { return { ...v, status: 'installed', progress: 100 } @@ -108,7 +119,7 @@ export class MySqlManagerService { }) } - async getVersions(): Promise { + async getVersions(): Promise { if (this.versions.length === 0) { await this.fetchAvailableVersions() } else { @@ -117,7 +128,7 @@ export class MySqlManagerService { return this.versions } - updateProgress(id: string, progress: number, status: MySqlVersion['status'] = 'downloading') { + updateProgress(id: string, progress: number, status: MariaDbVersion['status'] = 'downloading') { const version = this.versions.find(v => v.id === id) if (version) { version.progress = progress @@ -126,4 +137,4 @@ export class MySqlManagerService { } } -export const mySqlManagerService = new MySqlManagerService() +export const mariaDbManagerService = new MariaDbManagerService() diff --git a/src/main/services/MySqlOperationService.ts b/src/main/services/MariaDbOperationService.ts similarity index 76% rename from src/main/services/MySqlOperationService.ts rename to src/main/services/MariaDbOperationService.ts index adc6ae9..07bca5d 100644 --- a/src/main/services/MySqlOperationService.ts +++ b/src/main/services/MariaDbOperationService.ts @@ -4,7 +4,7 @@ import { spawn } from 'child_process' import decompress from 'decompress' import zlib from 'zlib' -export interface MySqlConfig { +export interface MariaDbConfig { binPath: string host: string port: string @@ -12,7 +12,7 @@ export interface MySqlConfig { rootPass: string } -export class MySqlOperationService { +export class MariaDbOperationService { async extractArchive(archivePath: string, targetDir: string): Promise { if (!fs.existsSync(targetDir)) { fs.mkdirSync(targetDir, { recursive: true }) @@ -58,8 +58,9 @@ export class MySqlOperationService { return sqlFiles } - async runQuery(config: MySqlConfig, query: string, database?: string): Promise<{ success: boolean; output: string; error?: string }> { + async runQuery(config: MariaDbConfig, query: string, database?: string): Promise<{ success: boolean; output: string; error?: string }> { return new Promise((resolve) => { + const mariaDbPath = config.binPath; const args = [ `--host=${config.host}`, `--port=${config.port}`, @@ -73,13 +74,17 @@ export class MySqlOperationService { args.push('-e', query) - const child = spawn(config.binPath, args, { windowsHide: true }) + const child = spawn(mariaDbPath, args, { windowsHide: true }) let output = '' let error = '' child.stdout.on('data', (data) => output += data.toString()) child.stderr.on('data', (data) => error += data.toString()) + child.on('error', (err) => { + resolve({ success: false, output, error: err.message }) + }) + child.on('close', (code) => { if (code === 0) { resolve({ success: true, output }) @@ -90,12 +95,12 @@ export class MySqlOperationService { }) } - async checkDatabaseExists(config: MySqlConfig, dbName: string): Promise { + async checkDatabaseExists(config: MariaDbConfig, dbName: string): Promise { const result = await this.runQuery(config, `SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = '${dbName}'`) return result.success && result.output.includes(dbName) } - async createDatabaseAndUser(config: MySqlConfig, dbName: string, user: string, pass: string): Promise<{ success: boolean; message: string }> { + async createDatabaseAndUser(config: MariaDbConfig, dbName: string, user: string, pass: string): Promise<{ success: boolean; message: string }> { const queries = [ `CREATE DATABASE IF NOT EXISTS \`${dbName}\` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;`, `CREATE USER IF NOT EXISTS '${user}'@'%' IDENTIFIED BY '${pass}';`, @@ -113,14 +118,14 @@ export class MySqlOperationService { return { success: true, message: 'Database and user created successfully.' } } - async dropDatabase(config: MySqlConfig, dbName: string): Promise<{ success: boolean; message: string }> { + async dropDatabase(config: MariaDbConfig, dbName: string): Promise<{ success: boolean; message: string }> { const result = await this.runQuery(config, `DROP DATABASE IF EXISTS \`${dbName}\``) return { success: result.success, message: result.success ? 'Database dropped' : `Drop failed: ${result.error}` } } - async importSql(config: MySqlConfig, dbName: string, sqlFilePath: string, onProgress?: (log: string) => void): Promise<{ success: boolean; message: string }> { + async importSql(config: MariaDbConfig, dbName: string, sqlFilePath: string, onProgress?: (log: string) => void): Promise<{ success: boolean; message: string }> { return new Promise((resolve) => { - const mysqlPath = config.binPath + const mariaDbPath = config.binPath const args = [ `--host=${config.host}`, `--port=${config.port}`, @@ -132,9 +137,9 @@ export class MySqlOperationService { args.push(`--password=${config.rootPass}`) } - onProgress?.(`Executing: "${mysqlPath}" ${args.join(' ')} < "${sqlFilePath}"`) + onProgress?.(`Executing: "${mariaDbPath}" ${args.join(' ')} < "${sqlFilePath}"`) - const child = spawn(mysqlPath, args, { windowsHide: true }) + const child = spawn(mariaDbPath, args, { windowsHide: true }) // Pipe the file content to stdin const fileStream = fs.createReadStream(sqlFilePath) @@ -163,7 +168,7 @@ export class MySqlOperationService { }) } - async listDatabases(config: MySqlConfig): Promise { + async listDatabases(config: MariaDbConfig): Promise { const result = await this.runQuery(config, 'SHOW DATABASES') if (!result.success) return [] @@ -173,9 +178,23 @@ export class MySqlOperationService { .filter(line => line && line !== 'Database' && !line.match(/^information_schema|performance_schema|mysql|sys$/i)) } - async exportSql(config: MySqlConfig, dbName: string, targetPath: string, compress: boolean, onProgress?: (log: string) => void): Promise<{ success: boolean; message: string }> { + async exportSql(config: MariaDbConfig, dbName: string, targetPath: string, compress: boolean, onProgress?: (log: string) => void): Promise<{ success: boolean; message: string }> { return new Promise((resolve) => { - const dumpPath = config.binPath.replace(/mysql\.exe$/i, 'mysqldump.exe') + const isWindows = process.platform === 'win32'; + const binDir = path.dirname(config.binPath); + const dumpBinary = isWindows ? 'mariadb-dump.exe' : 'mariadb-dump'; + const mysqlDumpFallback = isWindows ? 'mysqldump.exe' : 'mysqldump'; + + let dumpPath = path.join(binDir, dumpBinary); + if (!fs.existsSync(dumpPath)) { + dumpPath = path.join(binDir, mysqlDumpFallback); + } + + if (!fs.existsSync(dumpPath)) { + onProgress?.(`Error: MariaDB/MySQL dump binary not found in ${binDir}`); + return resolve({ success: false, message: 'Dump binary (mariadb-dump or mysqldump) not found.' }); + } + const args = [ `--host=${config.host}`, `--port=${config.port}`, @@ -219,11 +238,11 @@ export class MySqlOperationService { resolve({ success: true, message: 'Export completed successfully.' }) } else { onProgress?.(`Export failed with exit code ${code}`) - resolve({ success: false, message: `Export failed: ${errorOutput}` }) + resolve({ success: false, message: `Export failed (Exit Code: ${code}).\n${errorOutput}` }) } }) }) } } -export const mySqlOperationService = new MySqlOperationService() +export const mariaDbOperationService = new MariaDbOperationService() diff --git a/src/main/services/PhpMyAdminService.ts b/src/main/services/PhpMyAdminService.ts index 151e3fb..bc05cdd 100644 --- a/src/main/services/PhpMyAdminService.ts +++ b/src/main/services/PhpMyAdminService.ts @@ -15,7 +15,7 @@ export class PhpMyAdminService { return fs.existsSync(path.join(this.pmaDir, 'index.php')) } - async setup(mysqlPort: string, onProgress?: (progress: number) => void): Promise { + async setup(mariaDbPort: string, onProgress?: (progress: number) => void): Promise { if (!fs.existsSync(this.pmaDir)) { fs.mkdirSync(this.pmaDir, { recursive: true }) } @@ -38,7 +38,7 @@ export class PhpMyAdminService { } // Generate config.inc.php - this.generateConfig(mysqlPort) + this.generateConfig(mariaDbPort) return true } catch (error) { @@ -47,7 +47,7 @@ export class PhpMyAdminService { } } - private generateConfig(mysqlPort: string = '3306') { + private generateConfig(mariaDbPort: string = '3306') { const configPath = path.join(this.pmaDir, 'config.inc.php') const blowfishSecret = Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15) @@ -56,7 +56,7 @@ $cfg['blowfish_secret'] = '${blowfishSecret}'; $i = 0; $i++; $cfg['Servers'][$i]['auth_type'] = 'config'; -$cfg['Servers'][$i]['host'] = '127.0.0.1:${mysqlPort}'; +$cfg['Servers'][$i]['host'] = '127.0.0.1:${mariaDbPort}'; $cfg['Servers'][$i]['user'] = 'root'; $cfg['Servers'][$i]['password'] = ''; $cfg['Servers'][$i]['extension'] = 'mysqli'; diff --git a/src/main/services/PortMappingService.ts b/src/main/services/PortMappingService.ts index 281786e..afda5c7 100644 --- a/src/main/services/PortMappingService.ts +++ b/src/main/services/PortMappingService.ts @@ -4,14 +4,14 @@ import { configService } from './ConfigService' interface PortMap { php: Record; - mysql: Record; + mariadb: Record; } export class PortMappingService { private configFile: string - private portMap: PortMap = { php: {}, mysql: {} } + private portMap: PortMap = { php: {}, mariadb: {} } private nextPhpPort = 9001 - private nextMysqlPort = 3306 + private nextMariaDbPort = 3306 constructor() { this.configFile = path.join(configService.getBasePath(), 'port_mappings.json') @@ -26,8 +26,8 @@ export class PortMappingService { const phpPorts = Object.values(this.portMap.php) if (phpPorts.length > 0) this.nextPhpPort = Math.max(...phpPorts) + 1 - const mysqlPorts = Object.values(this.portMap.mysql) - if (mysqlPorts.length > 0) this.nextMysqlPort = Math.max(...mysqlPorts) + 1 + const mariaDbPorts = Object.values(this.portMap.mariadb) + if (mariaDbPorts.length > 0) this.nextMariaDbPort = Math.max(...mariaDbPorts) + 1 } catch (e) { console.error('Failed to load port mappings', e) } @@ -46,12 +46,12 @@ export class PortMappingService { return this.portMap.php[version] } - getMySqlPort(version: string): number { - if (!this.portMap.mysql[version]) { - this.portMap.mysql[version] = this.nextMysqlPort++ + getMariaDbPort(version: string): number { + if (!this.portMap.mariadb[version]) { + this.portMap.mariadb[version] = this.nextMariaDbPort++ this.save() } - return this.portMap.mysql[version] + return this.portMap.mariadb[version] } } diff --git a/src/main/services/ProcessManager.ts b/src/main/services/ProcessManager.ts index 659f1c8..8a80fd6 100644 --- a/src/main/services/ProcessManager.ts +++ b/src/main/services/ProcessManager.ts @@ -22,7 +22,7 @@ export class ProcessManager { fs.mkdirSync(this.logDir, { recursive: true }) } // Initialize with default logs - ['nginx', 'php', 'mysql'].forEach(s => { + ['nginx', 'php', 'mariadb'].forEach(s => { if (!this.logs.has(s)) { this.logs.set(s, [`[${new Date().toLocaleTimeString()}] Sistem hazır. İşlem bekleniyor...`]) } @@ -158,7 +158,16 @@ export class ProcessManager { const child = this.processes.get(name) if (child) { this.addLog(name, 'Stopping service...') - child.kill() + try { + if (process.platform === 'win32') { + child.kill() + } else { + // On Unix, try SIGTERM first, then SIGKILL if needed + child.kill('SIGTERM') + } + } catch (e) { + this.addLog(name, `Stop error: ${e instanceof Error ? e.message : String(e)}`) + } this.processes.delete(name) } @@ -167,117 +176,103 @@ export class ProcessManager { } async forceKillAll(name: string): Promise { + const isWindows = process.platform === 'win32'; return new Promise((resolve) => { const childProcess = this.processes.get(name) - if (childProcess && childProcess.pid) { - this.addLog(name, `Killing process by PID: ${childProcess.pid}`) - const kill = spawn('taskkill', ['/F', '/PID', childProcess.pid.toString(), '/T'], { - windowsHide: true, - shell: false - }) - kill.on('error', (err) => { - this.addLog(name, `PID Kill error: ${err.message}`) - this.processes.delete(name) - resolve(true) - }) - kill.on('close', () => { - this.processes.delete(name) - resolve(true) - }) - return - } - // Fallback: If it's a generic service name without version, or we just want to be sure - // For MySQL multi-instance, we should avoid global kill if possible. - const isVersionedMysql = name.startsWith('mysql:') - const executableName = name === 'nginx' ? 'nginx.exe' : name === 'php' ? 'php-cgi.exe' : 'mysqld.exe' + if (isWindows) { + if (childProcess && childProcess.pid) { + this.addLog(name, `Killing process by PID: ${childProcess.pid}`) + spawn('taskkill', ['/F', '/PID', childProcess.pid.toString(), '/T'], { windowsHide: true }) + .on('close', () => { this.processes.delete(name); resolve(true) }) + .on('error', () => { this.processes.delete(name); resolve(true) }) + return + } - if (isVersionedMysql) { - // For versioned mysql, we try to find by command line (contains data directory name) - const mysqlId = name.replace('mysql:', 'mysql-') - this.addLog(name, `Attempting to kill specific MySQL instance: ${mysqlId}`) - // Using powershell to find and kill as wmic is deprecated/missing on some Win11 - const psCommand = `Get-CimInstance Win32_Process -Filter "name = 'mysqld.exe' AND CommandLine LIKE '%mysql_data_${mysqlId}%'" | Invoke-CimMethod -MethodName Terminate` - const ps = spawn('powershell', ['-Command', psCommand], { - windowsHide: true, - shell: false - }) - ps.on('error', (err) => { - this.addLog(name, `PowerShell kill error: ${err.message}`) - resolve(true) - }) - ps.on('close', () => resolve(true)) + const isVersionedMariaDb = name.startsWith('mariadb:') || name.startsWith('mysql:') + + if (isVersionedMariaDb) { + const dbVersion = name.split(':')[1] + const dbId = `mariadb-${dbVersion}` + + if (process.platform === 'win32') { + const psCommand = `Get-CimInstance Win32_Process -Filter "name = 'mariadbd.exe' OR name = 'mysqld.exe'" | Where-Object { $_.CommandLine -like '*${dbId}*' } | Invoke-CimMethod -MethodName Terminate` + spawn('powershell', ['-Command', psCommand], { windowsHide: true }) + .on('close', () => resolve(true)) + .on('error', () => resolve(true)) + } else { + // Unix: pkill -f matches full command line (data directory path usually contains the dbId) + spawn('pkill', ['-f', dbId], { windowsHide: true }) + .on('close', () => resolve(true)) + .on('error', () => resolve(true)) + } + } else { + const isWindows = process.platform === 'win32'; + if (isWindows) { + const executableName = name === 'nginx' ? 'nginx.exe' : name === 'php' ? 'php-cgi.exe' : 'mariadbd.exe' + spawn('taskkill', ['/F', '/IM', executableName, '/T'], { windowsHide: true }) + .on('close', () => resolve(true)) + .on('error', () => resolve(true)) + } else { + const processName = name === 'nginx' ? 'nginx' : name === 'php' ? 'php-cgi' : 'mariadbd' + spawn('pkill', ['-f', processName], { windowsHide: true }) + .on('close', () => resolve(true)) + .on('error', () => resolve(true)) + } + } } else { - this.addLog(name, `Force killing all ${executableName} processes...`) - const tk = spawn('taskkill', ['/F', '/IM', executableName, '/T'], { - windowsHide: true, - shell: false - }) - tk.on('error', (err) => { - this.addLog(name, `Taskkill error: ${err.message}`) - resolve(true) - }) - tk.on('close', () => resolve(true)) + // macOS / Linux + if (childProcess && childProcess.pid) { + try { process.kill(childProcess.pid, 'SIGKILL') } catch (e) { } + this.processes.delete(name) + } + + const procName = name === 'nginx' ? 'nginx' : name === 'php' ? 'php-fpm' : 'mariadbd' + spawn('pkill', ['-9', '-f', procName]) + .on('close', () => resolve(true)) + .on('error', () => resolve(true)) } }) } async killByPort(name: string, port: string | number): Promise { + const isWindows = process.platform === 'win32'; 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()) + if (isWindows) { + const cmd = `netstat -ano | findstr :${port} | findstr LISTENING` + const netstat = spawn('cmd.exe', ['/c', cmd], { windowsHide: true }) + let output = '' + netstat.stdout?.on('data', d => output += d.toString()) + netstat.on('close', () => { + const lines = output.split('\n').filter(l => l.trim()) + if (lines.length === 0) return resolve(true) + const pids = new Set() + lines.forEach(line => { + const parts = line.trim().split(/\s+/) + const pid = parts[parts.length - 1] + if (pid && !isNaN(parseInt(pid))) pids.add(pid) }) + const kills = Array.from(pids).map(pid => new Promise(res => { + spawn('taskkill', ['/F', '/PID', pid, '/T'], { windowsHide: true }).on('close', () => res()) + })) + Promise.all(kills).then(() => resolve(true)) }) - - Promise.all(killProcesses).then(() => { - this.addLog(name, `Successfully cleared port ${port}.`) - resolve(true) + } else { + const lsof = spawn('lsof', ['-i', `:${port}`, '-t']) + let output = '' + lsof.stdout?.on('data', d => output += d.toString()) + lsof.on('close', () => { + const pids = output.split('\n').filter(l => l.trim()) + if (pids.length === 0) return resolve(true) + const kills = pids.map(pid => new Promise(res => { + spawn('kill', ['-9', pid]).on('close', () => res()) + })) + Promise.all(kills).then(() => resolve(true)) }) - }) - - netstat.on('error', (err) => { - this.addLog(name, `Port check error: ${err.message}`) - resolve(true) - }) + } }) } @@ -292,7 +287,7 @@ export class ProcessManager { } // Ensure defaults are present if stopped if (!statuses['nginx']) statuses['nginx'] = 'stopped' - if (!statuses['mysql']) statuses['mysql'] = 'stopped' + if (!statuses['mariadb']) statuses['mariadb'] = 'stopped' return statuses } @@ -300,7 +295,7 @@ export class ProcessManager { if (!this.logDir) return const logPaths: Record = { nginx: path.join(this.logDir, 'nginx_error.log'), - mysql: path.join(this.logDir, 'mysql_error.log') + mariadb: path.join(this.logDir, 'mariadb_error.log') } const logPath = logPaths[name] @@ -341,7 +336,7 @@ export class ProcessManager { const possibleFiles = [ path.join(this.logDir, `${sanitizedName}.log`), path.join(this.logDir, `${sanitizedName}_error.log`), - path.join(this.logDir, `mysql_error.log`), + path.join(this.logDir, `mariadb_error.log`), path.join(this.logDir, `nginx_error.log`) ] diff --git a/src/main/services/ProjectService.ts b/src/main/services/ProjectService.ts index a845699..56c26ba 100644 --- a/src/main/services/ProjectService.ts +++ b/src/main/services/ProjectService.ts @@ -7,7 +7,7 @@ export interface Project { name: string path: string phpVersion: string - mySqlVersion: string + mariaDbVersion: string host: string } diff --git a/src/preload/index.ts b/src/preload/index.ts index 03e3857..e82359e 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -16,9 +16,9 @@ const api = { getPhpVersions: () => electronAPI.ipcRenderer.invoke('binaries:list-php'), downloadPhpVersion: (id: string) => electronAPI.ipcRenderer.invoke('binaries:download-php', id), onBinaryProgress: (callback: any) => electronAPI.ipcRenderer.on('binaries:progress', (_event, data) => callback(data)), - getMySqlVersions: () => electronAPI.ipcRenderer.invoke('mysql:versions'), - downloadMySqlVersion: (id: string) => electronAPI.ipcRenderer.invoke('mysql:download', id), - onMySqlProgress: (callback: any) => electronAPI.ipcRenderer.on('mysql:progress', (_event, data) => callback(data)), + getMariaDbVersions: () => electronAPI.ipcRenderer.invoke('mariadb:versions'), + downloadMariaDbVersion: (id: string) => electronAPI.ipcRenderer.invoke('mariadb:download', id), + onMariaDbProgress: (callback: any) => electronAPI.ipcRenderer.on('mariadb:progress', (_event, data) => callback(data)), selectDirectory: () => electronAPI.ipcRenderer.invoke('dialog:select-directory'), getSettings: () => electronAPI.ipcRenderer.invoke('config:get'), saveSettings: (settings: any) => electronAPI.ipcRenderer.invoke('config:save', settings), @@ -26,15 +26,20 @@ const api = { downloadNginxVersion: (id: string) => electronAPI.ipcRenderer.invoke('nginx:download', id), onNginxProgress: (callback: any) => electronAPI.ipcRenderer.on('nginx:progress', (_event, data) => callback(data)), pmaStatus: () => electronAPI.ipcRenderer.invoke('pma:status'), - pmaSetup: (mysqlPort: string) => electronAPI.ipcRenderer.invoke('pma:setup', mysqlPort), + pmaSetup: (mariaDbPort: string) => electronAPI.ipcRenderer.invoke('pma:setup', mariaDbPort), onPmaProgress: (callback: any) => electronAPI.ipcRenderer.on('pma:progress', (_event, data) => callback(data)), selectFile: (filters?: any[]) => electronAPI.ipcRenderer.invoke('dialog:select-file', filters), - checkMySqlDb: (versionId: string, dbName: string) => electronAPI.ipcRenderer.invoke('mysql:import-wizard:check-db', { versionId, dbName }), - startMySqlImport: (data: any) => electronAPI.ipcRenderer.invoke('mysql:import-wizard:start', data), - onMySqlImportLog: (callback: any) => { - const listener = (_event, msg) => callback(msg) - electronAPI.ipcRenderer.on('mysql:import:log', listener) - return () => electronAPI.ipcRenderer.removeListener('mysql:import:log', listener) + checkMariaDbDb: (versionId: string, dbName: string) => electronAPI.ipcRenderer.invoke('mariadb:import-wizard:check-db', { versionId, dbName }), + startMariaDbImport: (data: any) => electronAPI.ipcRenderer.invoke('mariadb:import-wizard:start', data), + onMariaDbImportLog: (callback: any) => { + const listener = (_event, msg: any) => callback(msg) + electronAPI.ipcRenderer.on('mariadb:import:log', listener) + return () => electronAPI.ipcRenderer.removeListener('mariadb:import:log', listener) + }, + onMariaDbExportLog: (callback: any) => { + const listener = (_event, msg: any) => callback(msg) + electronAPI.ipcRenderer.on('mariadb:export:log', listener) + return () => electronAPI.ipcRenderer.removeListener('mariadb:export:log', listener) }, invoke: (channel: string, ...args: any[]) => electronAPI.ipcRenderer.invoke(channel, ...args) } diff --git a/src/renderer/index.html b/src/renderer/index.html index 2507be7..5708427 100644 --- a/src/renderer/index.html +++ b/src/renderer/index.html @@ -1,17 +1,17 @@ - - - Trunçgil Multi-PHP & MySQL Server - - - - -
- - - + + + Trunçgil Multi-PHP & MariaDB Server + + + + + +
+ + + + \ No newline at end of file diff --git a/src/renderer/src/App.tsx b/src/renderer/src/App.tsx index 09a7d84..172d3df 100644 --- a/src/renderer/src/App.tsx +++ b/src/renderer/src/App.tsx @@ -71,8 +71,8 @@ import { } from '@mui/icons-material' import Swal from 'sweetalert2' import { useTranslation } from 'react-i18next' -import MySqlImportWizard from './components/MySqlImportWizard' -import MySqlExportWizard from './components/MySqlExportWizard' +import MariaDbImportWizard from './components/MariaDbImportWizard' +import MariaDbExportWizard from './components/MariaDbExportWizard' import NginxCodeEditor from './components/NginxCodeEditor' declare global { @@ -90,8 +90,8 @@ interface ServiceStatus { interface AppSettings { nginxPort: string phpPort: string - mysqlPort: string - mysqlPorts?: Record + mariaDbPort: string + mariaDbPorts?: Record language: string } @@ -100,22 +100,22 @@ function App(): JSX.Element { const [status, setStatus] = useState({ nginx: 'stopped', php: 'stopped', - mysql: 'stopped' + mariadb: 'stopped' }) const [activeTab, setActiveTab] = useState('dashboard') const [settingsTab, setSettingsTab] = useState(0) const [dashboardTab, setDashboardTab] = useState(0) const [projects, setProjects] = useState([]) const [phpVersions, setPhpVersions] = useState([]) - const [mySqlVersions, setMySqlVersions] = useState([]) + const [mariaDbVersions, setMariaDbVersions] = useState([]) const [nginxVersions, setNginxVersions] = useState([]) const [isProjectDialogOpen, setIsProjectDialogOpen] = useState(false) const slugify = (text: string) => text.toLowerCase().trim().replace(/[^\w\s-]/g, '').replace(/[\s_-]+/g, '-').replace(/^-+|-+$/g, ''); - const [newProject, setNewProject] = useState({ name: '', path: '', phpVersion: '', mySqlVersion: '', host: 'localhost' }) + const [newProject, setNewProject] = useState({ name: '', path: '', phpVersion: '', mariaDbVersion: '', host: 'localhost' }) const [settings, setSettings] = useState({ nginxPort: '80', phpPort: '9001', - mysqlPort: '3306', + mariaDbPort: '3306', language: 'tr' }) const [notification, setNotification] = useState<{ open: boolean, message: string, severity: 'success' | 'error' }>({ @@ -131,8 +131,8 @@ function App(): JSX.Element { const [pmaInstalled, setPmaInstalled] = useState(false) const [pmaLoading, setPmaLoading] = useState(false) const [pmaProgress, setPmaProgress] = useState(0) - const [isMySqlWizardOpen, setIsMySqlWizardOpen] = useState(false) - const [isMySqlExportWizardOpen, setIsMySqlExportWizardOpen] = useState(false) + const [isMariaDbWizardOpen, setIsMariaDbWizardOpen] = useState(false) + const [isMariaDbExportWizardOpen, setIsMariaDbExportWizardOpen] = useState(false) const [isNginxConfigOpen, setIsNginxConfigOpen] = useState(false) const [nginxConfig, setNginxConfig] = useState('') const [isProjectNginxConfigOpen, setIsProjectNginxConfigOpen] = useState(false) @@ -178,10 +178,10 @@ function App(): JSX.Element { } } - const fetchMySqlVersions = async () => { - if (window.api && window.api.getMySqlVersions) { - const list = await window.api.getMySqlVersions() - setMySqlVersions(list || []) + const fetchMariaDbVersions = async () => { + if (window.api && window.api.getMariaDbVersions) { + const list = await window.api.getMariaDbVersions() + setMariaDbVersions(list || []) } } @@ -207,15 +207,15 @@ function App(): JSX.Element { const installedPhp = phpVersions.find(v => v.status === 'installed') if (installedPhp) updates.phpVersion = installedPhp.version } - if (!newProject.mySqlVersion && mySqlVersions.length > 0) { - const installedMysql = mySqlVersions.find(v => v.status === 'installed') - if (installedMysql) updates.mySqlVersion = installedMysql.version + if (!newProject.mariaDbVersion && mariaDbVersions.length > 0) { + const installedMariaDb = mariaDbVersions.find(v => v.status === 'installed') + if (installedMariaDb) updates.mariaDbVersion = installedMariaDb.version } if (Object.keys(updates).length > 0) { setNewProject(prev => ({ ...prev, ...updates })) } } - }, [isProjectDialogOpen, phpVersions, mySqlVersions]) + }, [isProjectDialogOpen, phpVersions, mariaDbVersions]) useEffect(() => { if (!window.api) return @@ -230,7 +230,7 @@ function App(): JSX.Element { fetchStatus() fetchProjects() fetchPhpVersions() - fetchMySqlVersions() + fetchMariaDbVersions() fetchNginxVersions() fetchSettings() fetchPmaStatus() @@ -241,10 +241,10 @@ function App(): JSX.Element { setPhpVersions(prev => prev.map(v => v.id === id ? { ...v, progress, status: progress === 100 ? 'installed' : 'downloading' } : v)) }) : null - const removeMySqlListener = window.api.onMySqlProgress ? window.api.onMySqlProgress((data) => { + const removeMariaDbListener = window.api.onMariaDbProgress ? window.api.onMariaDbProgress((data) => { if (!data) return const { id, progress } = data - setMySqlVersions(prev => prev.map(v => v.id === id ? { ...v, progress, status: progress === 100 ? 'installed' : 'downloading' } : v)) + setMariaDbVersions(prev => prev.map(v => v.id === id ? { ...v, progress, status: progress === 100 ? 'installed' : 'downloading' } : v)) }) : null const removeNginxListener = window.api.onNginxProgress ? window.api.onNginxProgress((data) => { @@ -261,7 +261,7 @@ function App(): JSX.Element { fetchStatus() // fetchProjects() // Reduced frequency for these // fetchPhpVersions() - // fetchMySqlVersions() + // fetchMariaDbVersions() // fetchNginxVersions() }, 3000) @@ -276,7 +276,7 @@ function App(): JSX.Element { clearInterval(interval) if (logInterval) clearInterval(logInterval) if (typeof removePhpListener === 'function') removePhpListener() - if (typeof removeMySqlListener === 'function') removeMySqlListener() + if (typeof removeMariaDbListener === 'function') removeMariaDbListener() if (typeof removeNginxListener === 'function') removeNginxListener() if (typeof removePmaListener === 'function') removePmaListener() } @@ -396,15 +396,15 @@ function App(): JSX.Element { 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) { + const mariaDbToStart = mariaDbVersions.filter(v => v.status === 'installed').map(v => `mariadb:${v.version}`) + for (const s of mariaDbToStart) { 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)), - ...mysqlToStart.filter(s => (status[s] || 'stopped') !== 'running').map(s => window.api?.startService(s)) + ...mariaDbToStart.filter(s => (status[s] || 'stopped') !== 'running').map(s => window.api?.startService(s)) ]) fetchStatus() @@ -415,13 +415,13 @@ function App(): JSX.Element { if (!window.api) return 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')) + const mariaDbToStop = Object.keys(status).filter(s => (s.startsWith('mariadb:') || 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)), - ...mysqlToStop.map(s => window.api?.stopService(s)), - window.api?.stopService('mysql') // Also stop legacy if any + ...mariaDbToStop.map(s => window.api?.stopService(s)), + window.api?.stopService('mariadb') // Also stop default ]) fetchStatus() @@ -444,7 +444,7 @@ function App(): JSX.Element { await window.api.addProject(newProject) } setIsProjectDialogOpen(false) - setNewProject({ name: '', path: '', phpVersion: '', mySqlVersion: '', host: 'localhost' }) + setNewProject({ name: '', path: '', phpVersion: '', mariaDbVersion: '', host: 'localhost' }) setNotification({ open: true, message: newProject.id ? 'Proje güncellendi.' : 'Proje başarıyla eklendi.', severity: 'success' }) fetchProjects() } @@ -559,15 +559,15 @@ function App(): JSX.Element { fetchPhpVersions() } - const handleDownloadMySql = async (id: string) => { + const handleDownloadMariaDb = async (id: string) => { if (!window.api) return - const result = await window.api.downloadMySqlVersion(id) + const result = await window.api.downloadMariaDbVersion(id) setNotification({ open: true, message: result.message, severity: result.success ? 'success' : 'error' }) - fetchMySqlVersions() + fetchMariaDbVersions() } const handleDownloadNginx = async (id: string) => { @@ -699,7 +699,7 @@ function App(): JSX.Element { setSettingsTab(v)} textColor="primary" indicatorColor="primary" variant="scrollable" scrollButtons="auto"> } iconPosition="start" /> - } iconPosition="start" /> + } iconPosition="start" /> } iconPosition="start" /> } iconPosition="start" /> @@ -749,14 +749,14 @@ function App(): JSX.Element { {settingsTab === 1 && ( - MySQL Sürümleri - Farklı MySQL sürümlerini indirebilir ve sunucunuzda kullanabilirsiniz. + MariaDB Sürümleri + Farklı MariaDB sürümlerini indirebilir ve sunucunuzda kullanabilirsiniz. - {mySqlVersions.length === 0 && ( + {mariaDbVersions.length === 0 && ( )} - {mySqlVersions.map((v) => ( + {mariaDbVersions.map((v) => ( {v.status === 'installed' ? : } @@ -764,7 +764,7 @@ function App(): JSX.Element { - {`MySQL ${v.version} (${v.cycle})`} + {`MariaDB ${v.version} (${v.cycle})`} {v.description && ( )} @@ -777,11 +777,11 @@ function App(): JSX.Element { {v.status === 'installed' && ( { - const newPorts = { ...(settings.mysqlPorts || {}), [v.id]: e.target.value } - setSettings({ ...settings, mysqlPorts: newPorts }) + const newPorts = { ...(settings.mariaDbPorts || {}), [v.id]: e.target.value } + setSettings({ ...settings, mariaDbPorts: newPorts }) }} variant="outlined" size="small" @@ -790,7 +790,7 @@ function App(): JSX.Element { )} {v.status === 'available' && ( - )} @@ -870,9 +870,9 @@ function App(): JSX.Element { helperText="Varsayılan: 9001" /> setSettings({ ...settings, mysqlPort: e.target.value })} + label="MariaDB Portu" + value={settings.mariaDbPort} + onChange={(e) => setSettings({ ...settings, mariaDbPort: e.target.value })} variant="filled" size="small" helperText="Varsayılan: 3306" @@ -898,7 +898,7 @@ function App(): JSX.Element { - + @@ -988,8 +988,8 @@ function App(): JSX.Element { ))} - {/* MySQL Boxes */} - {(dashboardTab === 0 || dashboardTab === 3) && mySqlVersions.filter(v => v.status === 'installed').map(v => ( + {/* MariaDB Boxes */} + {(dashboardTab === 0 || dashboardTab === 3) && mariaDbVersions.filter(v => v.status === 'installed').map(v => ( - {getStatusChip(status[`mysql:${v.version}`] || 'stopped')} + {getStatusChip(status[`mariadb:${v.version}`] || 'stopped')} - MySQL {v.version} - Port: {settings.mysqlPorts?.[v.id] || v.port} + MariaDB {v.version} + Port: {settings.mariaDbPorts?.[v.id] || v.port} - handleOpenLogs(`mysql:${v.version}`)} title="Hata Logları" size="small" sx={{ bgcolor: 'rgba(255,255,255,0.05)' }}> + handleOpenLogs(`mariadb:${v.version}`)} title="Hata Logları" size="small" sx={{ bgcolor: 'rgba(255,255,255,0.05)' }}> - handleResetData(`mysql:${v.version}`)} title="Verileri Sıfırla" size="small" sx={{ bgcolor: 'rgba(255,255,255,0.05)', '&:hover': { color: 'error.main' } }}> + handleResetData(`mariadb:${v.version}`)} title="Verileri Sıfırla" size="small" sx={{ bgcolor: 'rgba(255,255,255,0.05)', '&:hover': { color: 'error.main' } }}> handleToggleService(`mysql:${v.version}`)} - disabled={status[`mysql:${v.version}`] === 'starting'} + checked={(status[`mariadb:${v.version}`] || 'stopped') === 'running' || status[`mariadb:${v.version}`] === 'starting'} + onChange={() => handleToggleService(`mariadb:${v.version}`)} + disabled={status[`mariadb:${v.version}`] === 'starting'} /> @@ -1093,7 +1093,7 @@ function App(): JSX.Element { - MySQL Veri İçe Aktar + MariaDB Veri İçe Aktar Seçtiğiniz bir .sql veya arşiv dosyasını hızlıca veritabanına yükleyin. @@ -1102,7 +1102,7 @@ function App(): JSX.Element {