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 { downloadService } from '../services/DownloadService'
|
||||||
import { projectService } from '../services/ProjectService'
|
import { projectService } from '../services/ProjectService'
|
||||||
import { phpManagerService } from '../services/PhpManagerService'
|
import { phpManagerService } from '../services/PhpManagerService'
|
||||||
import { mySqlManagerService } from '../services/MySqlManagerService'
|
import { mariaDbManagerService } from '../services/MariaDbManagerService'
|
||||||
import { nginxManagerService } from '../services/NginxManagerService'
|
import { nginxManagerService } from '../services/NginxManagerService'
|
||||||
import { phpMyAdminService } from '../services/PhpMyAdminService'
|
import { phpMyAdminService } from '../services/PhpMyAdminService'
|
||||||
import { portMappingService } from '../services/PortMappingService'
|
import { portMappingService } from '../services/PortMappingService'
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
import { mySqlOperationService } from '../services/MySqlOperationService'
|
import { mariaDbOperationService } from '../services/MariaDbOperationService'
|
||||||
|
|
||||||
function findExecutable(dir: string, name: string): string | null {
|
function findExecutable(dir: string, name: string): string | null {
|
||||||
if (!fs.existsSync(dir)) return 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ı.')
|
if (!activeVersion) throw new Error('Nginx sürümü bulunamadı.')
|
||||||
|
|
||||||
const nginxPrefix = path.join(nginxDir, activeVersion)
|
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 confDir = path.join(nginxPrefix, 'conf')
|
||||||
const logDir = path.join(configService.getBasePath(), 'logs')
|
const logDir = path.join(configService.getBasePath(), 'logs')
|
||||||
const rootPath = path.join(app.getAppPath(), 'www')
|
const rootPath = path.join(app.getAppPath(), 'www')
|
||||||
@@ -167,8 +168,10 @@ export function registerIpcHandlers(): void {
|
|||||||
const phpVersion = versionMatch ? versionMatch[1] : null
|
const phpVersion = versionMatch ? versionMatch[1] : null
|
||||||
const phpId = phpVersion ? `php-${phpVersion}` : null
|
const phpId = phpVersion ? `php-${phpVersion}` : null
|
||||||
|
|
||||||
|
const isWindows = process.platform === 'win32';
|
||||||
const phpDir = phpId ? path.join(binDir, phpId) : binDir
|
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.` }
|
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ı.` }
|
return { success, message: success ? `${phpVersion || ''} PHP başlatıldı.` : `${phpVersion || ''} PHP devreye alınamadı.` }
|
||||||
}
|
}
|
||||||
|
|
||||||
if (serviceName.startsWith('mysql')) {
|
if (serviceName.startsWith('mariadb') || serviceName.startsWith('mysql')) {
|
||||||
const versionMatch = serviceName.match(/^mysql:(.+)$/)
|
const versionMatch = serviceName.match(/^(?:mariadb|mysql):(.+)$/)
|
||||||
const mysqlVersion = versionMatch ? versionMatch[1] : null
|
const dbVersion = versionMatch ? versionMatch[1] : null
|
||||||
const mysqlId = mysqlVersion ? `mysql-${mysqlVersion}` : null
|
const dbId = dbVersion ? `mariadb-${dbVersion}` : 'mariadb-default'
|
||||||
|
|
||||||
const dataDir = mysqlId
|
const dataDir = path.join(configService.getBasePath(), 'data', dbId)
|
||||||
? path.join(configService.getBasePath(), 'data', mysqlId)
|
const dbBinDir = dbVersion ? path.join(binDir, `mariadb-${dbVersion}`) : binDir
|
||||||
: path.join(configService.getBasePath(), 'data', 'default')
|
|
||||||
|
|
||||||
const mysqlDir = mysqlId ? path.join(binDir, mysqlId) : binDir
|
const isWindows = process.platform === 'win32';
|
||||||
const binPath = findExecutable(mysqlDir, 'mysqld.exe')
|
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 dbVersions = await mariaDbManagerService.getVersions()
|
||||||
const mysqlInfo = mysqlVersions.find(v => v.id === mysqlId)
|
const dbInfo = dbVersions.find(v => v.id === `mariadb-${dbVersion}`)
|
||||||
const mysqlPort = mysqlInfo?.port || (mysqlId ? '3306' : settings.mysqlPort)
|
const dbPort = dbInfo?.port || (dbVersion ? portMappingService.getMariaDbPort(dbVersion).toString() : settings.mariaDbPort)
|
||||||
|
|
||||||
// Force kill this specific version if running or generic mysql
|
// Force kill this specific version if running or generic mysql
|
||||||
await processManager.forceKillAll(serviceName)
|
await processManager.forceKillAll(serviceName)
|
||||||
|
|
||||||
// Also kill anything else listening on the same port
|
// 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
|
// Initialize if data directory is empty
|
||||||
if (!fs.existsSync(dataDir) || fs.readdirSync(dataDir).length === 0) {
|
if (!fs.existsSync(dataDir) || fs.readdirSync(dataDir).length === 0) {
|
||||||
if (!fs.existsSync(dataDir)) fs.mkdirSync(dataDir, { recursive: true })
|
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 {
|
||||||
// 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, '/')}`])
|
await processManager.runOnce(binPath, ['--initialize-insecure', `--datadir=${dataDir.replace(/\\/g, '/')}`])
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(`Modern init failed, trying template copy for ${serviceName}...`)
|
console.log(`Modern init failed, trying template copy for ${serviceName}...`)
|
||||||
// Fallback for 5.6: Copy default 'data' folder from binary dir if it exists
|
// Fallback: Copy default 'data' folder from binary dir if it exists
|
||||||
const templateDataDir = path.join(mysqlDir, 'data')
|
const templateDataDir = path.join(dbBinDir, 'data')
|
||||||
if (fs.existsSync(templateDataDir)) {
|
if (fs.existsSync(templateDataDir)) {
|
||||||
// Recursively copy template data (mysql, performance_schema, etc.)
|
|
||||||
fs.cpSync(templateDataDir, dataDir, { recursive: true })
|
fs.cpSync(templateDataDir, dataDir, { recursive: true })
|
||||||
console.log(`Successfully copied template data for ${serviceName}`)
|
console.log(`Successfully copied template data for ${serviceName}`)
|
||||||
} else {
|
} 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 sanitizedServiceName = serviceName.replace(/:/g, '_')
|
||||||
const logFile = path.join(configService.getBasePath(), 'logs', `${sanitizedServiceName}_error.log`)
|
const logFile = path.join(configService.getBasePath(), 'logs', `${sanitizedServiceName}_error.log`)
|
||||||
|
|
||||||
const configPath = await configService.generateConfig('my.ini.template', `my_${mysqlId || 'default'}.ini`, {
|
const configPath = await configService.generateConfig('mariadb.ini.template', `mariadb_${dbId || 'default'}.ini`, {
|
||||||
PORT: mysqlPort,
|
PORT: dbPort,
|
||||||
DATADIR: dataDir.replace(/\\/g, '/'),
|
DATADIR: dataDir.replace(/\\/g, '/'),
|
||||||
SOCKET: `/tmp/${sanitizedServiceName}.sock`,
|
SOCKET: `/tmp/${sanitizedServiceName}.sock`,
|
||||||
LOG_FILE: logFile.replace(/\\/g, '/')
|
LOG_FILE: logFile.replace(/\\/g, '/')
|
||||||
@@ -258,18 +260,18 @@ export function registerIpcHandlers(): void {
|
|||||||
const success = await processManager.startService(serviceName, binPath, ['--defaults-file=' + configPath], false)
|
const success = await processManager.startService(serviceName, binPath, ['--defaults-file=' + configPath], false)
|
||||||
|
|
||||||
if (success) {
|
if (success) {
|
||||||
// Quick health check for MySQL
|
// Quick health check
|
||||||
await new Promise(r => setTimeout(r, 1500))
|
await new Promise(r => setTimeout(r, 1500))
|
||||||
if (!processManager.isServiceRunning(serviceName)) {
|
if (!processManager.isServiceRunning(serviceName)) {
|
||||||
const errorLogs = await processManager.getLastErrorLogs(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 {
|
} else {
|
||||||
const errorLogs = await processManager.getLastErrorLogs(serviceName)
|
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.' }
|
return { success: false, message: 'Bilinmeyen servis.' }
|
||||||
@@ -320,30 +322,30 @@ export function registerIpcHandlers(): void {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// MySQL Versions
|
// MariaDB Versions
|
||||||
ipcMain.handle('mysql:versions', async () => {
|
ipcMain.handle('mariadb:versions', async () => {
|
||||||
return await mySqlManagerService.getVersions()
|
return await mariaDbManagerService.getVersions()
|
||||||
})
|
})
|
||||||
|
|
||||||
ipcMain.handle('mysql:download', async (event, id: string) => {
|
ipcMain.handle('mariadb:download', async (event, id: string) => {
|
||||||
const versions = await mySqlManagerService.getVersions()
|
const versions = await mariaDbManagerService.getVersions()
|
||||||
const version = versions.find(v => v.id === id)
|
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 {
|
try {
|
||||||
mySqlManagerService.updateProgress(id, 0, 'downloading')
|
mariaDbManagerService.updateProgress(id, 0, 'downloading')
|
||||||
event.sender.send('mysql:progress', { id, progress: 0 })
|
event.sender.send('mariadb:progress', { id, progress: 0 })
|
||||||
|
|
||||||
await downloadService.downloadAndExtract(version.url, id, (p) => {
|
await downloadService.downloadAndExtract(version.url, id, (p) => {
|
||||||
mySqlManagerService.updateProgress(id, p)
|
mariaDbManagerService.updateProgress(id, p)
|
||||||
event.sender.send('mysql:progress', { id, progress: p })
|
event.sender.send('mariadb:progress', { id, progress: p })
|
||||||
})
|
})
|
||||||
|
|
||||||
mySqlManagerService.updateProgress(id, 100, 'installed')
|
mariaDbManagerService.updateProgress(id, 100, 'installed')
|
||||||
return { success: true, message: 'MySQL indirme tamamlandı.' }
|
return { success: true, message: 'MariaDB indirme tamamlandı.' }
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
mySqlManagerService.updateProgress(id, 0, 'available')
|
mariaDbManagerService.updateProgress(id, 0, 'available')
|
||||||
return { success: false, message: `MySQL hatası: ${error.message}` }
|
return { success: false, message: `MariaDB hatası: ${error.message}` }
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -565,8 +567,8 @@ export function registerIpcHandlers(): void {
|
|||||||
return await phpMyAdminService.isInstalled()
|
return await phpMyAdminService.isInstalled()
|
||||||
})
|
})
|
||||||
|
|
||||||
ipcMain.handle('pma:setup', async (event, mysqlPort: string) => {
|
ipcMain.handle('pma:setup', async (event, mariaDbPort: string) => {
|
||||||
return await phpMyAdminService.setup(mysqlPort, (p) => {
|
return await phpMyAdminService.setup(mariaDbPort, (p) => {
|
||||||
event.sender.send('pma:progress', p)
|
event.sender.send('pma:progress', p)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -575,13 +577,13 @@ export function registerIpcHandlers(): void {
|
|||||||
try {
|
try {
|
||||||
await processManager.forceKillAll(serviceName)
|
await processManager.forceKillAll(serviceName)
|
||||||
|
|
||||||
const versionMatch = serviceName.match(/^mysql:(.+)$/)
|
const versionMatch = serviceName.match(/^(?:mariadb|mysql):(.+)$/)
|
||||||
const mysqlVersion = versionMatch ? versionMatch[1] : null
|
const dbVersion = versionMatch ? versionMatch[1] : null
|
||||||
const mysqlId = mysqlVersion ? `mysql-${mysqlVersion}` : 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)) {
|
if (fs.existsSync(dataDir)) {
|
||||||
fs.rmSync(dataDir, { recursive: true, force: true })
|
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 }) => {
|
// MariaDB Operations
|
||||||
const versions = await mySqlManagerService.getVersions()
|
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)
|
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 binDir = path.join(configService.getBasePath(), 'bin', versionId)
|
||||||
const binPath = findExecutable(binDir, 'mysql.exe')
|
const isWindows = process.platform === 'win32'
|
||||||
if (!binPath) return { exists: false, error: 'mysql.exe not found' }
|
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,
|
binPath,
|
||||||
host: '127.0.0.1',
|
host: '127.0.0.1',
|
||||||
port: v.port,
|
port: v.port,
|
||||||
@@ -611,33 +615,34 @@ export function registerIpcHandlers(): void {
|
|||||||
return { exists }
|
return { exists }
|
||||||
})
|
})
|
||||||
|
|
||||||
ipcMain.handle('mysql:import-wizard:start', async (event, { versionId, filePath, dbName, user, pass, rootPass, overwrite }) => {
|
ipcMain.handle('mariadb:import-wizard:start', async (event, { versionId, filePath, dbName, user, pass, rootPass, overwrite }) => {
|
||||||
const versions = await mySqlManagerService.getVersions()
|
const versions = await mariaDbManagerService.getVersions()
|
||||||
const v = versions.find(v => v.id === versionId)
|
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 binDir = path.join(configService.getBasePath(), 'bin', versionId)
|
||||||
const mysqlBin = findExecutable(binDir, 'mysql.exe')
|
const isWindows = process.platform === 'win32'
|
||||||
if (!mysqlBin) return { success: false, message: 'mysql.exe not found' }
|
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 = {
|
const config = {
|
||||||
binPath: mysqlBin,
|
binPath: dbBin,
|
||||||
host: '127.0.0.1',
|
host: '127.0.0.1',
|
||||||
port: v.port,
|
port: v.port,
|
||||||
rootUser: 'root',
|
rootUser: 'root',
|
||||||
rootPass: rootPass || ''
|
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 {
|
try {
|
||||||
if (overwrite) {
|
if (overwrite) {
|
||||||
log(`Dropping existing database: ${dbName}`)
|
log(`Dropping existing database: ${dbName}`)
|
||||||
await mySqlOperationService.dropDatabase(config, dbName)
|
await mariaDbOperationService.dropDatabase(config, dbName)
|
||||||
}
|
}
|
||||||
|
|
||||||
log(`Creating database and user: ${dbName} / ${user}`)
|
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
|
if (!createRes.success) return createRes
|
||||||
|
|
||||||
let sqlFiles: string[] = []
|
let sqlFiles: string[] = []
|
||||||
@@ -645,14 +650,14 @@ export function registerIpcHandlers(): void {
|
|||||||
sqlFiles = [filePath]
|
sqlFiles = [filePath]
|
||||||
} else {
|
} else {
|
||||||
log(`Extracting archive: ${path.basename(filePath)}`)
|
log(`Extracting archive: ${path.basename(filePath)}`)
|
||||||
const extractDir = path.join(app.getPath('temp'), `mysql_import_${Date.now()}`)
|
const extractDir = path.join(app.getPath('temp'), `mariadb_import_${Date.now()}`)
|
||||||
sqlFiles = await mySqlOperationService.extractArchive(filePath, extractDir)
|
sqlFiles = await mariaDbOperationService.extractArchive(filePath, extractDir)
|
||||||
if (sqlFiles.length === 0) return { success: false, message: 'No .sql files found in archive.' }
|
if (sqlFiles.length === 0) return { success: false, message: 'No .sql files found in archive.' }
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const sqlFile of sqlFiles) {
|
for (const sqlFile of sqlFiles) {
|
||||||
log(`Importing: ${path.basename(sqlFile)}`)
|
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
|
if (!importRes.success) return importRes
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -662,17 +667,18 @@ export function registerIpcHandlers(): void {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
ipcMain.handle('mysql:export-wizard:list-dbs', async (_event, { versionId, rootPass }) => {
|
ipcMain.handle('mariadb:export-wizard:list-dbs', async (_event, { versionId, rootPass }) => {
|
||||||
const versions = await mySqlManagerService.getVersions()
|
const versions = await mariaDbManagerService.getVersions()
|
||||||
const v = versions.find(v => v.id === versionId)
|
const v = versions.find(v => v.id === versionId)
|
||||||
if (!v) return []
|
if (!v) return []
|
||||||
|
|
||||||
const binDir = path.join(configService.getBasePath(), 'bin', versionId)
|
const binDir = path.join(configService.getBasePath(), 'bin', versionId)
|
||||||
const mysqlBin = findExecutable(binDir, 'mysql.exe')
|
const isWindows = process.platform === 'win32'
|
||||||
if (!mysqlBin) return []
|
const dbBin = findExecutable(binDir, isWindows ? 'mariadb.exe' : 'mariadb') || findExecutable(binDir, isWindows ? 'mysql.exe' : 'mysql')
|
||||||
|
if (!dbBin) return []
|
||||||
|
|
||||||
return await mySqlOperationService.listDatabases({
|
return await mariaDbOperationService.listDatabases({
|
||||||
binPath: mysqlBin,
|
binPath: dbBin,
|
||||||
host: '127.0.0.1',
|
host: '127.0.0.1',
|
||||||
port: v.port,
|
port: v.port,
|
||||||
rootUser: 'root',
|
rootUser: 'root',
|
||||||
@@ -680,19 +686,20 @@ export function registerIpcHandlers(): void {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
ipcMain.handle('mysql:export-wizard:start', async (event, { versionId, dbName, savePath, compress, rootPass }) => {
|
ipcMain.handle('mariadb:export-wizard:start', async (event, { versionId, dbName, savePath, compress, rootPass }) => {
|
||||||
const versions = await mySqlManagerService.getVersions()
|
const versions = await mariaDbManagerService.getVersions()
|
||||||
const v = versions.find(v => v.id === versionId)
|
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 binDir = path.join(configService.getBasePath(), 'bin', versionId)
|
||||||
const mysqlBin = findExecutable(binDir, 'mysql.exe')
|
const isWindows = process.platform === 'win32'
|
||||||
if (!mysqlBin) return { success: false, message: 'mysql.exe not found' }
|
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({
|
return await mariaDbOperationService.exportSql({
|
||||||
binPath: mysqlBin,
|
binPath: dbBin,
|
||||||
host: '127.0.0.1',
|
host: '127.0.0.1',
|
||||||
port: v.port,
|
port: v.port,
|
||||||
rootUser: 'root',
|
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 = {
|
private defaultSettings = {
|
||||||
nginxPort: '80',
|
nginxPort: '80',
|
||||||
phpPort: '9001',
|
phpPort: '9001',
|
||||||
mysqlPort: '3306',
|
mariaDbPort: '3306',
|
||||||
mysqlPorts: {} as Record<string, string>,
|
mariaDbPorts: {} as Record<string, string>,
|
||||||
language: 'tr'
|
language: 'tr'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,9 @@ export class DownloadService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async downloadAndExtract(url: string, targetName: string, onProgress?: (p: number) => void, customTargetDir?: string): Promise<string> {
|
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)
|
const targetDir = customTargetDir || path.join(this.binDir, targetName)
|
||||||
|
|
||||||
// Cleanup failed/previous attempts if we want a fresh start
|
// Cleanup failed/previous attempts if we want a fresh start
|
||||||
@@ -70,7 +72,9 @@ export class DownloadService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getBinPath(service: string, executable: string): string {
|
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 { app } from 'electron'
|
||||||
import { configService } from './ConfigService'
|
import { configService } from './ConfigService'
|
||||||
|
|
||||||
export interface MySqlVersion {
|
export interface MariaDbVersion {
|
||||||
id: string
|
id: string
|
||||||
version: string
|
version: string
|
||||||
cycle: string
|
cycle: string
|
||||||
@@ -14,16 +14,16 @@ export interface MySqlVersion {
|
|||||||
description?: string
|
description?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export class MySqlManagerService {
|
export class MariaDbManagerService {
|
||||||
private versions: MySqlVersion[] = []
|
private versions: MariaDbVersion[] = []
|
||||||
private binDir: string
|
private binDir: string
|
||||||
private catalogPath: string
|
private catalogPath: string
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.binDir = path.join(configService.getBasePath(), 'bin')
|
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)) {
|
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()
|
this.init()
|
||||||
}
|
}
|
||||||
@@ -34,54 +34,60 @@ export class MySqlManagerService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fetchAvailableVersions(): Promise<MySqlVersion[]> {
|
async fetchAvailableVersions(): Promise<MariaDbVersion[]> {
|
||||||
console.log('Loading MySQL versions from local catalog:', this.catalogPath)
|
console.log('Loading MariaDB versions from local catalog:', this.catalogPath)
|
||||||
try {
|
try {
|
||||||
if (!fs.existsSync(this.catalogPath)) {
|
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
|
return this.versions
|
||||||
}
|
}
|
||||||
|
|
||||||
const rawData = fs.readFileSync(this.catalogPath, 'utf8')
|
const rawData = fs.readFileSync(this.catalogPath, 'utf8')
|
||||||
const catalog = JSON.parse(rawData)
|
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 version = v.version
|
||||||
const cycle = v.series
|
const cycle = v.series
|
||||||
|
|
||||||
// Extract MAJOR.MINOR (e.g. 8.0 from 8.0.40)
|
// Resolve URL based on platform and architecture
|
||||||
const versionParts = version.split('.')
|
let url = '';
|
||||||
const majorMinor = `${versionParts[0]}.${versionParts[1]}`
|
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
|
if (!url) {
|
||||||
const url = `https://cdn.mysql.com/archives/mysql-${majorMinor}/mysql-${version}-winx64.zip`
|
console.warn(`No MariaDB URL found for platform ${platform}/${arch} in version ${version}`);
|
||||||
|
}
|
||||||
|
|
||||||
const settings = configService.getSettings()
|
const settings = configService.getSettings()
|
||||||
const defaultPort = (3306 + index).toString()
|
const defaultPort = (3306 + index).toString()
|
||||||
const customPort = settings.mysqlPorts?.[`mysql-${version}`] || defaultPort
|
const customPort = settings.mariaDbPorts?.[`mariadb-${version}`] || defaultPort
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: `mysql-${version}`,
|
id: `mariadb-${version}`,
|
||||||
version,
|
version,
|
||||||
cycle,
|
cycle,
|
||||||
url,
|
url,
|
||||||
status: 'available',
|
status: 'available' as MariaDbVersion['status'],
|
||||||
progress: 0,
|
progress: 0,
|
||||||
port: customPort,
|
port: customPort,
|
||||||
description: v.status
|
description: v.status
|
||||||
}
|
}
|
||||||
})
|
}).filter(v => v.url !== '') // Only include versions available for this platform
|
||||||
|
|
||||||
this.updateVersionsList(dynamicVersions)
|
this.updateVersionsList(dynamicVersions)
|
||||||
return this.versions
|
return this.versions
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.error('Failed to load MySQL catalog:', e.message)
|
console.error('Failed to load MariaDB catalog:', e.message)
|
||||||
return this.versions
|
return this.versions
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private updateVersionsList(newList: MySqlVersion[]) {
|
private updateVersionsList(newList: MariaDbVersion[]) {
|
||||||
this.versions = newList.map(dv => {
|
this.versions = newList.map(dv => {
|
||||||
const current = this.versions.find(cv => cv.id === dv.id)
|
const current = this.versions.find(cv => cv.id === dv.id)
|
||||||
if (current && current.status === 'downloading') return current
|
if (current && current.status === 'downloading') return current
|
||||||
@@ -92,14 +98,19 @@ export class MySqlManagerService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private refreshInstalledStatus() {
|
private refreshInstalledStatus() {
|
||||||
|
const isWindows = process.platform === 'win32';
|
||||||
|
const binaryName = isWindows ? 'mariadbd.exe' : 'mariadbd';
|
||||||
|
|
||||||
this.versions = this.versions.map(v => {
|
this.versions = this.versions.map(v => {
|
||||||
const targetDir = path.join(this.binDir, v.id)
|
const targetDir = path.join(this.binDir, v.id)
|
||||||
const possiblePaths = [
|
const possibleBinPaths = [
|
||||||
path.join(targetDir, 'bin', 'mysqld.exe'),
|
path.join(targetDir, 'bin', binaryName),
|
||||||
path.join(targetDir, `mysql-${v.version}-winx64`, 'bin', 'mysqld.exe')
|
// 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) {
|
if (isInstalled) {
|
||||||
return { ...v, status: 'installed', progress: 100 }
|
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) {
|
if (this.versions.length === 0) {
|
||||||
await this.fetchAvailableVersions()
|
await this.fetchAvailableVersions()
|
||||||
} else {
|
} else {
|
||||||
@@ -117,7 +128,7 @@ export class MySqlManagerService {
|
|||||||
return this.versions
|
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)
|
const version = this.versions.find(v => v.id === id)
|
||||||
if (version) {
|
if (version) {
|
||||||
version.progress = progress
|
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 decompress from 'decompress'
|
||||||
import zlib from 'zlib'
|
import zlib from 'zlib'
|
||||||
|
|
||||||
export interface MySqlConfig {
|
export interface MariaDbConfig {
|
||||||
binPath: string
|
binPath: string
|
||||||
host: string
|
host: string
|
||||||
port: string
|
port: string
|
||||||
@@ -12,7 +12,7 @@ export interface MySqlConfig {
|
|||||||
rootPass: string
|
rootPass: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export class MySqlOperationService {
|
export class MariaDbOperationService {
|
||||||
async extractArchive(archivePath: string, targetDir: string): Promise<string[]> {
|
async extractArchive(archivePath: string, targetDir: string): Promise<string[]> {
|
||||||
if (!fs.existsSync(targetDir)) {
|
if (!fs.existsSync(targetDir)) {
|
||||||
fs.mkdirSync(targetDir, { recursive: true })
|
fs.mkdirSync(targetDir, { recursive: true })
|
||||||
@@ -58,8 +58,9 @@ export class MySqlOperationService {
|
|||||||
return sqlFiles
|
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) => {
|
return new Promise((resolve) => {
|
||||||
|
const mariaDbPath = config.binPath;
|
||||||
const args = [
|
const args = [
|
||||||
`--host=${config.host}`,
|
`--host=${config.host}`,
|
||||||
`--port=${config.port}`,
|
`--port=${config.port}`,
|
||||||
@@ -73,13 +74,17 @@ export class MySqlOperationService {
|
|||||||
|
|
||||||
args.push('-e', query)
|
args.push('-e', query)
|
||||||
|
|
||||||
const child = spawn(config.binPath, args, { windowsHide: true })
|
const child = spawn(mariaDbPath, args, { windowsHide: true })
|
||||||
let output = ''
|
let output = ''
|
||||||
let error = ''
|
let error = ''
|
||||||
|
|
||||||
child.stdout.on('data', (data) => output += data.toString())
|
child.stdout.on('data', (data) => output += data.toString())
|
||||||
child.stderr.on('data', (data) => error += 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) => {
|
child.on('close', (code) => {
|
||||||
if (code === 0) {
|
if (code === 0) {
|
||||||
resolve({ success: true, output })
|
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}'`)
|
const result = await this.runQuery(config, `SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = '${dbName}'`)
|
||||||
return result.success && result.output.includes(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 = [
|
const queries = [
|
||||||
`CREATE DATABASE IF NOT EXISTS \`${dbName}\` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;`,
|
`CREATE DATABASE IF NOT EXISTS \`${dbName}\` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;`,
|
||||||
`CREATE USER IF NOT EXISTS '${user}'@'%' IDENTIFIED BY '${pass}';`,
|
`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.' }
|
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}\``)
|
const result = await this.runQuery(config, `DROP DATABASE IF EXISTS \`${dbName}\``)
|
||||||
return { success: result.success, message: result.success ? 'Database dropped' : `Drop failed: ${result.error}` }
|
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) => {
|
return new Promise((resolve) => {
|
||||||
const mysqlPath = config.binPath
|
const mariaDbPath = config.binPath
|
||||||
const args = [
|
const args = [
|
||||||
`--host=${config.host}`,
|
`--host=${config.host}`,
|
||||||
`--port=${config.port}`,
|
`--port=${config.port}`,
|
||||||
@@ -132,9 +137,9 @@ export class MySqlOperationService {
|
|||||||
args.push(`--password=${config.rootPass}`)
|
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
|
// Pipe the file content to stdin
|
||||||
const fileStream = fs.createReadStream(sqlFilePath)
|
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')
|
const result = await this.runQuery(config, 'SHOW DATABASES')
|
||||||
if (!result.success) return []
|
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))
|
.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) => {
|
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 = [
|
const args = [
|
||||||
`--host=${config.host}`,
|
`--host=${config.host}`,
|
||||||
`--port=${config.port}`,
|
`--port=${config.port}`,
|
||||||
@@ -219,11 +238,11 @@ export class MySqlOperationService {
|
|||||||
resolve({ success: true, message: 'Export completed successfully.' })
|
resolve({ success: true, message: 'Export completed successfully.' })
|
||||||
} else {
|
} else {
|
||||||
onProgress?.(`Export failed with exit code ${code}`)
|
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'))
|
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)) {
|
if (!fs.existsSync(this.pmaDir)) {
|
||||||
fs.mkdirSync(this.pmaDir, { recursive: true })
|
fs.mkdirSync(this.pmaDir, { recursive: true })
|
||||||
}
|
}
|
||||||
@@ -38,7 +38,7 @@ export class PhpMyAdminService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Generate config.inc.php
|
// Generate config.inc.php
|
||||||
this.generateConfig(mysqlPort)
|
this.generateConfig(mariaDbPort)
|
||||||
|
|
||||||
return true
|
return true
|
||||||
} catch (error) {
|
} 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 configPath = path.join(this.pmaDir, 'config.inc.php')
|
||||||
const blowfishSecret = Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15)
|
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 = 0;
|
||||||
$i++;
|
$i++;
|
||||||
$cfg['Servers'][$i]['auth_type'] = 'config';
|
$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]['user'] = 'root';
|
||||||
$cfg['Servers'][$i]['password'] = '';
|
$cfg['Servers'][$i]['password'] = '';
|
||||||
$cfg['Servers'][$i]['extension'] = 'mysqli';
|
$cfg['Servers'][$i]['extension'] = 'mysqli';
|
||||||
|
|||||||
@@ -4,14 +4,14 @@ import { configService } from './ConfigService'
|
|||||||
|
|
||||||
interface PortMap {
|
interface PortMap {
|
||||||
php: Record<string, number>;
|
php: Record<string, number>;
|
||||||
mysql: Record<string, number>;
|
mariadb: Record<string, number>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class PortMappingService {
|
export class PortMappingService {
|
||||||
private configFile: string
|
private configFile: string
|
||||||
private portMap: PortMap = { php: {}, mysql: {} }
|
private portMap: PortMap = { php: {}, mariadb: {} }
|
||||||
private nextPhpPort = 9001
|
private nextPhpPort = 9001
|
||||||
private nextMysqlPort = 3306
|
private nextMariaDbPort = 3306
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.configFile = path.join(configService.getBasePath(), 'port_mappings.json')
|
this.configFile = path.join(configService.getBasePath(), 'port_mappings.json')
|
||||||
@@ -26,8 +26,8 @@ export class PortMappingService {
|
|||||||
const phpPorts = Object.values(this.portMap.php)
|
const phpPorts = Object.values(this.portMap.php)
|
||||||
if (phpPorts.length > 0) this.nextPhpPort = Math.max(...phpPorts) + 1
|
if (phpPorts.length > 0) this.nextPhpPort = Math.max(...phpPorts) + 1
|
||||||
|
|
||||||
const mysqlPorts = Object.values(this.portMap.mysql)
|
const mariaDbPorts = Object.values(this.portMap.mariadb)
|
||||||
if (mysqlPorts.length > 0) this.nextMysqlPort = Math.max(...mysqlPorts) + 1
|
if (mariaDbPorts.length > 0) this.nextMariaDbPort = Math.max(...mariaDbPorts) + 1
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('Failed to load port mappings', e)
|
console.error('Failed to load port mappings', e)
|
||||||
}
|
}
|
||||||
@@ -46,12 +46,12 @@ export class PortMappingService {
|
|||||||
return this.portMap.php[version]
|
return this.portMap.php[version]
|
||||||
}
|
}
|
||||||
|
|
||||||
getMySqlPort(version: string): number {
|
getMariaDbPort(version: string): number {
|
||||||
if (!this.portMap.mysql[version]) {
|
if (!this.portMap.mariadb[version]) {
|
||||||
this.portMap.mysql[version] = this.nextMysqlPort++
|
this.portMap.mariadb[version] = this.nextMariaDbPort++
|
||||||
this.save()
|
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 })
|
fs.mkdirSync(this.logDir, { recursive: true })
|
||||||
}
|
}
|
||||||
// Initialize with default logs
|
// Initialize with default logs
|
||||||
['nginx', 'php', 'mysql'].forEach(s => {
|
['nginx', 'php', 'mariadb'].forEach(s => {
|
||||||
if (!this.logs.has(s)) {
|
if (!this.logs.has(s)) {
|
||||||
this.logs.set(s, [`[${new Date().toLocaleTimeString()}] Sistem hazır. İşlem bekleniyor...`])
|
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)
|
const child = this.processes.get(name)
|
||||||
if (child) {
|
if (child) {
|
||||||
this.addLog(name, 'Stopping service...')
|
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)
|
this.processes.delete(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -167,117 +176,103 @@ export class ProcessManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async forceKillAll(name: string): Promise<boolean> {
|
async forceKillAll(name: string): Promise<boolean> {
|
||||||
|
const isWindows = process.platform === 'win32';
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
const childProcess = this.processes.get(name)
|
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
|
if (isWindows) {
|
||||||
// For MySQL multi-instance, we should avoid global kill if possible.
|
if (childProcess && childProcess.pid) {
|
||||||
const isVersionedMysql = name.startsWith('mysql:')
|
this.addLog(name, `Killing process by PID: ${childProcess.pid}`)
|
||||||
const executableName = name === 'nginx' ? 'nginx.exe' : name === 'php' ? 'php-cgi.exe' : 'mysqld.exe'
|
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) {
|
const isVersionedMariaDb = name.startsWith('mariadb:') || name.startsWith('mysql:')
|
||||||
// For versioned mysql, we try to find by command line (contains data directory name)
|
|
||||||
const mysqlId = name.replace('mysql:', 'mysql-')
|
if (isVersionedMariaDb) {
|
||||||
this.addLog(name, `Attempting to kill specific MySQL instance: ${mysqlId}`)
|
const dbVersion = name.split(':')[1]
|
||||||
// Using powershell to find and kill as wmic is deprecated/missing on some Win11
|
const dbId = `mariadb-${dbVersion}`
|
||||||
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], {
|
if (process.platform === 'win32') {
|
||||||
windowsHide: true,
|
const psCommand = `Get-CimInstance Win32_Process -Filter "name = 'mariadbd.exe' OR name = 'mysqld.exe'" | Where-Object { $_.CommandLine -like '*${dbId}*' } | Invoke-CimMethod -MethodName Terminate`
|
||||||
shell: false
|
spawn('powershell', ['-Command', psCommand], { windowsHide: true })
|
||||||
})
|
.on('close', () => resolve(true))
|
||||||
ps.on('error', (err) => {
|
.on('error', () => resolve(true))
|
||||||
this.addLog(name, `PowerShell kill error: ${err.message}`)
|
} else {
|
||||||
resolve(true)
|
// Unix: pkill -f matches full command line (data directory path usually contains the dbId)
|
||||||
})
|
spawn('pkill', ['-f', dbId], { windowsHide: true })
|
||||||
ps.on('close', () => resolve(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 {
|
} else {
|
||||||
this.addLog(name, `Force killing all ${executableName} processes...`)
|
// macOS / Linux
|
||||||
const tk = spawn('taskkill', ['/F', '/IM', executableName, '/T'], {
|
if (childProcess && childProcess.pid) {
|
||||||
windowsHide: true,
|
try { process.kill(childProcess.pid, 'SIGKILL') } catch (e) { }
|
||||||
shell: false
|
this.processes.delete(name)
|
||||||
})
|
}
|
||||||
tk.on('error', (err) => {
|
|
||||||
this.addLog(name, `Taskkill error: ${err.message}`)
|
const procName = name === 'nginx' ? 'nginx' : name === 'php' ? 'php-fpm' : 'mariadbd'
|
||||||
resolve(true)
|
spawn('pkill', ['-9', '-f', procName])
|
||||||
})
|
.on('close', () => resolve(true))
|
||||||
tk.on('close', () => resolve(true))
|
.on('error', () => resolve(true))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async killByPort(name: string, port: string | number): Promise<boolean> {
|
async killByPort(name: string, port: string | number): Promise<boolean> {
|
||||||
|
const isWindows = process.platform === 'win32';
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
if (!port) return resolve(true)
|
if (!port) return resolve(true)
|
||||||
|
|
||||||
this.addLog(name, `Checking if port ${port} is in use...`)
|
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 = ''
|
if (isWindows) {
|
||||||
netstat.stdout?.on('data', (data) => {
|
const cmd = `netstat -ano | findstr :${port} | findstr LISTENING`
|
||||||
output += data.toString()
|
const netstat = spawn('cmd.exe', ['/c', cmd], { windowsHide: true })
|
||||||
})
|
let output = ''
|
||||||
|
netstat.stdout?.on('data', d => output += d.toString())
|
||||||
netstat.on('close', () => {
|
netstat.on('close', () => {
|
||||||
const lines = output.split('\n').filter(l => l.trim())
|
const lines = output.split('\n').filter(l => l.trim())
|
||||||
if (lines.length === 0) {
|
if (lines.length === 0) return resolve(true)
|
||||||
this.addLog(name, `Port ${port} is free.`)
|
const pids = new Set<string>()
|
||||||
return resolve(true)
|
lines.forEach(line => {
|
||||||
}
|
const parts = line.trim().split(/\s+/)
|
||||||
|
const pid = parts[parts.length - 1]
|
||||||
// netstat output format: TCP 0.0.0.0:3306 0.0.0.0:0 LISTENING 1234
|
if (pid && !isNaN(parseInt(pid))) pids.add(pid)
|
||||||
// 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())
|
|
||||||
})
|
})
|
||||||
|
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))
|
||||||
})
|
})
|
||||||
|
} else {
|
||||||
Promise.all(killProcesses).then(() => {
|
const lsof = spawn('lsof', ['-i', `:${port}`, '-t'])
|
||||||
this.addLog(name, `Successfully cleared port ${port}.`)
|
let output = ''
|
||||||
resolve(true)
|
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
|
// Ensure defaults are present if stopped
|
||||||
if (!statuses['nginx']) statuses['nginx'] = 'stopped'
|
if (!statuses['nginx']) statuses['nginx'] = 'stopped'
|
||||||
if (!statuses['mysql']) statuses['mysql'] = 'stopped'
|
if (!statuses['mariadb']) statuses['mariadb'] = 'stopped'
|
||||||
return statuses
|
return statuses
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -300,7 +295,7 @@ export class ProcessManager {
|
|||||||
if (!this.logDir) return
|
if (!this.logDir) return
|
||||||
const logPaths: Record<string, string> = {
|
const logPaths: Record<string, string> = {
|
||||||
nginx: path.join(this.logDir, 'nginx_error.log'),
|
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]
|
const logPath = logPaths[name]
|
||||||
@@ -341,7 +336,7 @@ export class ProcessManager {
|
|||||||
const possibleFiles = [
|
const possibleFiles = [
|
||||||
path.join(this.logDir, `${sanitizedName}.log`),
|
path.join(this.logDir, `${sanitizedName}.log`),
|
||||||
path.join(this.logDir, `${sanitizedName}_error.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`)
|
path.join(this.logDir, `nginx_error.log`)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ export interface Project {
|
|||||||
name: string
|
name: string
|
||||||
path: string
|
path: string
|
||||||
phpVersion: string
|
phpVersion: string
|
||||||
mySqlVersion: string
|
mariaDbVersion: string
|
||||||
host: string
|
host: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+15
-10
@@ -16,9 +16,9 @@ const api = {
|
|||||||
getPhpVersions: () => electronAPI.ipcRenderer.invoke('binaries:list-php'),
|
getPhpVersions: () => electronAPI.ipcRenderer.invoke('binaries:list-php'),
|
||||||
downloadPhpVersion: (id: string) => electronAPI.ipcRenderer.invoke('binaries:download-php', id),
|
downloadPhpVersion: (id: string) => electronAPI.ipcRenderer.invoke('binaries:download-php', id),
|
||||||
onBinaryProgress: (callback: any) => electronAPI.ipcRenderer.on('binaries:progress', (_event, data) => callback(data)),
|
onBinaryProgress: (callback: any) => electronAPI.ipcRenderer.on('binaries:progress', (_event, data) => callback(data)),
|
||||||
getMySqlVersions: () => electronAPI.ipcRenderer.invoke('mysql:versions'),
|
getMariaDbVersions: () => electronAPI.ipcRenderer.invoke('mariadb:versions'),
|
||||||
downloadMySqlVersion: (id: string) => electronAPI.ipcRenderer.invoke('mysql:download', id),
|
downloadMariaDbVersion: (id: string) => electronAPI.ipcRenderer.invoke('mariadb:download', id),
|
||||||
onMySqlProgress: (callback: any) => electronAPI.ipcRenderer.on('mysql:progress', (_event, data) => callback(data)),
|
onMariaDbProgress: (callback: any) => electronAPI.ipcRenderer.on('mariadb:progress', (_event, data) => callback(data)),
|
||||||
selectDirectory: () => electronAPI.ipcRenderer.invoke('dialog:select-directory'),
|
selectDirectory: () => electronAPI.ipcRenderer.invoke('dialog:select-directory'),
|
||||||
getSettings: () => electronAPI.ipcRenderer.invoke('config:get'),
|
getSettings: () => electronAPI.ipcRenderer.invoke('config:get'),
|
||||||
saveSettings: (settings: any) => electronAPI.ipcRenderer.invoke('config:save', settings),
|
saveSettings: (settings: any) => electronAPI.ipcRenderer.invoke('config:save', settings),
|
||||||
@@ -26,15 +26,20 @@ const api = {
|
|||||||
downloadNginxVersion: (id: string) => electronAPI.ipcRenderer.invoke('nginx:download', id),
|
downloadNginxVersion: (id: string) => electronAPI.ipcRenderer.invoke('nginx:download', id),
|
||||||
onNginxProgress: (callback: any) => electronAPI.ipcRenderer.on('nginx:progress', (_event, data) => callback(data)),
|
onNginxProgress: (callback: any) => electronAPI.ipcRenderer.on('nginx:progress', (_event, data) => callback(data)),
|
||||||
pmaStatus: () => electronAPI.ipcRenderer.invoke('pma:status'),
|
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)),
|
onPmaProgress: (callback: any) => electronAPI.ipcRenderer.on('pma:progress', (_event, data) => callback(data)),
|
||||||
selectFile: (filters?: any[]) => electronAPI.ipcRenderer.invoke('dialog:select-file', filters),
|
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 }),
|
checkMariaDbDb: (versionId: string, dbName: string) => electronAPI.ipcRenderer.invoke('mariadb:import-wizard:check-db', { versionId, dbName }),
|
||||||
startMySqlImport: (data: any) => electronAPI.ipcRenderer.invoke('mysql:import-wizard:start', data),
|
startMariaDbImport: (data: any) => electronAPI.ipcRenderer.invoke('mariadb:import-wizard:start', data),
|
||||||
onMySqlImportLog: (callback: any) => {
|
onMariaDbImportLog: (callback: any) => {
|
||||||
const listener = (_event, msg) => callback(msg)
|
const listener = (_event, msg: any) => callback(msg)
|
||||||
electronAPI.ipcRenderer.on('mysql:import:log', listener)
|
electronAPI.ipcRenderer.on('mariadb:import:log', listener)
|
||||||
return () => electronAPI.ipcRenderer.removeListener('mysql: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)
|
invoke: (channel: string, ...args: any[]) => electronAPI.ipcRenderer.invoke(channel, ...args)
|
||||||
}
|
}
|
||||||
|
|||||||
+14
-14
@@ -1,17 +1,17 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<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>
|
<head>
|
||||||
<div id="root"></div>
|
<meta charset="UTF-8" />
|
||||||
<script type="module" src="/src/main.tsx"></script>
|
<title>Trunçgil Multi-PHP & MariaDB Server</title>
|
||||||
</body>
|
<!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
|
||||||
</html>
|
<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'
|
} from '@mui/icons-material'
|
||||||
import Swal from 'sweetalert2'
|
import Swal from 'sweetalert2'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import MySqlImportWizard from './components/MySqlImportWizard'
|
import MariaDbImportWizard from './components/MariaDbImportWizard'
|
||||||
import MySqlExportWizard from './components/MySqlExportWizard'
|
import MariaDbExportWizard from './components/MariaDbExportWizard'
|
||||||
import NginxCodeEditor from './components/NginxCodeEditor'
|
import NginxCodeEditor from './components/NginxCodeEditor'
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
@@ -90,8 +90,8 @@ interface ServiceStatus {
|
|||||||
interface AppSettings {
|
interface AppSettings {
|
||||||
nginxPort: string
|
nginxPort: string
|
||||||
phpPort: string
|
phpPort: string
|
||||||
mysqlPort: string
|
mariaDbPort: string
|
||||||
mysqlPorts?: Record<string, string>
|
mariaDbPorts?: Record<string, string>
|
||||||
language: string
|
language: string
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,22 +100,22 @@ function App(): JSX.Element {
|
|||||||
const [status, setStatus] = useState<ServiceStatus>({
|
const [status, setStatus] = useState<ServiceStatus>({
|
||||||
nginx: 'stopped',
|
nginx: 'stopped',
|
||||||
php: 'stopped',
|
php: 'stopped',
|
||||||
mysql: 'stopped'
|
mariadb: 'stopped'
|
||||||
})
|
})
|
||||||
const [activeTab, setActiveTab] = useState('dashboard')
|
const [activeTab, setActiveTab] = useState('dashboard')
|
||||||
const [settingsTab, setSettingsTab] = useState(0)
|
const [settingsTab, setSettingsTab] = useState(0)
|
||||||
const [dashboardTab, setDashboardTab] = useState(0)
|
const [dashboardTab, setDashboardTab] = useState(0)
|
||||||
const [projects, setProjects] = useState<any[]>([])
|
const [projects, setProjects] = useState<any[]>([])
|
||||||
const [phpVersions, setPhpVersions] = useState<any[]>([])
|
const [phpVersions, setPhpVersions] = useState<any[]>([])
|
||||||
const [mySqlVersions, setMySqlVersions] = useState<any[]>([])
|
const [mariaDbVersions, setMariaDbVersions] = useState<any[]>([])
|
||||||
const [nginxVersions, setNginxVersions] = useState<any[]>([])
|
const [nginxVersions, setNginxVersions] = useState<any[]>([])
|
||||||
const [isProjectDialogOpen, setIsProjectDialogOpen] = useState(false)
|
const [isProjectDialogOpen, setIsProjectDialogOpen] = useState(false)
|
||||||
const slugify = (text: string) => text.toLowerCase().trim().replace(/[^\w\s-]/g, '').replace(/[\s_-]+/g, '-').replace(/^-+|-+$/g, '');
|
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>({
|
const [settings, setSettings] = useState<AppSettings>({
|
||||||
nginxPort: '80',
|
nginxPort: '80',
|
||||||
phpPort: '9001',
|
phpPort: '9001',
|
||||||
mysqlPort: '3306',
|
mariaDbPort: '3306',
|
||||||
language: 'tr'
|
language: 'tr'
|
||||||
})
|
})
|
||||||
const [notification, setNotification] = useState<{ open: boolean, message: string, severity: 'success' | 'error' }>({
|
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 [pmaInstalled, setPmaInstalled] = useState(false)
|
||||||
const [pmaLoading, setPmaLoading] = useState(false)
|
const [pmaLoading, setPmaLoading] = useState(false)
|
||||||
const [pmaProgress, setPmaProgress] = useState(0)
|
const [pmaProgress, setPmaProgress] = useState(0)
|
||||||
const [isMySqlWizardOpen, setIsMySqlWizardOpen] = useState(false)
|
const [isMariaDbWizardOpen, setIsMariaDbWizardOpen] = useState(false)
|
||||||
const [isMySqlExportWizardOpen, setIsMySqlExportWizardOpen] = useState(false)
|
const [isMariaDbExportWizardOpen, setIsMariaDbExportWizardOpen] = useState(false)
|
||||||
const [isNginxConfigOpen, setIsNginxConfigOpen] = useState(false)
|
const [isNginxConfigOpen, setIsNginxConfigOpen] = useState(false)
|
||||||
const [nginxConfig, setNginxConfig] = useState('')
|
const [nginxConfig, setNginxConfig] = useState('')
|
||||||
const [isProjectNginxConfigOpen, setIsProjectNginxConfigOpen] = useState(false)
|
const [isProjectNginxConfigOpen, setIsProjectNginxConfigOpen] = useState(false)
|
||||||
@@ -178,10 +178,10 @@ function App(): JSX.Element {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const fetchMySqlVersions = async () => {
|
const fetchMariaDbVersions = async () => {
|
||||||
if (window.api && window.api.getMySqlVersions) {
|
if (window.api && window.api.getMariaDbVersions) {
|
||||||
const list = await window.api.getMySqlVersions()
|
const list = await window.api.getMariaDbVersions()
|
||||||
setMySqlVersions(list || [])
|
setMariaDbVersions(list || [])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -207,15 +207,15 @@ function App(): JSX.Element {
|
|||||||
const installedPhp = phpVersions.find(v => v.status === 'installed')
|
const installedPhp = phpVersions.find(v => v.status === 'installed')
|
||||||
if (installedPhp) updates.phpVersion = installedPhp.version
|
if (installedPhp) updates.phpVersion = installedPhp.version
|
||||||
}
|
}
|
||||||
if (!newProject.mySqlVersion && mySqlVersions.length > 0) {
|
if (!newProject.mariaDbVersion && mariaDbVersions.length > 0) {
|
||||||
const installedMysql = mySqlVersions.find(v => v.status === 'installed')
|
const installedMariaDb = mariaDbVersions.find(v => v.status === 'installed')
|
||||||
if (installedMysql) updates.mySqlVersion = installedMysql.version
|
if (installedMariaDb) updates.mariaDbVersion = installedMariaDb.version
|
||||||
}
|
}
|
||||||
if (Object.keys(updates).length > 0) {
|
if (Object.keys(updates).length > 0) {
|
||||||
setNewProject(prev => ({ ...prev, ...updates }))
|
setNewProject(prev => ({ ...prev, ...updates }))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [isProjectDialogOpen, phpVersions, mySqlVersions])
|
}, [isProjectDialogOpen, phpVersions, mariaDbVersions])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!window.api) return
|
if (!window.api) return
|
||||||
@@ -230,7 +230,7 @@ function App(): JSX.Element {
|
|||||||
fetchStatus()
|
fetchStatus()
|
||||||
fetchProjects()
|
fetchProjects()
|
||||||
fetchPhpVersions()
|
fetchPhpVersions()
|
||||||
fetchMySqlVersions()
|
fetchMariaDbVersions()
|
||||||
fetchNginxVersions()
|
fetchNginxVersions()
|
||||||
fetchSettings()
|
fetchSettings()
|
||||||
fetchPmaStatus()
|
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))
|
setPhpVersions(prev => prev.map(v => v.id === id ? { ...v, progress, status: progress === 100 ? 'installed' : 'downloading' } : v))
|
||||||
}) : null
|
}) : null
|
||||||
|
|
||||||
const removeMySqlListener = window.api.onMySqlProgress ? window.api.onMySqlProgress((data) => {
|
const removeMariaDbListener = window.api.onMariaDbProgress ? window.api.onMariaDbProgress((data) => {
|
||||||
if (!data) return
|
if (!data) return
|
||||||
const { id, progress } = data
|
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
|
}) : null
|
||||||
|
|
||||||
const removeNginxListener = window.api.onNginxProgress ? window.api.onNginxProgress((data) => {
|
const removeNginxListener = window.api.onNginxProgress ? window.api.onNginxProgress((data) => {
|
||||||
@@ -261,7 +261,7 @@ function App(): JSX.Element {
|
|||||||
fetchStatus()
|
fetchStatus()
|
||||||
// fetchProjects() // Reduced frequency for these
|
// fetchProjects() // Reduced frequency for these
|
||||||
// fetchPhpVersions()
|
// fetchPhpVersions()
|
||||||
// fetchMySqlVersions()
|
// fetchMariaDbVersions()
|
||||||
// fetchNginxVersions()
|
// fetchNginxVersions()
|
||||||
}, 3000)
|
}, 3000)
|
||||||
|
|
||||||
@@ -276,7 +276,7 @@ function App(): JSX.Element {
|
|||||||
clearInterval(interval)
|
clearInterval(interval)
|
||||||
if (logInterval) clearInterval(logInterval)
|
if (logInterval) clearInterval(logInterval)
|
||||||
if (typeof removePhpListener === 'function') removePhpListener()
|
if (typeof removePhpListener === 'function') removePhpListener()
|
||||||
if (typeof removeMySqlListener === 'function') removeMySqlListener()
|
if (typeof removeMariaDbListener === 'function') removeMariaDbListener()
|
||||||
if (typeof removeNginxListener === 'function') removeNginxListener()
|
if (typeof removeNginxListener === 'function') removeNginxListener()
|
||||||
if (typeof removePmaListener === 'function') removePmaListener()
|
if (typeof removePmaListener === 'function') removePmaListener()
|
||||||
}
|
}
|
||||||
@@ -396,15 +396,15 @@ function App(): JSX.Element {
|
|||||||
if ((status[s] || 'stopped') === 'stopped') setStatus(prev => ({ ...prev, [s]: 'starting' }))
|
if ((status[s] || 'stopped') === 'stopped') setStatus(prev => ({ ...prev, [s]: 'starting' }))
|
||||||
}
|
}
|
||||||
|
|
||||||
const mysqlToStart = mySqlVersions.filter(v => v.status === 'installed').map(v => `mysql:${v.version}`)
|
const mariaDbToStart = mariaDbVersions.filter(v => v.status === 'installed').map(v => `mariadb:${v.version}`)
|
||||||
for (const s of mysqlToStart) {
|
for (const s of mariaDbToStart) {
|
||||||
if ((status[s] || 'stopped') === 'stopped') setStatus(prev => ({ ...prev, [s]: 'starting' }))
|
if ((status[s] || 'stopped') === 'stopped') setStatus(prev => ({ ...prev, [s]: 'starting' }))
|
||||||
}
|
}
|
||||||
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
...criticalServices.filter(s => status[s] !== 'running').map(s => window.api?.startService(s)),
|
...criticalServices.filter(s => status[s] !== 'running').map(s => window.api?.startService(s)),
|
||||||
...phpToStart.filter(s => (status[s] || 'stopped') !== 'running').map(s => window.api?.startService(s)),
|
...phpToStart.filter(s => (status[s] || 'stopped') !== 'running').map(s => window.api?.startService(s)),
|
||||||
...mysqlToStart.filter(s => (status[s] || 'stopped') !== 'running').map(s => window.api?.startService(s))
|
...mariaDbToStart.filter(s => (status[s] || 'stopped') !== 'running').map(s => window.api?.startService(s))
|
||||||
])
|
])
|
||||||
|
|
||||||
fetchStatus()
|
fetchStatus()
|
||||||
@@ -415,13 +415,13 @@ function App(): JSX.Element {
|
|||||||
if (!window.api) return
|
if (!window.api) return
|
||||||
const criticalServices = ['nginx']
|
const criticalServices = ['nginx']
|
||||||
const phpToStop = Object.keys(status).filter(s => s.startsWith('php:') && (status[s] === 'running' || status[s] === 'starting'))
|
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([
|
await Promise.all([
|
||||||
...criticalServices.filter(s => status[s] === 'running' || status[s] === 'starting').map(s => window.api?.stopService(s)),
|
...criticalServices.filter(s => status[s] === 'running' || status[s] === 'starting').map(s => window.api?.stopService(s)),
|
||||||
...phpToStop.map(s => window.api?.stopService(s)),
|
...phpToStop.map(s => window.api?.stopService(s)),
|
||||||
...mysqlToStop.map(s => window.api?.stopService(s)),
|
...mariaDbToStop.map(s => window.api?.stopService(s)),
|
||||||
window.api?.stopService('mysql') // Also stop legacy if any
|
window.api?.stopService('mariadb') // Also stop default
|
||||||
])
|
])
|
||||||
|
|
||||||
fetchStatus()
|
fetchStatus()
|
||||||
@@ -444,7 +444,7 @@ function App(): JSX.Element {
|
|||||||
await window.api.addProject(newProject)
|
await window.api.addProject(newProject)
|
||||||
}
|
}
|
||||||
setIsProjectDialogOpen(false)
|
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' })
|
setNotification({ open: true, message: newProject.id ? 'Proje güncellendi.' : 'Proje başarıyla eklendi.', severity: 'success' })
|
||||||
fetchProjects()
|
fetchProjects()
|
||||||
}
|
}
|
||||||
@@ -559,15 +559,15 @@ function App(): JSX.Element {
|
|||||||
fetchPhpVersions()
|
fetchPhpVersions()
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleDownloadMySql = async (id: string) => {
|
const handleDownloadMariaDb = async (id: string) => {
|
||||||
if (!window.api) return
|
if (!window.api) return
|
||||||
const result = await window.api.downloadMySqlVersion(id)
|
const result = await window.api.downloadMariaDbVersion(id)
|
||||||
setNotification({
|
setNotification({
|
||||||
open: true,
|
open: true,
|
||||||
message: result.message,
|
message: result.message,
|
||||||
severity: result.success ? 'success' : 'error'
|
severity: result.success ? 'success' : 'error'
|
||||||
})
|
})
|
||||||
fetchMySqlVersions()
|
fetchMariaDbVersions()
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleDownloadNginx = async (id: string) => {
|
const handleDownloadNginx = async (id: string) => {
|
||||||
@@ -699,7 +699,7 @@ function App(): JSX.Element {
|
|||||||
<Box sx={{ borderBottom: 1, borderColor: 'divider', mb: 3 }}>
|
<Box sx={{ borderBottom: 1, borderColor: 'divider', mb: 3 }}>
|
||||||
<Tabs value={settingsTab} onChange={(_e, v) => setSettingsTab(v)} textColor="primary" indicatorColor="primary" variant="scrollable" scrollButtons="auto">
|
<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="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="Nginx" icon={<WebIcon />} iconPosition="start" />
|
||||||
<Tab label="Sunucu Ayarları" icon={<ServerIcon />} iconPosition="start" />
|
<Tab label="Sunucu Ayarları" icon={<ServerIcon />} iconPosition="start" />
|
||||||
</Tabs>
|
</Tabs>
|
||||||
@@ -749,14 +749,14 @@ function App(): JSX.Element {
|
|||||||
{settingsTab === 1 && (
|
{settingsTab === 1 && (
|
||||||
<Paper sx={{ bgcolor: 'rgba(255, 255, 255, 0.03)', borderRadius: 2 }}>
|
<Paper sx={{ bgcolor: 'rgba(255, 255, 255, 0.03)', borderRadius: 2 }}>
|
||||||
<Box sx={{ p: 2, borderBottom: '1px solid rgba(255, 255, 255, 0.05)' }}>
|
<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="subtitle1" sx={{ fontWeight: 'bold' }}>MariaDB Sürümleri</Typography>
|
||||||
<Typography variant="body2" color="text.secondary">Farklı MySQL sürümlerini indirebilir ve sunucunuzda kullanabilirsiniz.</Typography>
|
<Typography variant="body2" color="text.secondary">Farklı MariaDB sürümlerini indirebilir ve sunucunuzda kullanabilirsiniz.</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
<List sx={{ maxHeight: 500, overflow: 'auto' }}>
|
<List sx={{ maxHeight: 500, overflow: 'auto' }}>
|
||||||
{mySqlVersions.length === 0 && (
|
{mariaDbVersions.length === 0 && (
|
||||||
<ListItem><ListItemText primary="Yükleniyor veya liste boş..." /></ListItem>
|
<ListItem><ListItemText primary="Yükleniyor veya liste boş..." /></ListItem>
|
||||||
)}
|
)}
|
||||||
{mySqlVersions.map((v) => (
|
{mariaDbVersions.map((v) => (
|
||||||
<ListItem key={v.id} sx={{ py: 2 }}>
|
<ListItem key={v.id} sx={{ py: 2 }}>
|
||||||
<ListItemIcon>
|
<ListItemIcon>
|
||||||
{v.status === 'installed' ? <InstalledIcon color="success" /> : <AvailableIcon color="action" />}
|
{v.status === 'installed' ? <InstalledIcon color="success" /> : <AvailableIcon color="action" />}
|
||||||
@@ -764,7 +764,7 @@ function App(): JSX.Element {
|
|||||||
<ListItemText
|
<ListItemText
|
||||||
primary={
|
primary={
|
||||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
||||||
{`MySQL ${v.version} (${v.cycle})`}
|
{`MariaDB ${v.version} (${v.cycle})`}
|
||||||
{v.description && (
|
{v.description && (
|
||||||
<Chip label={v.description} size="small" variant="outlined" sx={{ fontSize: '0.7rem', height: 20 }} />
|
<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' && (
|
{v.status === 'installed' && (
|
||||||
<TextField
|
<TextField
|
||||||
label="Port"
|
label="Port"
|
||||||
value={settings.mysqlPorts?.[v.id] || ''}
|
value={settings.mariaDbPorts?.[v.id] || ''}
|
||||||
placeholder={v.port}
|
placeholder={v.port}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
const newPorts = { ...(settings.mysqlPorts || {}), [v.id]: e.target.value }
|
const newPorts = { ...(settings.mariaDbPorts || {}), [v.id]: e.target.value }
|
||||||
setSettings({ ...settings, mysqlPorts: newPorts })
|
setSettings({ ...settings, mariaDbPorts: newPorts })
|
||||||
}}
|
}}
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
size="small"
|
size="small"
|
||||||
@@ -790,7 +790,7 @@ function App(): JSX.Element {
|
|||||||
)}
|
)}
|
||||||
<Box sx={{ minWidth: 120, textAlign: 'right' }}>
|
<Box sx={{ minWidth: 120, textAlign: 'right' }}>
|
||||||
{v.status === 'available' && (
|
{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
|
İNDİR
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
@@ -870,9 +870,9 @@ function App(): JSX.Element {
|
|||||||
helperText="Varsayılan: 9001"
|
helperText="Varsayılan: 9001"
|
||||||
/>
|
/>
|
||||||
<TextField
|
<TextField
|
||||||
label="MySQL Portu"
|
label="MariaDB Portu"
|
||||||
value={settings.mysqlPort}
|
value={settings.mariaDbPort}
|
||||||
onChange={(e) => setSettings({ ...settings, mysqlPort: e.target.value })}
|
onChange={(e) => setSettings({ ...settings, mariaDbPort: e.target.value })}
|
||||||
variant="filled"
|
variant="filled"
|
||||||
size="small"
|
size="small"
|
||||||
helperText="Varsayılan: 3306"
|
helperText="Varsayılan: 3306"
|
||||||
@@ -898,7 +898,7 @@ function App(): JSX.Element {
|
|||||||
<Tab label={t('common.all')} />
|
<Tab label={t('common.all')} />
|
||||||
<Tab label="Nginx" />
|
<Tab label="Nginx" />
|
||||||
<Tab label="PHP" />
|
<Tab label="PHP" />
|
||||||
<Tab label="MySQL" />
|
<Tab label="MariaDB" />
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
@@ -988,8 +988,8 @@ function App(): JSX.Element {
|
|||||||
</Grid>
|
</Grid>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
{/* MySQL Boxes */}
|
{/* MariaDB Boxes */}
|
||||||
{(dashboardTab === 0 || dashboardTab === 3) && mySqlVersions.filter(v => v.status === 'installed').map(v => (
|
{(dashboardTab === 0 || dashboardTab === 3) && mariaDbVersions.filter(v => v.status === 'installed').map(v => (
|
||||||
<Grid item xs={12} sm={6} md={4} key={v.id}>
|
<Grid item xs={12} sm={6} md={4} key={v.id}>
|
||||||
<Paper elevation={4} sx={{
|
<Paper elevation={4} sx={{
|
||||||
p: 3, borderRadius: 3,
|
p: 3, borderRadius: 3,
|
||||||
@@ -1001,23 +1001,23 @@ function App(): JSX.Element {
|
|||||||
}}>
|
}}>
|
||||||
<Box sx={{ display: 'flex', justifyContent: 'space-between', mb: 2 }}>
|
<Box sx={{ display: 'flex', justifyContent: 'space-between', mb: 2 }}>
|
||||||
<DbIcon color="primary" sx={{ fontSize: 40 }} />
|
<DbIcon color="primary" sx={{ fontSize: 40 }} />
|
||||||
{getStatusChip(status[`mysql:${v.version}`] || 'stopped')}
|
{getStatusChip(status[`mariadb:${v.version}`] || 'stopped')}
|
||||||
</Box>
|
</Box>
|
||||||
<Typography variant="h6" sx={{ fontWeight: 'bold' }}>MySQL {v.version}</Typography>
|
<Typography variant="h6" sx={{ fontWeight: 'bold' }}>MariaDB {v.version}</Typography>
|
||||||
<Typography variant="body2" color="text.secondary" sx={{ mb: 2 }}>Port: {settings.mysqlPorts?.[v.id] || v.port}</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', alignItems: 'center', justifyContent: 'space-between' }}>
|
||||||
<Box sx={{ display: 'flex', gap: 1 }}>
|
<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" />
|
<TerminalIcon fontSize="small" />
|
||||||
</IconButton>
|
</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" />
|
<ResetIcon fontSize="small" />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</Box>
|
</Box>
|
||||||
<Switch
|
<Switch
|
||||||
checked={(status[`mysql:${v.version}`] || 'stopped') === 'running' || status[`mysql:${v.version}`] === 'starting'}
|
checked={(status[`mariadb:${v.version}`] || 'stopped') === 'running' || status[`mariadb:${v.version}`] === 'starting'}
|
||||||
onChange={() => handleToggleService(`mysql:${v.version}`)}
|
onChange={() => handleToggleService(`mariadb:${v.version}`)}
|
||||||
disabled={status[`mysql:${v.version}`] === 'starting'}
|
disabled={status[`mariadb:${v.version}`] === 'starting'}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
</Paper>
|
</Paper>
|
||||||
@@ -1093,7 +1093,7 @@ function App(): JSX.Element {
|
|||||||
<DbIcon color="primary" sx={{ fontSize: 32 }} />
|
<DbIcon color="primary" sx={{ fontSize: 32 }} />
|
||||||
</Box>
|
</Box>
|
||||||
<Box sx={{ flexGrow: 1 }}>
|
<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">
|
<Typography variant="body2" color="text.secondary">
|
||||||
Seçtiğiniz bir .sql veya arşiv dosyasını hızlıca veritabanına yükleyin.
|
Seçtiğiniz bir .sql veya arşiv dosyasını hızlıca veritabanına yükleyin.
|
||||||
</Typography>
|
</Typography>
|
||||||
@@ -1102,7 +1102,7 @@ function App(): JSX.Element {
|
|||||||
<Button
|
<Button
|
||||||
variant="contained"
|
variant="contained"
|
||||||
size="large"
|
size="large"
|
||||||
onClick={() => setIsMySqlWizardOpen(true)}
|
onClick={() => setIsMariaDbWizardOpen(true)}
|
||||||
startIcon={<DbIcon />}
|
startIcon={<DbIcon />}
|
||||||
sx={{ borderRadius: 2 }}
|
sx={{ borderRadius: 2 }}
|
||||||
>
|
>
|
||||||
@@ -1118,7 +1118,7 @@ function App(): JSX.Element {
|
|||||||
<DbIcon color="secondary" sx={{ fontSize: 32 }} />
|
<DbIcon color="secondary" sx={{ fontSize: 32 }} />
|
||||||
</Box>
|
</Box>
|
||||||
<Box sx={{ flexGrow: 1 }}>
|
<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">
|
<Typography variant="body2" color="text.secondary">
|
||||||
Veritabanlarını .sql veya .sql.gz olarak güvenle yedekleyin.
|
Veritabanlarını .sql veya .sql.gz olarak güvenle yedekleyin.
|
||||||
</Typography>
|
</Typography>
|
||||||
@@ -1128,7 +1128,7 @@ function App(): JSX.Element {
|
|||||||
variant="contained"
|
variant="contained"
|
||||||
color="secondary"
|
color="secondary"
|
||||||
size="large"
|
size="large"
|
||||||
onClick={() => setIsMySqlExportWizardOpen(true)}
|
onClick={() => setIsMariaDbExportWizardOpen(true)}
|
||||||
startIcon={<DbIcon />}
|
startIcon={<DbIcon />}
|
||||||
sx={{ borderRadius: 2 }}
|
sx={{ borderRadius: 2 }}
|
||||||
>
|
>
|
||||||
@@ -1388,7 +1388,7 @@ function App(): JSX.Element {
|
|||||||
|
|
||||||
<Dialog open={isProjectDialogOpen} onClose={() => {
|
<Dialog open={isProjectDialogOpen} onClose={() => {
|
||||||
setIsProjectDialogOpen(false)
|
setIsProjectDialogOpen(false)
|
||||||
setNewProject({ name: '', path: '', phpVersion: '', mySqlVersion: '', host: 'localhost' })
|
setNewProject({ name: '', path: '', phpVersion: '', mariaDbVersion: '', host: 'localhost' })
|
||||||
}} fullWidth maxWidth="sm">
|
}} fullWidth maxWidth="sm">
|
||||||
<DialogTitle>{newProject.id ? 'Proje Düzenle' : 'Yeni Proje Ekle'}</DialogTitle>
|
<DialogTitle>{newProject.id ? 'Proje Düzenle' : 'Yeni Proje Ekle'}</DialogTitle>
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
@@ -1442,7 +1442,7 @@ function App(): JSX.Element {
|
|||||||
<DialogActions>
|
<DialogActions>
|
||||||
<Button onClick={() => {
|
<Button onClick={() => {
|
||||||
setIsProjectDialogOpen(false)
|
setIsProjectDialogOpen(false)
|
||||||
setNewProject({ name: '', path: '', phpVersion: '', mySqlVersion: '', host: 'localhost' })
|
setNewProject({ name: '', path: '', phpVersion: '', mariaDbVersion: '', host: 'localhost' })
|
||||||
}}>İptal</Button>
|
}}>İptal</Button>
|
||||||
<Button
|
<Button
|
||||||
onClick={handleAddProject}
|
onClick={handleAddProject}
|
||||||
@@ -1454,16 +1454,16 @@ function App(): JSX.Element {
|
|||||||
</DialogActions>
|
</DialogActions>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|
||||||
<MySqlImportWizard
|
<MariaDbImportWizard
|
||||||
open={isMySqlWizardOpen}
|
open={isMariaDbWizardOpen}
|
||||||
onClose={() => setIsMySqlWizardOpen(false)}
|
onClose={() => setIsMariaDbWizardOpen(false)}
|
||||||
mySqlVersions={mySqlVersions}
|
mariaDbVersions={mariaDbVersions}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<MySqlExportWizard
|
<MariaDbExportWizard
|
||||||
open={isMySqlExportWizardOpen}
|
open={isMariaDbExportWizardOpen}
|
||||||
onClose={() => setIsMySqlExportWizardOpen(false)}
|
onClose={() => setIsMariaDbExportWizardOpen(false)}
|
||||||
mySqlVersions={mySqlVersions}
|
mariaDbVersions={mariaDbVersions}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Dialog
|
<Dialog
|
||||||
|
|||||||
+17
-17
@@ -30,10 +30,10 @@ import {
|
|||||||
} from '@mui/icons-material'
|
} from '@mui/icons-material'
|
||||||
import Swal from 'sweetalert2'
|
import Swal from 'sweetalert2'
|
||||||
|
|
||||||
interface MySqlExportWizardProps {
|
interface MariaDbExportWizardProps {
|
||||||
open: boolean
|
open: boolean
|
||||||
onClose: () => void
|
onClose: () => void
|
||||||
mySqlVersions: any[]
|
mariaDbVersions: any[]
|
||||||
}
|
}
|
||||||
|
|
||||||
const steps = [
|
const steps = [
|
||||||
@@ -43,7 +43,7 @@ const steps = [
|
|||||||
'Özet ve İşlem'
|
'Ö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 [activeStep, setActiveStep] = useState(0)
|
||||||
const [selectedVersion, setSelectedVersion] = useState('')
|
const [selectedVersion, setSelectedVersion] = useState('')
|
||||||
const [databases, setDatabases] = useState<string[]>([])
|
const [databases, setDatabases] = useState<string[]>([])
|
||||||
@@ -63,10 +63,10 @@ const MySqlExportWizard: React.FC<MySqlExportWizardProps> = ({ open, onClose, my
|
|||||||
setSavePath('')
|
setSavePath('')
|
||||||
setLogs([])
|
setLogs([])
|
||||||
setExportResult(null)
|
setExportResult(null)
|
||||||
const installedVersion = mySqlVersions.find(v => v.status === 'installed')
|
const installedVersion = mariaDbVersions.find(v => v.status === 'installed')
|
||||||
if (installedVersion) setSelectedVersion(installedVersion.id)
|
if (installedVersion) setSelectedVersion(installedVersion.id)
|
||||||
}
|
}
|
||||||
}, [open, mySqlVersions])
|
}, [open, mariaDbVersions])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (logEndRef.current) {
|
if (logEndRef.current) {
|
||||||
@@ -79,12 +79,12 @@ const MySqlExportWizard: React.FC<MySqlExportWizardProps> = ({ open, onClose, my
|
|||||||
|
|
||||||
const handleStep1Next = async () => {
|
const handleStep1Next = async () => {
|
||||||
if (!selectedVersion) {
|
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
|
return
|
||||||
}
|
}
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
try {
|
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)
|
setDatabases(dbs)
|
||||||
nextStep()
|
nextStep()
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
@@ -105,12 +105,12 @@ const MySqlExportWizard: React.FC<MySqlExportWizardProps> = ({ open, onClose, my
|
|||||||
setLoading(true)
|
setLoading(true)
|
||||||
setLogs(['[Sistem] Dışa aktarma işlemi başlatılıyor...'])
|
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])
|
setLogs(prev => [...prev, msg])
|
||||||
})
|
})
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const result = await window.api.invoke('mysql:export-wizard:start', {
|
const result = await window.api.invoke('mariadb:export-wizard:start', {
|
||||||
versionId: selectedVersion,
|
versionId: selectedVersion,
|
||||||
dbName: selectedDb,
|
dbName: selectedDb,
|
||||||
savePath,
|
savePath,
|
||||||
@@ -131,17 +131,17 @@ const MySqlExportWizard: React.FC<MySqlExportWizardProps> = ({ open, onClose, my
|
|||||||
case 0:
|
case 0:
|
||||||
return (
|
return (
|
||||||
<Box sx={{ mt: 2 }}>
|
<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 }}>
|
<FormControl fullWidth sx={{ mt: 2 }}>
|
||||||
<InputLabel>MySQL Sunucusu</InputLabel>
|
<InputLabel>MariaDB Sunucusu</InputLabel>
|
||||||
<Select
|
<Select
|
||||||
value={selectedVersion}
|
value={selectedVersion}
|
||||||
label="MySQL Sunucusu"
|
label="MariaDB Sunucusu"
|
||||||
onChange={(e) => setSelectedVersion(e.target.value)}
|
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}>
|
<MenuItem key={v.id} value={v.id}>
|
||||||
MySQL {v.version} (Port: {v.port})
|
MariaDB {v.version} (Port: {v.port})
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
))}
|
))}
|
||||||
</Select>
|
</Select>
|
||||||
@@ -153,7 +153,7 @@ const MySqlExportWizard: React.FC<MySqlExportWizardProps> = ({ open, onClose, my
|
|||||||
value={rootPass}
|
value={rootPass}
|
||||||
type="password"
|
type="password"
|
||||||
onChange={(e) => setRootPass(e.target.value)}
|
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>
|
</Box>
|
||||||
)
|
)
|
||||||
@@ -266,7 +266,7 @@ const MySqlExportWizard: React.FC<MySqlExportWizardProps> = ({ open, onClose, my
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog open={open} onClose={loading ? undefined : onClose} maxWidth="sm" fullWidth>
|
<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>
|
<DialogContent>
|
||||||
<Stepper activeStep={activeStep} orientation="horizontal" sx={{ pt: 2, pb: 4 }}>
|
<Stepper activeStep={activeStep} orientation="horizontal" sx={{ pt: 2, pb: 4 }}>
|
||||||
{steps.map((label) => (
|
{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'
|
} from '@mui/icons-material'
|
||||||
import Swal from 'sweetalert2'
|
import Swal from 'sweetalert2'
|
||||||
|
|
||||||
interface MySqlImportWizardProps {
|
interface MariaDbImportWizardProps {
|
||||||
open: boolean
|
open: boolean
|
||||||
onClose: () => void
|
onClose: () => void
|
||||||
mySqlVersions: any[]
|
mariaDbVersions: any[]
|
||||||
}
|
}
|
||||||
|
|
||||||
const steps = [
|
const steps = [
|
||||||
@@ -43,7 +43,7 @@ const steps = [
|
|||||||
'Özet ve İşlem'
|
'Ö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 [activeStep, setActiveStep] = useState(0)
|
||||||
const [filePath, setFilePath] = useState('')
|
const [filePath, setFilePath] = useState('')
|
||||||
const [selectedVersion, setSelectedVersion] = useState('')
|
const [selectedVersion, setSelectedVersion] = useState('')
|
||||||
@@ -64,7 +64,7 @@ const MySqlImportWizard: React.FC<MySqlImportWizardProps> = ({ open, onClose, my
|
|||||||
setFilePath('')
|
setFilePath('')
|
||||||
setLogs([])
|
setLogs([])
|
||||||
setImportResult(null)
|
setImportResult(null)
|
||||||
const installedVersion = mySqlVersions.find(v => v.status === 'installed')
|
const installedVersion = mariaDbVersions.find(v => v.status === 'installed')
|
||||||
if (installedVersion) setSelectedVersion(installedVersion.id)
|
if (installedVersion) setSelectedVersion(installedVersion.id)
|
||||||
|
|
||||||
// Generate random credentials
|
// Generate random credentials
|
||||||
@@ -76,7 +76,7 @@ const MySqlImportWizard: React.FC<MySqlImportWizardProps> = ({ open, onClose, my
|
|||||||
rootPass: ''
|
rootPass: ''
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}, [open, mySqlVersions])
|
}, [open, mariaDbVersions])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (logEndRef.current) {
|
if (logEndRef.current) {
|
||||||
@@ -103,7 +103,7 @@ const MySqlImportWizard: React.FC<MySqlImportWizardProps> = ({ open, onClose, my
|
|||||||
|
|
||||||
const handleStep2Next = () => {
|
const handleStep2Next = () => {
|
||||||
if (!selectedVersion) {
|
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
|
return
|
||||||
}
|
}
|
||||||
nextStep()
|
nextStep()
|
||||||
@@ -117,7 +117,7 @@ const MySqlImportWizard: React.FC<MySqlImportWizardProps> = ({ open, onClose, my
|
|||||||
|
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
try {
|
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) {
|
if (exists) {
|
||||||
const result = await Swal.fire({
|
const result = await Swal.fire({
|
||||||
title: 'Veritabanı Mevcut!',
|
title: 'Veritabanı Mevcut!',
|
||||||
@@ -142,19 +142,19 @@ const MySqlImportWizard: React.FC<MySqlImportWizardProps> = ({ open, onClose, my
|
|||||||
setLoading(true)
|
setLoading(true)
|
||||||
setLogs(['[Sistem] İşlem başlatılıyor...'])
|
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])
|
setLogs(prev => [...prev, msg])
|
||||||
})
|
})
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const result = await window.api.startMySqlImport({
|
const result = await window.api.startMariaDbImport({
|
||||||
versionId: selectedVersion,
|
versionId: selectedVersion,
|
||||||
filePath,
|
filePath,
|
||||||
dbName: dbConfig.dbName,
|
dbName: dbConfig.dbName,
|
||||||
user: dbConfig.user,
|
user: dbConfig.user,
|
||||||
pass: dbConfig.pass,
|
pass: dbConfig.pass,
|
||||||
rootPass: dbConfig.rootPass,
|
rootPass: dbConfig.rootPass,
|
||||||
overwrite: true // Logic handled by checkMySqlDb before
|
overwrite: true // Logic handled by checkMariaDbDb before
|
||||||
})
|
})
|
||||||
setImportResult(result)
|
setImportResult(result)
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
@@ -193,17 +193,17 @@ const MySqlImportWizard: React.FC<MySqlImportWizardProps> = ({ open, onClose, my
|
|||||||
case 1:
|
case 1:
|
||||||
return (
|
return (
|
||||||
<Box sx={{ mt: 2 }}>
|
<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 }}>
|
<FormControl fullWidth sx={{ mt: 2 }}>
|
||||||
<InputLabel>MySQL Sunucusu</InputLabel>
|
<InputLabel>MariaDB Sunucusu</InputLabel>
|
||||||
<Select
|
<Select
|
||||||
value={selectedVersion}
|
value={selectedVersion}
|
||||||
label="MySQL Sunucusu"
|
label="MariaDB Sunucusu"
|
||||||
onChange={(e) => setSelectedVersion(e.target.value)}
|
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}>
|
<MenuItem key={v.id} value={v.id}>
|
||||||
MySQL {v.version} (Port: {v.port})
|
MariaDB {v.version} (Port: {v.port})
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
))}
|
))}
|
||||||
</Select>
|
</Select>
|
||||||
@@ -215,7 +215,7 @@ const MySqlImportWizard: React.FC<MySqlImportWizardProps> = ({ open, onClose, my
|
|||||||
value={dbConfig.rootPass}
|
value={dbConfig.rootPass}
|
||||||
type="password"
|
type="password"
|
||||||
onChange={(e) => setDbConfig({ ...dbConfig, rootPass: e.target.value })}
|
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>
|
</Box>
|
||||||
)
|
)
|
||||||
@@ -315,7 +315,7 @@ const MySqlImportWizard: React.FC<MySqlImportWizardProps> = ({ open, onClose, my
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog open={open} onClose={loading ? undefined : onClose} maxWidth="sm" fullWidth>
|
<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>
|
<DialogContent>
|
||||||
<Stepper activeStep={activeStep} orientation="horizontal" sx={{ pt: 2, pb: 4 }}>
|
<Stepper activeStep={activeStep} orientation="horizontal" sx={{ pt: 2, pb: 4 }}>
|
||||||
{steps.map((label) => (
|
{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