feat: implement core services for Nginx, PHP, and project management with IPC integration
This commit is contained in:
+90
-83
@@ -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',
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -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/"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,8 +9,8 @@ export class ConfigService {
|
||||
private defaultSettings = {
|
||||
nginxPort: '80',
|
||||
phpPort: '9001',
|
||||
mysqlPort: '3306',
|
||||
mysqlPorts: {} as Record<string, string>,
|
||||
mariaDbPort: '3306',
|
||||
mariaDbPorts: {} as Record<string, string>,
|
||||
language: 'tr'
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,9 @@ export class DownloadService {
|
||||
}
|
||||
|
||||
async downloadAndExtract(url: string, targetName: string, onProgress?: (p: number) => void, customTargetDir?: string): Promise<string> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+40
-29
@@ -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<MySqlVersion[]> {
|
||||
console.log('Loading MySQL versions from local catalog:', this.catalogPath)
|
||||
async fetchAvailableVersions(): Promise<MariaDbVersion[]> {
|
||||
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<MySqlVersion[]> {
|
||||
async getVersions(): Promise<MariaDbVersion[]> {
|
||||
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()
|
||||
+35
-16
@@ -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<string[]> {
|
||||
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<boolean> {
|
||||
async checkDatabaseExists(config: MariaDbConfig, dbName: string): Promise<boolean> {
|
||||
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<string[]> {
|
||||
async listDatabases(config: MariaDbConfig): Promise<string[]> {
|
||||
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()
|
||||
@@ -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<boolean> {
|
||||
async setup(mariaDbPort: string, onProgress?: (progress: number) => void): Promise<boolean> {
|
||||
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';
|
||||
|
||||
@@ -4,14 +4,14 @@ import { configService } from './ConfigService'
|
||||
|
||||
interface PortMap {
|
||||
php: Record<string, number>;
|
||||
mysql: Record<string, number>;
|
||||
mariadb: Record<string, number>;
|
||||
}
|
||||
|
||||
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]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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<boolean> {
|
||||
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<boolean> {
|
||||
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<string>()
|
||||
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<void>((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<string>()
|
||||
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<void>(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<void>(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<string, string> = {
|
||||
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`)
|
||||
]
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ export interface Project {
|
||||
name: string
|
||||
path: string
|
||||
phpVersion: string
|
||||
mySqlVersion: string
|
||||
mariaDbVersion: string
|
||||
host: string
|
||||
}
|
||||
|
||||
|
||||
+15
-10
@@ -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)
|
||||
}
|
||||
|
||||
+14
-14
@@ -1,17 +1,17 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Trunçgil Multi-PHP & MySQL Server</title>
|
||||
<!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
|
||||
<meta
|
||||
http-equiv="Content-Security-Policy"
|
||||
content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'"
|
||||
/>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Trunçgil Multi-PHP & MariaDB Server</title>
|
||||
<!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
|
||||
<meta http-equiv="Content-Security-Policy"
|
||||
content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
+71
-71
@@ -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<string, string>
|
||||
mariaDbPort: string
|
||||
mariaDbPorts?: Record<string, string>
|
||||
language: string
|
||||
}
|
||||
|
||||
@@ -100,22 +100,22 @@ function App(): JSX.Element {
|
||||
const [status, setStatus] = useState<ServiceStatus>({
|
||||
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<any[]>([])
|
||||
const [phpVersions, setPhpVersions] = useState<any[]>([])
|
||||
const [mySqlVersions, setMySqlVersions] = useState<any[]>([])
|
||||
const [mariaDbVersions, setMariaDbVersions] = useState<any[]>([])
|
||||
const [nginxVersions, setNginxVersions] = useState<any[]>([])
|
||||
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<any>({ name: '', path: '', phpVersion: '', mySqlVersion: '', host: 'localhost' })
|
||||
const [newProject, setNewProject] = useState<any>({ name: '', path: '', phpVersion: '', mariaDbVersion: '', host: 'localhost' })
|
||||
const [settings, setSettings] = useState<AppSettings>({
|
||||
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 {
|
||||
<Box sx={{ borderBottom: 1, borderColor: 'divider', mb: 3 }}>
|
||||
<Tabs value={settingsTab} onChange={(_e, v) => setSettingsTab(v)} textColor="primary" indicatorColor="primary" variant="scrollable" scrollButtons="auto">
|
||||
<Tab label="PHP" icon={<PhpIcon />} iconPosition="start" />
|
||||
<Tab label="MySQL" icon={<DbIcon />} iconPosition="start" />
|
||||
<Tab label="MariaDB" icon={<DbIcon />} iconPosition="start" />
|
||||
<Tab label="Nginx" icon={<WebIcon />} iconPosition="start" />
|
||||
<Tab label="Sunucu Ayarları" icon={<ServerIcon />} iconPosition="start" />
|
||||
</Tabs>
|
||||
@@ -749,14 +749,14 @@ function App(): JSX.Element {
|
||||
{settingsTab === 1 && (
|
||||
<Paper sx={{ bgcolor: 'rgba(255, 255, 255, 0.03)', borderRadius: 2 }}>
|
||||
<Box sx={{ p: 2, borderBottom: '1px solid rgba(255, 255, 255, 0.05)' }}>
|
||||
<Typography variant="subtitle1" sx={{ fontWeight: 'bold' }}>MySQL Sürümleri</Typography>
|
||||
<Typography variant="body2" color="text.secondary">Farklı MySQL sürümlerini indirebilir ve sunucunuzda kullanabilirsiniz.</Typography>
|
||||
<Typography variant="subtitle1" sx={{ fontWeight: 'bold' }}>MariaDB Sürümleri</Typography>
|
||||
<Typography variant="body2" color="text.secondary">Farklı MariaDB sürümlerini indirebilir ve sunucunuzda kullanabilirsiniz.</Typography>
|
||||
</Box>
|
||||
<List sx={{ maxHeight: 500, overflow: 'auto' }}>
|
||||
{mySqlVersions.length === 0 && (
|
||||
{mariaDbVersions.length === 0 && (
|
||||
<ListItem><ListItemText primary="Yükleniyor veya liste boş..." /></ListItem>
|
||||
)}
|
||||
{mySqlVersions.map((v) => (
|
||||
{mariaDbVersions.map((v) => (
|
||||
<ListItem key={v.id} sx={{ py: 2 }}>
|
||||
<ListItemIcon>
|
||||
{v.status === 'installed' ? <InstalledIcon color="success" /> : <AvailableIcon color="action" />}
|
||||
@@ -764,7 +764,7 @@ function App(): JSX.Element {
|
||||
<ListItemText
|
||||
primary={
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
||||
{`MySQL ${v.version} (${v.cycle})`}
|
||||
{`MariaDB ${v.version} (${v.cycle})`}
|
||||
{v.description && (
|
||||
<Chip label={v.description} size="small" variant="outlined" sx={{ fontSize: '0.7rem', height: 20 }} />
|
||||
)}
|
||||
@@ -777,11 +777,11 @@ function App(): JSX.Element {
|
||||
{v.status === 'installed' && (
|
||||
<TextField
|
||||
label="Port"
|
||||
value={settings.mysqlPorts?.[v.id] || ''}
|
||||
value={settings.mariaDbPorts?.[v.id] || ''}
|
||||
placeholder={v.port}
|
||||
onChange={(e) => {
|
||||
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 {
|
||||
)}
|
||||
<Box sx={{ minWidth: 120, textAlign: 'right' }}>
|
||||
{v.status === 'available' && (
|
||||
<Button variant="outlined" size="small" startIcon={<DownloadIcon />} onClick={() => handleDownloadMySql(v.id)}>
|
||||
<Button variant="outlined" size="small" startIcon={<DownloadIcon />} onClick={() => handleDownloadMariaDb(v.id)}>
|
||||
İNDİR
|
||||
</Button>
|
||||
)}
|
||||
@@ -870,9 +870,9 @@ function App(): JSX.Element {
|
||||
helperText="Varsayılan: 9001"
|
||||
/>
|
||||
<TextField
|
||||
label="MySQL Portu"
|
||||
value={settings.mysqlPort}
|
||||
onChange={(e) => 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 {
|
||||
<Tab label={t('common.all')} />
|
||||
<Tab label="Nginx" />
|
||||
<Tab label="PHP" />
|
||||
<Tab label="MySQL" />
|
||||
<Tab label="MariaDB" />
|
||||
</Tabs>
|
||||
</Box>
|
||||
|
||||
@@ -988,8 +988,8 @@ function App(): JSX.Element {
|
||||
</Grid>
|
||||
))}
|
||||
|
||||
{/* 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 => (
|
||||
<Grid item xs={12} sm={6} md={4} key={v.id}>
|
||||
<Paper elevation={4} sx={{
|
||||
p: 3, borderRadius: 3,
|
||||
@@ -1001,23 +1001,23 @@ function App(): JSX.Element {
|
||||
}}>
|
||||
<Box sx={{ display: 'flex', justifyContent: 'space-between', mb: 2 }}>
|
||||
<DbIcon color="primary" sx={{ fontSize: 40 }} />
|
||||
{getStatusChip(status[`mysql:${v.version}`] || 'stopped')}
|
||||
{getStatusChip(status[`mariadb:${v.version}`] || 'stopped')}
|
||||
</Box>
|
||||
<Typography variant="h6" sx={{ fontWeight: 'bold' }}>MySQL {v.version}</Typography>
|
||||
<Typography variant="body2" color="text.secondary" sx={{ mb: 2 }}>Port: {settings.mysqlPorts?.[v.id] || v.port}</Typography>
|
||||
<Typography variant="h6" sx={{ fontWeight: 'bold' }}>MariaDB {v.version}</Typography>
|
||||
<Typography variant="body2" color="text.secondary" sx={{ mb: 2 }}>Port: {settings.mariaDbPorts?.[v.id] || v.port}</Typography>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
|
||||
<Box sx={{ display: 'flex', gap: 1 }}>
|
||||
<IconButton onClick={() => handleOpenLogs(`mysql:${v.version}`)} title="Hata Logları" size="small" sx={{ bgcolor: 'rgba(255,255,255,0.05)' }}>
|
||||
<IconButton onClick={() => handleOpenLogs(`mariadb:${v.version}`)} title="Hata Logları" size="small" sx={{ bgcolor: 'rgba(255,255,255,0.05)' }}>
|
||||
<TerminalIcon fontSize="small" />
|
||||
</IconButton>
|
||||
<IconButton onClick={() => handleResetData(`mysql:${v.version}`)} title="Verileri Sıfırla" size="small" sx={{ bgcolor: 'rgba(255,255,255,0.05)', '&:hover': { color: 'error.main' } }}>
|
||||
<IconButton onClick={() => handleResetData(`mariadb:${v.version}`)} title="Verileri Sıfırla" size="small" sx={{ bgcolor: 'rgba(255,255,255,0.05)', '&:hover': { color: 'error.main' } }}>
|
||||
<ResetIcon fontSize="small" />
|
||||
</IconButton>
|
||||
</Box>
|
||||
<Switch
|
||||
checked={(status[`mysql:${v.version}`] || 'stopped') === 'running' || status[`mysql:${v.version}`] === 'starting'}
|
||||
onChange={() => 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'}
|
||||
/>
|
||||
</Box>
|
||||
</Paper>
|
||||
@@ -1093,7 +1093,7 @@ function App(): JSX.Element {
|
||||
<DbIcon color="primary" sx={{ fontSize: 32 }} />
|
||||
</Box>
|
||||
<Box sx={{ flexGrow: 1 }}>
|
||||
<Typography variant="h6" sx={{ fontWeight: 'bold' }}>MySQL Veri İçe Aktar</Typography>
|
||||
<Typography variant="h6" sx={{ fontWeight: 'bold' }}>MariaDB Veri İçe Aktar</Typography>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
Seçtiğiniz bir .sql veya arşiv dosyasını hızlıca veritabanına yükleyin.
|
||||
</Typography>
|
||||
@@ -1102,7 +1102,7 @@ function App(): JSX.Element {
|
||||
<Button
|
||||
variant="contained"
|
||||
size="large"
|
||||
onClick={() => setIsMySqlWizardOpen(true)}
|
||||
onClick={() => setIsMariaDbWizardOpen(true)}
|
||||
startIcon={<DbIcon />}
|
||||
sx={{ borderRadius: 2 }}
|
||||
>
|
||||
@@ -1118,7 +1118,7 @@ function App(): JSX.Element {
|
||||
<DbIcon color="secondary" sx={{ fontSize: 32 }} />
|
||||
</Box>
|
||||
<Box sx={{ flexGrow: 1 }}>
|
||||
<Typography variant="h6" sx={{ fontWeight: 'bold' }}>MySQL Veri Dışa Aktar</Typography>
|
||||
<Typography variant="h6" sx={{ fontWeight: 'bold' }}>MariaDB Veri Dışa Aktar</Typography>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
Veritabanlarını .sql veya .sql.gz olarak güvenle yedekleyin.
|
||||
</Typography>
|
||||
@@ -1128,7 +1128,7 @@ function App(): JSX.Element {
|
||||
variant="contained"
|
||||
color="secondary"
|
||||
size="large"
|
||||
onClick={() => setIsMySqlExportWizardOpen(true)}
|
||||
onClick={() => setIsMariaDbExportWizardOpen(true)}
|
||||
startIcon={<DbIcon />}
|
||||
sx={{ borderRadius: 2 }}
|
||||
>
|
||||
@@ -1388,7 +1388,7 @@ function App(): JSX.Element {
|
||||
|
||||
<Dialog open={isProjectDialogOpen} onClose={() => {
|
||||
setIsProjectDialogOpen(false)
|
||||
setNewProject({ name: '', path: '', phpVersion: '', mySqlVersion: '', host: 'localhost' })
|
||||
setNewProject({ name: '', path: '', phpVersion: '', mariaDbVersion: '', host: 'localhost' })
|
||||
}} fullWidth maxWidth="sm">
|
||||
<DialogTitle>{newProject.id ? 'Proje Düzenle' : 'Yeni Proje Ekle'}</DialogTitle>
|
||||
<DialogContent>
|
||||
@@ -1442,7 +1442,7 @@ function App(): JSX.Element {
|
||||
<DialogActions>
|
||||
<Button onClick={() => {
|
||||
setIsProjectDialogOpen(false)
|
||||
setNewProject({ name: '', path: '', phpVersion: '', mySqlVersion: '', host: 'localhost' })
|
||||
setNewProject({ name: '', path: '', phpVersion: '', mariaDbVersion: '', host: 'localhost' })
|
||||
}}>İptal</Button>
|
||||
<Button
|
||||
onClick={handleAddProject}
|
||||
@@ -1454,16 +1454,16 @@ function App(): JSX.Element {
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
|
||||
<MySqlImportWizard
|
||||
open={isMySqlWizardOpen}
|
||||
onClose={() => setIsMySqlWizardOpen(false)}
|
||||
mySqlVersions={mySqlVersions}
|
||||
<MariaDbImportWizard
|
||||
open={isMariaDbWizardOpen}
|
||||
onClose={() => setIsMariaDbWizardOpen(false)}
|
||||
mariaDbVersions={mariaDbVersions}
|
||||
/>
|
||||
|
||||
<MySqlExportWizard
|
||||
open={isMySqlExportWizardOpen}
|
||||
onClose={() => setIsMySqlExportWizardOpen(false)}
|
||||
mySqlVersions={mySqlVersions}
|
||||
<MariaDbExportWizard
|
||||
open={isMariaDbExportWizardOpen}
|
||||
onClose={() => setIsMariaDbExportWizardOpen(false)}
|
||||
mariaDbVersions={mariaDbVersions}
|
||||
/>
|
||||
|
||||
<Dialog
|
||||
|
||||
+17
-17
@@ -30,10 +30,10 @@ import {
|
||||
} from '@mui/icons-material'
|
||||
import Swal from 'sweetalert2'
|
||||
|
||||
interface MySqlExportWizardProps {
|
||||
interface MariaDbExportWizardProps {
|
||||
open: boolean
|
||||
onClose: () => void
|
||||
mySqlVersions: any[]
|
||||
mariaDbVersions: any[]
|
||||
}
|
||||
|
||||
const steps = [
|
||||
@@ -43,7 +43,7 @@ const steps = [
|
||||
'Özet ve İşlem'
|
||||
]
|
||||
|
||||
const MySqlExportWizard: React.FC<MySqlExportWizardProps> = ({ open, onClose, mySqlVersions }) => {
|
||||
const MariaDbExportWizard: React.FC<MariaDbExportWizardProps> = ({ open, onClose, mariaDbVersions }) => {
|
||||
const [activeStep, setActiveStep] = useState(0)
|
||||
const [selectedVersion, setSelectedVersion] = useState('')
|
||||
const [databases, setDatabases] = useState<string[]>([])
|
||||
@@ -63,10 +63,10 @@ const MySqlExportWizard: React.FC<MySqlExportWizardProps> = ({ open, onClose, my
|
||||
setSavePath('')
|
||||
setLogs([])
|
||||
setExportResult(null)
|
||||
const installedVersion = mySqlVersions.find(v => v.status === 'installed')
|
||||
const installedVersion = mariaDbVersions.find(v => v.status === 'installed')
|
||||
if (installedVersion) setSelectedVersion(installedVersion.id)
|
||||
}
|
||||
}, [open, mySqlVersions])
|
||||
}, [open, mariaDbVersions])
|
||||
|
||||
useEffect(() => {
|
||||
if (logEndRef.current) {
|
||||
@@ -79,12 +79,12 @@ const MySqlExportWizard: React.FC<MySqlExportWizardProps> = ({ open, onClose, my
|
||||
|
||||
const handleStep1Next = async () => {
|
||||
if (!selectedVersion) {
|
||||
Swal.fire('Hata', 'Lütfen bir MySQL sunucusu seçin.', 'error')
|
||||
Swal.fire('Hata', 'Lütfen bir MariaDB sunucusu seçin.', 'error')
|
||||
return
|
||||
}
|
||||
setLoading(true)
|
||||
try {
|
||||
const dbs = await window.api.invoke('mysql:export-wizard:list-dbs', { versionId: selectedVersion, rootPass })
|
||||
const dbs = await window.api.invoke('mariadb:export-wizard:list-dbs', { versionId: selectedVersion, rootPass })
|
||||
setDatabases(dbs)
|
||||
nextStep()
|
||||
} catch (err: any) {
|
||||
@@ -105,12 +105,12 @@ const MySqlExportWizard: React.FC<MySqlExportWizardProps> = ({ open, onClose, my
|
||||
setLoading(true)
|
||||
setLogs(['[Sistem] Dışa aktarma işlemi başlatılıyor...'])
|
||||
|
||||
const removeListener = window.api.onMySqlExportLog((msg: string) => {
|
||||
const removeListener = window.api.onMariaDbExportLog((msg: string) => {
|
||||
setLogs(prev => [...prev, msg])
|
||||
})
|
||||
|
||||
try {
|
||||
const result = await window.api.invoke('mysql:export-wizard:start', {
|
||||
const result = await window.api.invoke('mariadb:export-wizard:start', {
|
||||
versionId: selectedVersion,
|
||||
dbName: selectedDb,
|
||||
savePath,
|
||||
@@ -131,17 +131,17 @@ const MySqlExportWizard: React.FC<MySqlExportWizardProps> = ({ open, onClose, my
|
||||
case 0:
|
||||
return (
|
||||
<Box sx={{ mt: 2 }}>
|
||||
<Typography variant="body1" gutterBottom>Veritabanın hangi MySQL sunucusunda olduğunu seçin.</Typography>
|
||||
<Typography variant="body1" gutterBottom>Veritabanın hangi MariaDB sunucusunda olduğunu seçin.</Typography>
|
||||
<FormControl fullWidth sx={{ mt: 2 }}>
|
||||
<InputLabel>MySQL Sunucusu</InputLabel>
|
||||
<InputLabel>MariaDB Sunucusu</InputLabel>
|
||||
<Select
|
||||
value={selectedVersion}
|
||||
label="MySQL Sunucusu"
|
||||
label="MariaDB Sunucusu"
|
||||
onChange={(e) => setSelectedVersion(e.target.value)}
|
||||
>
|
||||
{mySqlVersions.filter(v => v.status === 'installed').map(v => (
|
||||
{mariaDbVersions.filter(v => v.status === 'installed').map(v => (
|
||||
<MenuItem key={v.id} value={v.id}>
|
||||
MySQL {v.version} (Port: {v.port})
|
||||
MariaDB {v.version} (Port: {v.port})
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
@@ -153,7 +153,7 @@ const MySqlExportWizard: React.FC<MySqlExportWizardProps> = ({ open, onClose, my
|
||||
value={rootPass}
|
||||
type="password"
|
||||
onChange={(e) => setRootPass(e.target.value)}
|
||||
helperText="MySQL 'root' kullanıcısının şifresi. Boşsa boş bırakın."
|
||||
helperText="MariaDB 'root' kullanıcısının şifresi. Boşsa boş bırakın."
|
||||
/>
|
||||
</Box>
|
||||
)
|
||||
@@ -266,7 +266,7 @@ const MySqlExportWizard: React.FC<MySqlExportWizardProps> = ({ open, onClose, my
|
||||
|
||||
return (
|
||||
<Dialog open={open} onClose={loading ? undefined : onClose} maxWidth="sm" fullWidth>
|
||||
<DialogTitle>MySQL Veritabanı Dışa Aktarma Sihirbazı</DialogTitle>
|
||||
<DialogTitle>MariaDB Veritabanı Dışa Aktarma Sihirbazı</DialogTitle>
|
||||
<DialogContent>
|
||||
<Stepper activeStep={activeStep} orientation="horizontal" sx={{ pt: 2, pb: 4 }}>
|
||||
{steps.map((label) => (
|
||||
@@ -306,4 +306,4 @@ const MySqlExportWizard: React.FC<MySqlExportWizardProps> = ({ open, onClose, my
|
||||
)
|
||||
}
|
||||
|
||||
export default MySqlExportWizard
|
||||
export default MariaDbExportWizard
|
||||
+18
-18
@@ -30,10 +30,10 @@ import {
|
||||
} from '@mui/icons-material'
|
||||
import Swal from 'sweetalert2'
|
||||
|
||||
interface MySqlImportWizardProps {
|
||||
interface MariaDbImportWizardProps {
|
||||
open: boolean
|
||||
onClose: () => void
|
||||
mySqlVersions: any[]
|
||||
mariaDbVersions: any[]
|
||||
}
|
||||
|
||||
const steps = [
|
||||
@@ -43,7 +43,7 @@ const steps = [
|
||||
'Özet ve İşlem'
|
||||
]
|
||||
|
||||
const MySqlImportWizard: React.FC<MySqlImportWizardProps> = ({ open, onClose, mySqlVersions }) => {
|
||||
const MariaDbImportWizard: React.FC<MariaDbImportWizardProps> = ({ open, onClose, mariaDbVersions }) => {
|
||||
const [activeStep, setActiveStep] = useState(0)
|
||||
const [filePath, setFilePath] = useState('')
|
||||
const [selectedVersion, setSelectedVersion] = useState('')
|
||||
@@ -64,7 +64,7 @@ const MySqlImportWizard: React.FC<MySqlImportWizardProps> = ({ open, onClose, my
|
||||
setFilePath('')
|
||||
setLogs([])
|
||||
setImportResult(null)
|
||||
const installedVersion = mySqlVersions.find(v => v.status === 'installed')
|
||||
const installedVersion = mariaDbVersions.find(v => v.status === 'installed')
|
||||
if (installedVersion) setSelectedVersion(installedVersion.id)
|
||||
|
||||
// Generate random credentials
|
||||
@@ -76,7 +76,7 @@ const MySqlImportWizard: React.FC<MySqlImportWizardProps> = ({ open, onClose, my
|
||||
rootPass: ''
|
||||
})
|
||||
}
|
||||
}, [open, mySqlVersions])
|
||||
}, [open, mariaDbVersions])
|
||||
|
||||
useEffect(() => {
|
||||
if (logEndRef.current) {
|
||||
@@ -103,7 +103,7 @@ const MySqlImportWizard: React.FC<MySqlImportWizardProps> = ({ open, onClose, my
|
||||
|
||||
const handleStep2Next = () => {
|
||||
if (!selectedVersion) {
|
||||
Swal.fire('Hata', 'Lütfen bir MySQL sunucusu seçin.', 'error')
|
||||
Swal.fire('Hata', 'Lütfen bir MariaDB sunucusu seçin.', 'error')
|
||||
return
|
||||
}
|
||||
nextStep()
|
||||
@@ -117,7 +117,7 @@ const MySqlImportWizard: React.FC<MySqlImportWizardProps> = ({ open, onClose, my
|
||||
|
||||
setLoading(true)
|
||||
try {
|
||||
const { exists } = await window.api.checkMySqlDb(selectedVersion, dbConfig.dbName, dbConfig.rootPass)
|
||||
const { exists } = await window.api.checkMariaDbDb(selectedVersion, dbConfig.dbName, dbConfig.rootPass)
|
||||
if (exists) {
|
||||
const result = await Swal.fire({
|
||||
title: 'Veritabanı Mevcut!',
|
||||
@@ -142,19 +142,19 @@ const MySqlImportWizard: React.FC<MySqlImportWizardProps> = ({ open, onClose, my
|
||||
setLoading(true)
|
||||
setLogs(['[Sistem] İşlem başlatılıyor...'])
|
||||
|
||||
const removeListener = window.api.onMySqlImportLog((msg: string) => {
|
||||
const removeListener = window.api.onMariaDbImportLog((msg: string) => {
|
||||
setLogs(prev => [...prev, msg])
|
||||
})
|
||||
|
||||
try {
|
||||
const result = await window.api.startMySqlImport({
|
||||
const result = await window.api.startMariaDbImport({
|
||||
versionId: selectedVersion,
|
||||
filePath,
|
||||
dbName: dbConfig.dbName,
|
||||
user: dbConfig.user,
|
||||
pass: dbConfig.pass,
|
||||
rootPass: dbConfig.rootPass,
|
||||
overwrite: true // Logic handled by checkMySqlDb before
|
||||
overwrite: true // Logic handled by checkMariaDbDb before
|
||||
})
|
||||
setImportResult(result)
|
||||
} catch (err: any) {
|
||||
@@ -193,17 +193,17 @@ const MySqlImportWizard: React.FC<MySqlImportWizardProps> = ({ open, onClose, my
|
||||
case 1:
|
||||
return (
|
||||
<Box sx={{ mt: 2 }}>
|
||||
<Typography variant="body1" gutterBottom>Dosyanın hangi MySQL sunucusuna yükleneceğini seçin.</Typography>
|
||||
<Typography variant="body1" gutterBottom>Dosyanın hangi MariaDB sunucusuna yükleneceğini seçin.</Typography>
|
||||
<FormControl fullWidth sx={{ mt: 2 }}>
|
||||
<InputLabel>MySQL Sunucusu</InputLabel>
|
||||
<InputLabel>MariaDB Sunucusu</InputLabel>
|
||||
<Select
|
||||
value={selectedVersion}
|
||||
label="MySQL Sunucusu"
|
||||
label="MariaDB Sunucusu"
|
||||
onChange={(e) => setSelectedVersion(e.target.value)}
|
||||
>
|
||||
{mySqlVersions.filter(v => v.status === 'installed').map(v => (
|
||||
{mariaDbVersions.filter(v => v.status === 'installed').map(v => (
|
||||
<MenuItem key={v.id} value={v.id}>
|
||||
MySQL {v.version} (Port: {v.port})
|
||||
MariaDB {v.version} (Port: {v.port})
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
@@ -215,7 +215,7 @@ const MySqlImportWizard: React.FC<MySqlImportWizardProps> = ({ open, onClose, my
|
||||
value={dbConfig.rootPass}
|
||||
type="password"
|
||||
onChange={(e) => setDbConfig({ ...dbConfig, rootPass: e.target.value })}
|
||||
helperText="MySQL 'root' kullanıcısının mevcut şifresini girin. Boşsa boş bırakın."
|
||||
helperText="MariaDB 'root' kullanıcısının mevcut şifresini girin. Boşsa boş bırakın."
|
||||
/>
|
||||
</Box>
|
||||
)
|
||||
@@ -315,7 +315,7 @@ const MySqlImportWizard: React.FC<MySqlImportWizardProps> = ({ open, onClose, my
|
||||
|
||||
return (
|
||||
<Dialog open={open} onClose={loading ? undefined : onClose} maxWidth="sm" fullWidth>
|
||||
<DialogTitle>MySQL Hızlı Dump/Import Sihirbazı</DialogTitle>
|
||||
<DialogTitle>MariaDB Hızlı Dump/Import Sihirbazı</DialogTitle>
|
||||
<DialogContent>
|
||||
<Stepper activeStep={activeStep} orientation="horizontal" sx={{ pt: 2, pb: 4 }}>
|
||||
{steps.map((label) => (
|
||||
@@ -355,4 +355,4 @@ const MySqlImportWizard: React.FC<MySqlImportWizardProps> = ({ open, onClose, my
|
||||
)
|
||||
}
|
||||
|
||||
export default MySqlImportWizard
|
||||
export default MariaDbImportWizard
|
||||
Reference in New Issue
Block a user