1146 lines
52 KiB
TypeScript
1146 lines
52 KiB
TypeScript
import { ipcMain, app, dialog, shell } from 'electron'
|
||
import fs from 'fs'
|
||
import { processManager } from '../services/ProcessManager'
|
||
import { configService } from '../services/ConfigService'
|
||
import { downloadService } from '../services/DownloadService'
|
||
import { projectService } from '../services/ProjectService'
|
||
import { phpManagerService } from '../services/PhpManagerService'
|
||
import { mariaDbManagerService } from '../services/MariaDbManagerService'
|
||
import { nginxManagerService } from '../services/NginxManagerService'
|
||
import { apacheManagerService } from '../services/ApacheManagerService'
|
||
import { phpMyAdminService } from '../services/PhpMyAdminService'
|
||
import { portMappingService } from '../services/PortMappingService'
|
||
import path from 'path'
|
||
import { mariaDbOperationService } from '../services/MariaDbOperationService'
|
||
import { cliAliasService } from '../services/CliAliasService'
|
||
import { cliBinService } from '../services/CliBinService'
|
||
|
||
function findExecutable(dir: string, name: string): string | null {
|
||
if (!fs.existsSync(dir)) return null
|
||
const files = fs.readdirSync(dir)
|
||
for (const file of files) {
|
||
const fullPath = path.join(dir, file)
|
||
const stat = fs.statSync(fullPath)
|
||
if (stat.isDirectory()) {
|
||
const found = findExecutable(fullPath, name)
|
||
if (found) return found
|
||
} else if (file.toLowerCase() === name.toLowerCase()) {
|
||
return fullPath
|
||
}
|
||
}
|
||
return null
|
||
}
|
||
|
||
async function rebuildNginxConfig(forceProjects: boolean = false): Promise<{ binPath: string; nginxPrefix: string; configPath: string }> {
|
||
const settings = configService.getSettings()
|
||
const binDir = path.join(configService.getBasePath(), 'bin')
|
||
const nginxDir = path.join(binDir, 'nginx')
|
||
const nginxVersions = fs.readdirSync(nginxDir).filter(f => fs.statSync(path.join(nginxDir, f)).isDirectory())
|
||
const activeVersion = nginxVersions[0]
|
||
if (!activeVersion) throw new Error('Nginx sürümü bulunamadı.')
|
||
|
||
const nginxPrefix = path.join(nginxDir, activeVersion)
|
||
const isWindows = process.platform === 'win32';
|
||
const binPath = path.join(nginxPrefix, isWindows ? 'nginx.exe' : 'nginx')
|
||
const confDir = path.join(nginxPrefix, 'conf')
|
||
const logDir = path.join(configService.getBasePath(), 'logs')
|
||
const rootPath = path.join(configService.getBasePath(), 'www')
|
||
if (!fs.existsSync(rootPath)) fs.mkdirSync(rootPath, { recursive: true })
|
||
|
||
// Ensure directories exist
|
||
const projectsConfDir = path.join(configService.getBasePath(), 'generated_configs', 'projects')
|
||
if (!fs.existsSync(projectsConfDir)) fs.mkdirSync(projectsConfDir, { recursive: true })
|
||
|
||
const tempDirs = ['temp', 'temp/client_body_temp', 'temp/proxy_temp', 'temp/fastcgi_temp', 'temp/uwsgi_temp', 'temp/scgi_temp', 'logs']
|
||
for (const dir of tempDirs) {
|
||
const fullDir = path.join(nginxPrefix, dir)
|
||
if (!fs.existsSync(fullDir)) fs.mkdirSync(fullDir, { recursive: true })
|
||
}
|
||
|
||
// Generate project configs
|
||
const allProjects = await projectService.getProjects()
|
||
const projects = allProjects.filter(p => !p.serverType || p.serverType === 'nginx')
|
||
for (const p of projects) {
|
||
const projectConfPath = path.join(projectsConfDir, `${p.host}.conf`)
|
||
let shouldGenerate = forceProjects || !fs.existsSync(projectConfPath)
|
||
|
||
// If file exists, check if it was manually edited
|
||
if (!shouldGenerate && fs.existsSync(projectConfPath)) {
|
||
const currentContent = fs.readFileSync(projectConfPath, 'utf8')
|
||
if (!currentContent.includes('# MANUAL_EDIT')) {
|
||
// Overwrite old, broken configs that don't have the marker
|
||
shouldGenerate = true
|
||
}
|
||
}
|
||
|
||
if (shouldGenerate) {
|
||
const phpPort = portMappingService.getPhpPort(p.phpVersion)
|
||
// Ensure path ends with a slash for alias compatibility
|
||
let normalizedPath = p.path.replace(/\\/g, '/')
|
||
if (!normalizedPath.endsWith('/')) {
|
||
normalizedPath += '/'
|
||
}
|
||
|
||
await configService.generateConfig('nginx_project.conf.template', `projects/${p.host}.conf`, {
|
||
HOST: p.host,
|
||
PATH: normalizedPath,
|
||
PHP_PORT: phpPort.toString(),
|
||
CONF_DIR: confDir.replace(/\\/g, '/')
|
||
})
|
||
}
|
||
}
|
||
|
||
const projectsInclusion = projects.length > 0
|
||
? `include "${projectsConfDir.replace(/\\/g, '/')}/*.conf";`
|
||
: ''
|
||
|
||
const configPath = await configService.generateConfig('nginx.conf.template', 'nginx.conf', {
|
||
PORT: settings.nginxPort,
|
||
LISTEN_ADDRESS: settings.allowRemoteAccess ? '0.0.0.0' : '127.0.0.1',
|
||
PHP_PORT: settings.phpPort,
|
||
ROOT: rootPath,
|
||
LOG_DIR: logDir.replace(/\\/g, '/'),
|
||
CONF_DIR: confDir.replace(/\\/g, '/'),
|
||
PHPMYADMIN_DIR: phpMyAdminService.getPath().replace(/\\/g, '/'),
|
||
PROJECTS: projectsInclusion
|
||
})
|
||
|
||
return { binPath, nginxPrefix, configPath }
|
||
}
|
||
|
||
async function restartNginx(): Promise<{ success: boolean; message: string }> {
|
||
try {
|
||
const { binPath, nginxPrefix, configPath } = await rebuildNginxConfig(false)
|
||
|
||
// Force kill Nginx to ensure it picks up new config on Windows
|
||
await processManager.forceKillAll('nginx')
|
||
await processManager.stopService('nginx')
|
||
// Give OS time to release the port
|
||
await new Promise(resolve => setTimeout(resolve, 1000))
|
||
|
||
// Start service
|
||
const success = await processManager.startService('nginx', binPath, ['-p', nginxPrefix, '-c', configPath], false)
|
||
|
||
if (success) {
|
||
return { success: true, message: 'Nginx restart successful' }
|
||
} else {
|
||
const errorLogs = await processManager.getLastErrorLogs('nginx')
|
||
return { success: false, message: `Nginx restart failed:\n${errorLogs.join('\n')}` }
|
||
}
|
||
} catch (error: any) {
|
||
return { success: false, message: `Restart hatası: ${error.message}` }
|
||
}
|
||
}
|
||
async function reloadNginx(): Promise<{ success: boolean; message: string }> {
|
||
try {
|
||
const { binPath, nginxPrefix, configPath } = await rebuildNginxConfig(false)
|
||
const result = await processManager.runOnce(binPath, ['-p', nginxPrefix, '-c', configPath, '-s', 'reload'], 'nginx')
|
||
if (result.success) {
|
||
console.log('Nginx reload successful')
|
||
return { success: true, message: 'Nginx reloaded' }
|
||
} else {
|
||
// Fallback to full restart if reload fails (common on Windows)
|
||
return await restartNginx()
|
||
}
|
||
} catch (error: any) {
|
||
return { success: false, message: `Reload hatası: ${error.message}` }
|
||
}
|
||
}
|
||
async function rebuildApacheConfig(forceProjects: boolean = false): Promise<{ binPath: string; apacheDir: string; configPath: string }> {
|
||
const settings = configService.getSettings()
|
||
const binDir = path.join(configService.getBasePath(), 'bin')
|
||
const apacheDir = path.join(binDir, 'apache')
|
||
|
||
const isWindows = process.platform === 'win32';
|
||
const binPath = findExecutable(apacheDir, isWindows ? 'httpd.exe' : 'httpd')
|
||
if (!binPath) throw new Error('Apache executable bulunamadı.')
|
||
|
||
const apacheRoot = path.dirname(path.dirname(binPath)) // bin'in üstü
|
||
const logDir = path.join(configService.getBasePath(), 'logs')
|
||
const rootPath = path.join(configService.getBasePath(), 'www')
|
||
if (!fs.existsSync(rootPath)) fs.mkdirSync(rootPath, { recursive: true })
|
||
|
||
const configPath = path.join(configService.getBasePath(), 'generated_configs', 'httpd.conf')
|
||
|
||
// Check if manual edit exists for global config
|
||
let skipGlobal = false
|
||
if (fs.existsSync(configPath) && !forceProjects) {
|
||
const content = fs.readFileSync(configPath, 'utf8')
|
||
if (content.includes('# MANUAL_EDIT')) {
|
||
console.log('Skipping Apache global config generation (Manual Edit detected)')
|
||
skipGlobal = true
|
||
}
|
||
}
|
||
|
||
// Ensure directories exist
|
||
const projectsConfDir = path.join(configService.getBasePath(), 'generated_configs', 'projects_apache')
|
||
if (!fs.existsSync(projectsConfDir)) fs.mkdirSync(projectsConfDir, { recursive: true })
|
||
|
||
// Generate project configs
|
||
const allProjects = await projectService.getProjects()
|
||
const projects = allProjects.filter(p => p.serverType === 'apache')
|
||
for (const p of projects) {
|
||
const projectConfPath = path.join(projectsConfDir, `${p.host}.conf`)
|
||
let shouldGenerate = forceProjects || !fs.existsSync(projectConfPath)
|
||
|
||
// If file exists, check if it was manually edited (though we don't have a UI for this yet, good for future)
|
||
if (!shouldGenerate && fs.existsSync(projectConfPath)) {
|
||
const currentContent = fs.readFileSync(projectConfPath, 'utf8')
|
||
if (!currentContent.includes('# MANUAL_EDIT')) {
|
||
shouldGenerate = true
|
||
}
|
||
}
|
||
|
||
if (shouldGenerate) {
|
||
const phpPort = portMappingService.getPhpPort(p.phpVersion)
|
||
let normalizedPath = p.path.replace(/\\/g, '/')
|
||
if (!normalizedPath.endsWith('/')) normalizedPath += '/'
|
||
|
||
const phpDir = path.join(configService.getBasePath(), 'bin', `php-${p.phpVersion}`)
|
||
const phpCgiBinary = findExecutable(phpDir, process.platform === 'win32' ? 'php-cgi.exe' : 'php-cgi') || ''
|
||
|
||
await configService.generateConfig('apache_project.conf.template', `projects_apache/${p.host}.conf`, {
|
||
HOST: p.host,
|
||
PATH: normalizedPath,
|
||
PHP_PORT: phpPort.toString(),
|
||
PHP_DIR: phpDir.replace(/\\/g, '/'),
|
||
PHP_CGI_PATH: phpCgiBinary.replace(/\\/g, '/'),
|
||
PHP_BIN_URL: `/php-bin-${p.host}`
|
||
})
|
||
}
|
||
}
|
||
|
||
if (!skipGlobal) {
|
||
const projectsInclusion = projects.length > 0
|
||
? `Include "${projectsConfDir.replace(/\\/g, '/')}/*.conf"`
|
||
: ''
|
||
|
||
await configService.generateConfig('httpd.conf.template', 'httpd.conf', {
|
||
PORT: settings.apachePort || '8080',
|
||
LISTEN_ADDRESS: settings.allowRemoteAccess ? '0.0.0.0' : '127.0.0.1',
|
||
PHP_PORT: settings.phpPort,
|
||
ROOT: rootPath.replace(/\\/g, '/'),
|
||
SRVROOT: apacheRoot.replace(/\\/g, '/'),
|
||
LOG_DIR: logDir.replace(/\\/g, '/'),
|
||
PHPMYADMIN_DIR: phpMyAdminService.getPath().replace(/\\/g, '/'),
|
||
PROJECTS: projectsInclusion
|
||
})
|
||
}
|
||
|
||
return { binPath, apacheDir, configPath }
|
||
}
|
||
|
||
async function restartApache(): Promise<{ success: boolean; message: string }> {
|
||
try {
|
||
const { binPath, configPath } = await rebuildApacheConfig(false)
|
||
const settings = configService.getSettings()
|
||
|
||
await processManager.forceKillAll('apache')
|
||
await processManager.stopService('apache')
|
||
// Force kill port to be absolutely sure
|
||
await processManager.killByPort('apache', settings.apachePort || '8080')
|
||
|
||
await new Promise(resolve => setTimeout(resolve, 1000))
|
||
|
||
const success = await processManager.startService('apache', binPath, ['-f', configPath], false)
|
||
|
||
if (success) {
|
||
return { success: true, message: 'Apache restart successful' }
|
||
} else {
|
||
const errorLogs = await processManager.getLastErrorLogs('apache')
|
||
return { success: false, message: `Apache restart failed:\n${errorLogs.join('\n')}` }
|
||
}
|
||
} catch (error: any) {
|
||
return { success: false, message: `Restart hatası: ${error.message}` }
|
||
}
|
||
}
|
||
|
||
export function registerIpcHandlers(): void {
|
||
ipcMain.handle('mariadb:processlist', async (_event, versionId) => {
|
||
try {
|
||
return await mariaDbManagerService.getProcessList(versionId)
|
||
} catch (error: any) {
|
||
console.error('Failed to get process list:', error)
|
||
return []
|
||
}
|
||
})
|
||
|
||
ipcMain.handle('mariadb:kill-process', async (_event, { versionId, processId }) => {
|
||
try {
|
||
await mariaDbManagerService.killProcess(versionId, processId)
|
||
return { success: true }
|
||
} catch (error: any) {
|
||
console.error('Failed to kill process:', error)
|
||
return { success: false, error: error.message }
|
||
}
|
||
})
|
||
|
||
// CLI & Environment
|
||
ipcMain.handle('cli:get-config', async () => {
|
||
const settings = configService.getSettings()
|
||
return {
|
||
enabled: settings.cliEnvEnabled !== false,
|
||
defaultPhp: settings.defaultPhpVersion,
|
||
defaultMariaDb: settings.defaultMariaDbVersion,
|
||
binDir: cliBinService.getBinDir(),
|
||
aliases: cliBinService.getAllAliases()
|
||
}
|
||
})
|
||
|
||
ipcMain.handle('cli:set-config', async (_event, config: any) => {
|
||
const settings = configService.getSettings()
|
||
configService.updateSettings({
|
||
...settings,
|
||
cliEnvEnabled: config.enabled,
|
||
defaultPhpVersion: config.defaultPhp,
|
||
defaultMariaDbVersion: config.defaultMariaDb
|
||
})
|
||
await cliAliasService.generateAliases()
|
||
return { success: true }
|
||
})
|
||
|
||
ipcMain.handle('cli:sync', async () => {
|
||
await cliAliasService.generateAliases()
|
||
return { success: true }
|
||
})
|
||
|
||
// Service management
|
||
ipcMain.handle('services:reload', async (_event, serviceName: string) => {
|
||
if (serviceName === 'nginx') return await reloadNginx()
|
||
if (serviceName === 'apache') return await restartApache() // Apache doesn't have a simple reload here
|
||
return { success: false, message: 'Bu servis için reload desteklenmiyor.' }
|
||
})
|
||
|
||
ipcMain.handle('services:start', async (_event, serviceName: string) => {
|
||
try {
|
||
const settings = configService.getSettings()
|
||
const binDir = path.join(configService.getBasePath(), 'bin')
|
||
|
||
if (serviceName === 'nginx') {
|
||
const { binPath, nginxPrefix, configPath } = await rebuildNginxConfig(false)
|
||
const success = await processManager.startService('nginx', binPath, ['-p', nginxPrefix, '-c', configPath], false)
|
||
if (!success) {
|
||
const errorLogs = await processManager.getLastErrorLogs('nginx')
|
||
return { success: false, message: `Nginx başlatılamadı.\n\n${errorLogs.join('\n')}` }
|
||
}
|
||
return { success, message: success ? 'Nginx başlatıldı.' : 'Nginx devreye alınamadı.' }
|
||
}
|
||
|
||
if (serviceName === 'apache') {
|
||
const { binPath, configPath } = await rebuildApacheConfig(false)
|
||
const settings = configService.getSettings()
|
||
// Ensure port is free
|
||
await processManager.killByPort('apache', settings.apachePort || '8080')
|
||
const success = await processManager.startService('apache', binPath, ['-f', configPath], false)
|
||
if (!success) {
|
||
const errorLogs = await processManager.getLastErrorLogs('apache')
|
||
return { success: false, message: `Apache başlatılamadı.\n\n${errorLogs.join('\n')}` }
|
||
}
|
||
return { success, message: success ? 'Apache başlatıldı.' : 'Apache devreye alınamadı.' }
|
||
}
|
||
|
||
if (serviceName.startsWith('php')) {
|
||
const versionMatch = serviceName.match(/^php:(.+)$/)
|
||
const phpVersion = versionMatch ? versionMatch[1] : null
|
||
const phpId = phpVersion ? `php-${phpVersion}` : null
|
||
|
||
const isWindows = process.platform === 'win32';
|
||
const phpDir = phpId ? path.join(binDir, phpId) : binDir
|
||
const 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.` }
|
||
|
||
const phpPort = phpVersion ? portMappingService.getPhpPort(phpVersion) : settings.phpPort
|
||
const extDir = path.join(path.dirname(binPath), 'ext').replace(/\\/g, '/')
|
||
const gdExtName = fs.existsSync(path.join(extDir, 'php_gd2.dll')) ? 'gd2' : 'gd'
|
||
|
||
const sessionDir = path.join(configService.getBasePath(), 'data', 'sessions').replace(/\\/g, '/')
|
||
const tempDir = path.join(configService.getBasePath(), 'data', 'temp').replace(/\\/g, '/')
|
||
|
||
if (!fs.existsSync(sessionDir)) fs.mkdirSync(sessionDir, { recursive: true })
|
||
if (!fs.existsSync(tempDir)) fs.mkdirSync(tempDir, { recursive: true })
|
||
|
||
const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms))
|
||
|
||
// --- 1. Get extensions from local php.ini ---
|
||
let extensionsList = ''
|
||
let errorReporting = 'E_ALL'
|
||
let displayErrors = 'On'
|
||
|
||
const localIniPath = path.join(phpDir, 'php.ini')
|
||
const mustHaves = ['curl', 'mbstring', 'openssl', 'mysqli', 'pdo_mysql', 'gd', 'fileinfo', 'exif', 'intl', 'bz2', 'gettext', 'soap', 'xsl']
|
||
|
||
if (fs.existsSync(localIniPath)) {
|
||
const localIniContent = fs.readFileSync(localIniPath, 'utf8')
|
||
|
||
// 1. Get extensions (and deduplicate them)
|
||
const extMatches = localIniContent.match(/^\s*extension\s*=.*$/gm)
|
||
if (extMatches) {
|
||
const uniqueExts = new Set<string>()
|
||
extMatches.forEach(match => {
|
||
// Extract just the dll name or extension name to prevent duplicates
|
||
const extValue = match.split('=')[1].trim()
|
||
uniqueExts.add(extValue)
|
||
})
|
||
extensionsList = Array.from(uniqueExts).map(ext => `extension=${ext}`).join('\n')
|
||
}
|
||
|
||
// 2. Get error settings
|
||
const erMatch = localIniContent.match(/^\s*error_reporting\s*=\s*(.+)$/m)
|
||
if (erMatch) errorReporting = erMatch[1].trim()
|
||
|
||
const deMatch = localIniContent.match(/^\s*display_errors\s*=\s*(.+)$/m)
|
||
if (deMatch) displayErrors = deMatch[1].trim()
|
||
} else {
|
||
// Fallback formatting based on version
|
||
const prefix = (phpVersion?.startsWith('5.') || phpVersion?.startsWith('7.')) ? 'php_' : ''
|
||
const suffix = (phpVersion?.startsWith('5.') || phpVersion?.startsWith('7.')) ? '.dll' : ''
|
||
extensionsList = mustHaves.map(m => `extension=${prefix}${m}${suffix}`).join('\n')
|
||
}
|
||
|
||
const configPath = await configService.generateConfig('php.ini.template', `php_${phpVersion || 'default'}.ini`, {
|
||
EXT_DIR: extDir,
|
||
GD_EXT: gdExtName,
|
||
SESSION_DIR: sessionDir,
|
||
TEMP_DIR: tempDir,
|
||
EXTENSIONS: extensionsList,
|
||
ERROR_REPORTING: errorReporting,
|
||
DISPLAY_ERRORS: displayErrors
|
||
})
|
||
|
||
// Add a small delay to ensure previous instance (if any) is fully killed
|
||
await sleep(1000)
|
||
const success = await processManager.startService(serviceName, binPath, ['-b', `127.0.0.1:${phpPort}`, '-c', configPath], false)
|
||
|
||
if (!success) {
|
||
const errorLogs = await processManager.getLastErrorLogs(serviceName)
|
||
return { success: false, message: `${phpVersion || ''} PHP başlatılamadı.\n\n${errorLogs.join('\n')}` }
|
||
}
|
||
|
||
return { success, message: success ? `${phpVersion || ''} PHP başlatıldı.` : `${phpVersion || ''} PHP devreye alınamadı.` }
|
||
}
|
||
|
||
if (serviceName.startsWith('mariadb') || serviceName.startsWith('mysql')) {
|
||
const versionMatch = serviceName.match(/^(?:mariadb|mysql):(.+)$/)
|
||
const dbVersion = versionMatch ? versionMatch[1] : null
|
||
const dbId = dbVersion ? `mariadb-${dbVersion}` : 'mariadb-default'
|
||
|
||
const dataDir = path.join(configService.getBasePath(), 'data', dbId)
|
||
const dbBinDir = dbVersion ? path.join(binDir, `mariadb-${dbVersion}`) : binDir
|
||
|
||
const isWindows = process.platform === 'win32';
|
||
const dbBinary = isWindows ? 'mariadbd.exe' : 'mariadbd';
|
||
const installBinary = isWindows ? 'mariadb-install-db.exe' : 'mariadb-install-db';
|
||
const mysqlInstallBinary = isWindows ? 'mysql_install_db.exe' : 'mysql_install_db';
|
||
|
||
let binPath = findExecutable(dbBinDir, dbBinary) || findExecutable(dbBinDir, isWindows ? 'mysqld.exe' : 'mysqld');
|
||
let installPath = findExecutable(dbBinDir, installBinary) || findExecutable(dbBinDir, mysqlInstallBinary);
|
||
|
||
if (!binPath) return { success: false, message: 'MariaDB executable bulunamadı. Lütfen indirin.' }
|
||
|
||
const dbVersions = await mariaDbManagerService.getVersions()
|
||
const dbInfo = dbVersions.find(v => v.id === `mariadb-${dbVersion}`)
|
||
const dbPort = dbInfo?.port || (dbVersion ? portMappingService.getMariaDbPort(dbVersion).toString() : settings.mariaDbPort)
|
||
|
||
// Force kill this specific version if running or generic mysql
|
||
await processManager.forceKillAll(serviceName)
|
||
|
||
// Also kill anything else listening on the same port
|
||
await processManager.killByPort(serviceName, dbPort)
|
||
|
||
// Initialize if data directory is empty or missing system tables
|
||
const systemDbDir = path.join(dataDir, 'mysql')
|
||
if (!fs.existsSync(dataDir) || fs.readdirSync(dataDir).length === 0 || !fs.existsSync(systemDbDir)) {
|
||
if (!fs.existsSync(dataDir)) fs.mkdirSync(dataDir, { recursive: true })
|
||
console.log(`Initializing MariaDB data directory for ${serviceName}...`)
|
||
|
||
try {
|
||
const normalizedDataDir = dataDir.replace(/\\/g, '/')
|
||
if (installPath) {
|
||
console.log(`Using install tool: ${installPath}`)
|
||
// MariaDB specific installation
|
||
await processManager.runOnce(installPath, [
|
||
`--datadir=${normalizedDataDir}`
|
||
])
|
||
} else {
|
||
console.log(`Using server init: ${binPath}`)
|
||
// MySQL 5.7+ / modern MariaDB fallback
|
||
await processManager.runOnce(binPath, ['--initialize-insecure', `--datadir=${normalizedDataDir}`])
|
||
}
|
||
} catch (err) {
|
||
console.log(`Initialization tool failed, trying template copy for ${serviceName}...`)
|
||
// Fallback: Copy default 'data' folder from binary dir if it exists
|
||
const templateDataDir = path.join(dbBinDir, 'data')
|
||
if (fs.existsSync(templateDataDir)) {
|
||
fs.cpSync(templateDataDir, dataDir, { recursive: true })
|
||
console.log(`Successfully copied template data for ${serviceName}`)
|
||
} else {
|
||
throw new Error(`MariaDB initialization failed and no template data found at ${templateDataDir}`)
|
||
}
|
||
}
|
||
}
|
||
|
||
const sanitizedServiceName = serviceName.replace(/:/g, '_')
|
||
const logFile = path.join(configService.getBasePath(), 'logs', `${sanitizedServiceName}_error.log`)
|
||
|
||
const tuning = settings.mariaDbTuning?.[dbId] || ''
|
||
const configPath = await configService.generateConfig('mariadb.ini.template', `mariadb_${dbId || 'default'}.ini`, {
|
||
PORT: dbPort,
|
||
DATADIR: dataDir.replace(/\\/g, '/'),
|
||
SOCKET: `/tmp/${sanitizedServiceName}.sock`,
|
||
LOG_FILE: logFile.replace(/\\/g, '/'),
|
||
EXTRA_CONFIG: tuning
|
||
})
|
||
|
||
const success = await processManager.startService(serviceName, binPath, ['--defaults-file=' + configPath], false)
|
||
|
||
if (success) {
|
||
// Quick health check
|
||
await new Promise(r => setTimeout(r, 1500))
|
||
if (!processManager.isServiceRunning(serviceName)) {
|
||
const errorLogs = await processManager.getLastErrorLogs(serviceName)
|
||
return { success: false, message: `${dbVersion || ''} MariaDB başlatıldı ancak çalışma hatası verdi (ExitCode: 1).\n\n${errorLogs.length > 0 ? 'Önemli Hata Logları:\n' + errorLogs.join('\n') : 'Lütfen log sekmesini kontrol edin.'}` }
|
||
}
|
||
} else {
|
||
const errorLogs = await processManager.getLastErrorLogs(serviceName)
|
||
return { success: false, message: `${dbVersion || ''} MariaDB başlatılamadı.\n\n${errorLogs.join('\n')}` }
|
||
}
|
||
|
||
return { success, message: success ? `${dbVersion || ''} MariaDB başlatıldı.` : `${dbVersion || ''} MariaDB devreye alınamadı.` }
|
||
}
|
||
|
||
return { success: false, message: 'Bilinmeyen servis.' }
|
||
} catch (error: any) {
|
||
console.error('Service start error:', error)
|
||
return { success: false, message: `Hata: ${error.message}` }
|
||
}
|
||
})
|
||
|
||
ipcMain.handle('services:stop', async (_event, serviceName: string) => {
|
||
const success = await processManager.stopService(serviceName)
|
||
return { success, message: success ? `${serviceName} durduruldu.` : `${serviceName} zaten durmuş.` }
|
||
})
|
||
|
||
ipcMain.handle('services:status', async () => {
|
||
return processManager.getStatuses()
|
||
})
|
||
|
||
ipcMain.handle('services:logs', async (_event, serviceName: string) => {
|
||
return processManager.getLogs(serviceName)
|
||
})
|
||
|
||
// Binary management
|
||
ipcMain.handle('binaries:list-php', async () => {
|
||
return phpManagerService.getVersions()
|
||
})
|
||
|
||
ipcMain.handle('binaries:download-php', async (event, id: string) => {
|
||
const versions = await phpManagerService.getVersions()
|
||
const version = versions.find(v => v.id === id)
|
||
if (!version) return { success: false, message: 'Geçersiz sürüm.' }
|
||
|
||
try {
|
||
// Signal immediate start
|
||
phpManagerService.updateProgress(id, 0, 'downloading')
|
||
event.sender.send('binaries:progress', { id, progress: 0 })
|
||
|
||
await downloadService.downloadAndExtract(version.url, id, (p) => {
|
||
phpManagerService.updateProgress(id, p)
|
||
event.sender.send('binaries:progress', { id, progress: p })
|
||
})
|
||
|
||
phpManagerService.updateProgress(id, 100, 'installed')
|
||
// Regenerate aliases after install
|
||
await cliAliasService.generateAliases()
|
||
return { success: true, message: 'İndirme tamamlandı.' }
|
||
} catch (error: any) {
|
||
phpManagerService.updateProgress(id, 0, 'available')
|
||
return { success: false, message: `Hata: ${error.message}` }
|
||
}
|
||
})
|
||
|
||
ipcMain.handle('php:extensions:get', async (_event, version: string) => {
|
||
return await phpManagerService.getExtensions(version)
|
||
})
|
||
|
||
ipcMain.handle('php:extensions:toggle', async (_event, { version, extName, enabled }) => {
|
||
const result = await phpManagerService.toggleExtension(version, extName, enabled)
|
||
if (result.success) {
|
||
const serviceName = `php:${version}`
|
||
if (processManager.isServiceRunning(serviceName)) {
|
||
await processManager.stopService(serviceName)
|
||
result.message += " (Service stopped for restart)"
|
||
}
|
||
}
|
||
return result
|
||
})
|
||
|
||
ipcMain.handle('php:config:get', async (_event, version: string) => {
|
||
const phpDir = path.join(configService.getBasePath(), 'bin', `php-${version}`)
|
||
const iniPath = path.join(phpDir, 'php.ini')
|
||
if (fs.existsSync(iniPath)) {
|
||
return {
|
||
content: fs.readFileSync(iniPath, 'utf8'),
|
||
path: iniPath
|
||
}
|
||
}
|
||
return { content: '', path: iniPath }
|
||
})
|
||
|
||
ipcMain.handle('php:config:save', async (_event, { version, content }) => {
|
||
const phpDir = path.join(configService.getBasePath(), 'bin', `php-${version}`)
|
||
const iniPath = path.join(phpDir, 'php.ini')
|
||
fs.writeFileSync(iniPath, content)
|
||
|
||
const serviceName = `php:${version}`
|
||
if (processManager.isServiceRunning(serviceName)) {
|
||
await processManager.stopService(serviceName)
|
||
}
|
||
|
||
return { success: true, message: 'Configuration saved.' }
|
||
})
|
||
|
||
// MariaDB Versions
|
||
ipcMain.handle('mariadb:versions', async () => {
|
||
return await mariaDbManagerService.getVersions()
|
||
})
|
||
|
||
ipcMain.handle('mariadb:download', async (event, id: string) => {
|
||
const versions = await mariaDbManagerService.getVersions()
|
||
const version = versions.find(v => v.id === id)
|
||
if (!version) return { success: false, message: 'Geçersiz MariaDB sürümü.' }
|
||
|
||
try {
|
||
mariaDbManagerService.updateProgress(id, 0, 'downloading')
|
||
event.sender.send('mariadb:progress', { id, progress: 0 })
|
||
|
||
await downloadService.downloadAndExtract(version.url, id, (p) => {
|
||
mariaDbManagerService.updateProgress(id, p)
|
||
event.sender.send('mariadb:progress', { id, progress: p })
|
||
})
|
||
|
||
mariaDbManagerService.updateProgress(id, 100, 'installed')
|
||
// Regenerate aliases after install
|
||
await cliAliasService.generateAliases()
|
||
return { success: true, message: 'MariaDB indirme tamamlandı.' }
|
||
} catch (error: any) {
|
||
mariaDbManagerService.updateProgress(id, 0, 'available')
|
||
return { success: false, message: `MariaDB hatası: ${error.message}` }
|
||
}
|
||
})
|
||
|
||
ipcMain.handle('mariadb:config:get', async (_event, versionId: string) => {
|
||
const settings = configService.getSettings()
|
||
const tuning = settings.mariaDbTuning?.[versionId] || ''
|
||
return { content: tuning }
|
||
})
|
||
|
||
ipcMain.handle('mariadb:config:save', async (_event, { versionId, content }) => {
|
||
const settings = configService.getSettings()
|
||
const mariaDbTuning = settings.mariaDbTuning || {}
|
||
mariaDbTuning[versionId] = content
|
||
configService.updateSettings({ ...settings, mariaDbTuning })
|
||
|
||
const serviceName = `mariadb:${versionId.replace('mariadb-', '')}`
|
||
if (processManager.isServiceRunning(serviceName)) {
|
||
await processManager.stopService(serviceName)
|
||
}
|
||
|
||
return { success: true, message: 'Tuning configuration saved.' }
|
||
})
|
||
|
||
ipcMain.handle('php:versions:all', async () => {
|
||
return phpManagerService.getVersions()
|
||
})
|
||
|
||
ipcMain.handle('php:config:get', async (_event, version: string) => {
|
||
const phpIniPath = path.join(configService.getBasePath(), 'generated_configs', `php_${version}.ini`)
|
||
if (fs.existsSync(phpIniPath)) {
|
||
return { content: fs.readFileSync(phpIniPath, 'utf8') }
|
||
}
|
||
return { content: '' }
|
||
})
|
||
|
||
ipcMain.handle('php:config:save', async (_event, { version, content }) => {
|
||
const phpIniPath = path.join(configService.getBasePath(), 'generated_configs', `php_${version}.ini`)
|
||
// Add manual edit marker to prevent overwriting
|
||
let finalContent = content
|
||
if (!finalContent.includes('; MANUAL_EDIT')) {
|
||
finalContent = `; MANUAL_EDIT\n${finalContent}`
|
||
}
|
||
fs.writeFileSync(phpIniPath, finalContent, 'utf8')
|
||
|
||
// Restart PHP if running
|
||
const serviceName = `php:${version}`
|
||
if (processManager.isServiceRunning(serviceName)) {
|
||
await processManager.stopService(serviceName)
|
||
// It will be started again later or manually
|
||
}
|
||
return { success: true }
|
||
})
|
||
|
||
// Apache Versions
|
||
ipcMain.handle('apache:versions', async () => {
|
||
return await apacheManagerService.getVersions()
|
||
})
|
||
|
||
ipcMain.handle('apache:download', async (event, id: string) => {
|
||
const versions = await apacheManagerService.getVersions()
|
||
const version = versions.find(v => v.id === id)
|
||
if (!version) return { success: false, message: 'Geçersiz Apache sürümü.' }
|
||
|
||
try {
|
||
apacheManagerService.updateProgress(id, 0, 'downloading')
|
||
event.sender.send('apache:progress', { id, progress: 0 })
|
||
|
||
await downloadService.downloadAndExtract(version.url, id, (p) => {
|
||
apacheManagerService.updateProgress(id, p)
|
||
event.sender.send('apache:progress', { id, progress: p })
|
||
})
|
||
|
||
apacheManagerService.updateProgress(id, 100, 'installed')
|
||
return { success: true, message: 'Apache indirme tamamlandı.' }
|
||
} catch (error: any) {
|
||
apacheManagerService.updateProgress(id, 0, 'available')
|
||
return { success: false, message: `Apache hatası: ${error.message}` }
|
||
}
|
||
})
|
||
|
||
// Nginx Versions
|
||
ipcMain.handle('nginx:versions', async () => {
|
||
return await nginxManagerService.getVersions()
|
||
})
|
||
|
||
ipcMain.handle('nginx:download', async (event, id: string) => {
|
||
const versions = await nginxManagerService.getVersions()
|
||
const version = versions.find(v => v.id === id)
|
||
if (!version) return { success: false, message: 'Geçersiz Nginx sürümü.' }
|
||
|
||
try {
|
||
nginxManagerService.updateProgress(id, 0, 'downloading')
|
||
event.sender.send('nginx:progress', { id, progress: 0 })
|
||
|
||
await downloadService.downloadAndExtract(version.url, id, (p) => {
|
||
nginxManagerService.updateProgress(id, p)
|
||
event.sender.send('nginx:progress', { id, progress: p })
|
||
})
|
||
|
||
nginxManagerService.updateProgress(id, 100, 'installed')
|
||
return { success: true, message: 'Nginx indirme tamamlandı.' }
|
||
} catch (error: any) {
|
||
nginxManagerService.updateProgress(id, 0, 'available')
|
||
return { success: false, message: `Nginx hatası: ${error.message}` }
|
||
}
|
||
})
|
||
|
||
ipcMain.handle('dialog:select-directory', async () => {
|
||
const result = await dialog.showOpenDialog({
|
||
properties: ['openDirectory']
|
||
})
|
||
if (result.canceled) return null
|
||
return result.filePaths[0]
|
||
})
|
||
|
||
ipcMain.handle('dialog:select-file', async (_event, filters: any[]) => {
|
||
const result = await dialog.showOpenDialog({
|
||
properties: ['openFile'],
|
||
filters: filters || [
|
||
{ name: 'SQL & Archives', extensions: ['sql', 'zip', 'gz', 'tar'] }
|
||
]
|
||
})
|
||
if (result.canceled) return null
|
||
return result.filePaths[0]
|
||
})
|
||
|
||
ipcMain.handle('dialog:save-file', async (_event, filters: any[]) => {
|
||
const result = await dialog.showSaveDialog({
|
||
filters: filters || [
|
||
{ name: 'SQL File', extensions: ['sql'] },
|
||
{ name: 'Compressed SQL', extensions: ['sql.gz'] }
|
||
]
|
||
})
|
||
if (result.canceled) return null
|
||
return result.filePath
|
||
})
|
||
|
||
ipcMain.handle('shell:open-path', async (_event, pathStr: string) => {
|
||
return await shell.openPath(pathStr)
|
||
})
|
||
|
||
ipcMain.handle('binaries:download', async (_event, { name, url }) => {
|
||
const result = await downloadService.downloadAndExtract(url, name, (p) => {
|
||
// TODO: Emit progress to renderer via webContents.send
|
||
console.log(`Download progress for ${name}: ${p}%`)
|
||
})
|
||
await cliAliasService.generateAliases()
|
||
return result
|
||
})
|
||
|
||
ipcMain.handle('cli:generate-aliases', async () => {
|
||
try {
|
||
await cliAliasService.generateAliases()
|
||
return {
|
||
success: true,
|
||
message: 'CLI aliasları başarıyla oluşturuldu.',
|
||
paths: {
|
||
powershell: configService.getGeneratedPath('aliases.ps1'),
|
||
bash: configService.getGeneratedPath('aliases.sh')
|
||
}
|
||
}
|
||
} catch (error: any) {
|
||
return { success: false, message: `Hata: ${error.message}` }
|
||
}
|
||
})
|
||
|
||
// Project management
|
||
ipcMain.handle('projects:list', async () => {
|
||
return projectService.getProjects()
|
||
})
|
||
|
||
ipcMain.handle('projects:add', async (_event, project) => {
|
||
const result = await projectService.addProject(project)
|
||
if (project.serverType === 'apache') {
|
||
await restartApache()
|
||
} else {
|
||
await restartNginx()
|
||
}
|
||
return result
|
||
})
|
||
|
||
ipcMain.handle('projects:remove', async (_event, id) => {
|
||
const projects = await projectService.getProjects()
|
||
const projectToRemove = projects.find(p => p.id === id)
|
||
const result = projectService.removeProject(id)
|
||
|
||
if (projectToRemove) {
|
||
if (projectToRemove.serverType === 'apache') {
|
||
const configPath = path.join(configService.getBasePath(), 'generated_configs', 'projects_apache', `${projectToRemove.host}.conf`)
|
||
if (fs.existsSync(configPath)) {
|
||
try { fs.unlinkSync(configPath) } catch (e) { console.error('Error deleting apache config:', e) }
|
||
}
|
||
await restartApache()
|
||
} else {
|
||
const configPath = path.join(configService.getBasePath(), 'generated_configs', 'projects', `${projectToRemove.host}.conf`)
|
||
if (fs.existsSync(configPath)) {
|
||
try { fs.unlinkSync(configPath) } catch (e) { console.error('Error deleting nginx config:', e) }
|
||
}
|
||
await restartNginx()
|
||
}
|
||
}
|
||
return result
|
||
})
|
||
ipcMain.handle('projects:update', async (_event, project) => {
|
||
// Clean up old config file if host or serverType changed
|
||
const projects = await projectService.getProjects()
|
||
const oldProject = projects.find(p => p.id === project.id)
|
||
if (oldProject && (oldProject.host !== project.host || oldProject.serverType !== project.serverType)) {
|
||
const oldTypeDir = oldProject.serverType === 'apache' ? 'projects_apache' : 'projects'
|
||
const oldConfigPath = path.join(configService.getBasePath(), 'generated_configs', oldTypeDir, `${oldProject.host}.conf`)
|
||
if (fs.existsSync(oldConfigPath)) {
|
||
try { fs.unlinkSync(oldConfigPath) } catch (e) { console.error('Error deleting old config:', e) }
|
||
}
|
||
}
|
||
|
||
const result = projectService.updateProject(project)
|
||
|
||
if (project.serverType === 'apache') {
|
||
await restartApache()
|
||
} else {
|
||
await restartNginx()
|
||
}
|
||
|
||
// If serverType changed, trigger restart for the old server as well
|
||
if (oldProject && oldProject.serverType !== project.serverType) {
|
||
if (oldProject.serverType === 'apache') await restartApache()
|
||
else await restartNginx()
|
||
}
|
||
|
||
return result
|
||
})
|
||
|
||
// Config management
|
||
ipcMain.handle('config:get', async () => {
|
||
return configService.getSettings()
|
||
})
|
||
|
||
ipcMain.handle('config:save', async (_event, config: any) => {
|
||
const settings = configService.updateSettings(config)
|
||
return { success: true, settings }
|
||
})
|
||
|
||
// Nginx Config Management
|
||
ipcMain.handle('nginx:config:get', async () => {
|
||
const configPath = path.join(configService.getBasePath(), 'generated_configs', 'nginx.conf')
|
||
if (fs.existsSync(configPath)) {
|
||
return fs.readFileSync(configPath, 'utf8')
|
||
}
|
||
return ''
|
||
})
|
||
|
||
ipcMain.handle('nginx:config:save', async (_event, content) => {
|
||
const configPath = path.join(configService.getBasePath(), 'generated_configs', 'nginx.conf')
|
||
fs.writeFileSync(configPath, content)
|
||
// Full restart after manual global save
|
||
return await restartNginx()
|
||
})
|
||
|
||
ipcMain.handle('nginx:config:test', async () => {
|
||
try {
|
||
const binDir = path.join(configService.getBasePath(), 'bin', 'nginx')
|
||
const nginxVersions = fs.readdirSync(binDir).filter(f => fs.statSync(path.join(binDir, f)).isDirectory())
|
||
const activeVersion = nginxVersions[0]
|
||
if (!activeVersion) throw new Error('Nginx not found')
|
||
|
||
const nginxPrefix = path.join(binDir, activeVersion)
|
||
const binPath = path.join(nginxPrefix, 'nginx.exe')
|
||
const configPath = path.join(configService.getBasePath(), 'generated_configs', 'nginx.conf')
|
||
|
||
const result = await processManager.runOnce(binPath, ['-t', '-p', nginxPrefix, '-c', configPath], 'nginx')
|
||
return { success: result.success, message: result.success ? 'Configuration test successful' : result.output }
|
||
} catch (error: any) {
|
||
return { success: false, message: error.message }
|
||
}
|
||
})
|
||
|
||
// Apache Config Management
|
||
ipcMain.handle('apache:config:get', async () => {
|
||
const configPath = path.join(configService.getBasePath(), 'generated_configs', 'httpd.conf')
|
||
if (fs.existsSync(configPath)) {
|
||
return fs.readFileSync(configPath, 'utf8')
|
||
}
|
||
return ''
|
||
})
|
||
|
||
ipcMain.handle('apache:config:save', async (_event, content) => {
|
||
const configPath = path.join(configService.getBasePath(), 'generated_configs', 'httpd.conf')
|
||
// Ensure manual edit marker is present
|
||
let finalContent = content
|
||
if (!finalContent.includes('# MANUAL_EDIT')) {
|
||
finalContent = `# MANUAL_EDIT\n${finalContent}`
|
||
}
|
||
fs.writeFileSync(configPath, finalContent)
|
||
return await restartApache()
|
||
})
|
||
|
||
ipcMain.handle('apache:config:test', async () => {
|
||
try {
|
||
const binDir = path.join(configService.getBasePath(), 'bin', 'apache')
|
||
const isWindows = process.platform === 'win32';
|
||
const binPath = findExecutable(binDir, isWindows ? 'httpd.exe' : 'httpd')
|
||
if (!binPath) throw new Error('Apache executable bulunamadı.')
|
||
|
||
const configPath = path.join(configService.getBasePath(), 'generated_configs', 'httpd.conf')
|
||
|
||
const result = await processManager.runOnce(binPath, ['-t', '-f', configPath], 'apache')
|
||
return { success: result.success, message: result.success ? 'Configuration test successful' : result.output }
|
||
} catch (error: any) {
|
||
return { success: false, message: error.message }
|
||
}
|
||
})
|
||
|
||
// Project Nginx Config
|
||
ipcMain.handle('project:nginx:get', async (_event, host) => {
|
||
const configPath = path.join(configService.getBasePath(), 'generated_configs', 'projects', `${host}.conf`)
|
||
if (fs.existsSync(configPath)) {
|
||
return fs.readFileSync(configPath, 'utf8')
|
||
}
|
||
return ''
|
||
})
|
||
|
||
ipcMain.handle('project:nginx:test', async (_event, host, content) => {
|
||
const configPath = path.join(configService.getBasePath(), 'generated_configs', 'projects', `${host}.conf`)
|
||
let originalContent = ''
|
||
if (fs.existsSync(configPath)) {
|
||
originalContent = fs.readFileSync(configPath, 'utf8')
|
||
}
|
||
|
||
try {
|
||
// 1. Save new content temporarily
|
||
fs.writeFileSync(configPath, content)
|
||
|
||
// 2. Run global test
|
||
const binDir = path.join(configService.getBasePath(), 'bin', 'nginx')
|
||
const nginxVersions = fs.readdirSync(binDir).filter(f => fs.statSync(path.join(binDir, f)).isDirectory())
|
||
const activeVersion = nginxVersions[0]
|
||
if (!activeVersion) throw new Error('Nginx sürümü bulunamadı.')
|
||
|
||
const nginxPrefix = path.join(binDir, activeVersion)
|
||
const binPath = path.join(nginxPrefix, 'nginx.exe')
|
||
const globalConfigPath = path.join(configService.getBasePath(), 'generated_configs', 'nginx.conf')
|
||
|
||
const result = await processManager.runOnce(binPath, ['-t', '-p', nginxPrefix, '-c', globalConfigPath], 'nginx')
|
||
|
||
// 3. Restore original content regardless of test result
|
||
// (The user must click "Save" to keep it)
|
||
if (originalContent) {
|
||
fs.writeFileSync(configPath, originalContent)
|
||
} else {
|
||
fs.unlinkSync(configPath)
|
||
}
|
||
|
||
return { success: result.success, message: result.success ? 'Configuration test successful' : result.output }
|
||
} catch (error: any) {
|
||
// Restore on error too
|
||
if (originalContent) fs.writeFileSync(configPath, originalContent)
|
||
return { success: false, message: error.message }
|
||
}
|
||
})
|
||
|
||
ipcMain.handle('project:nginx:save', async (_event, host, content) => {
|
||
const configPath = path.join(configService.getBasePath(), 'generated_configs', 'projects', `${host}.conf`)
|
||
// Ensure manual edit marker is present
|
||
let finalContent = content
|
||
if (!finalContent.includes('# MANUAL_EDIT')) {
|
||
finalContent = `# MANUAL_EDIT\n${finalContent}`
|
||
}
|
||
fs.writeFileSync(configPath, finalContent)
|
||
return await restartNginx()
|
||
})
|
||
|
||
// phpMyAdmin
|
||
ipcMain.handle('pma:status', async () => {
|
||
return await phpMyAdminService.isInstalled()
|
||
})
|
||
|
||
ipcMain.handle('pma:setup', async (event, mariaDbPort: string) => {
|
||
return await phpMyAdminService.setup(mariaDbPort, (p) => {
|
||
event.sender.send('pma:progress', p)
|
||
})
|
||
})
|
||
|
||
ipcMain.handle('services:reset-data', async (_event, serviceName: string) => {
|
||
try {
|
||
await processManager.forceKillAll(serviceName)
|
||
|
||
const versionMatch = serviceName.match(/^(?:mariadb|mysql):(.+)$/)
|
||
const dbVersion = versionMatch ? versionMatch[1] : null
|
||
const dbId = dbVersion ? `mariadb-${dbVersion}` : null
|
||
|
||
if (!dbId) return { success: false, message: 'Geçersiz servis adı.' }
|
||
|
||
const dataDir = path.join(configService.getBasePath(), 'data', dbId)
|
||
if (fs.existsSync(dataDir)) {
|
||
fs.rmSync(dataDir, { recursive: true, force: true })
|
||
}
|
||
return { success: true, message: `${serviceName} verileri sıfırlandı. Tekrar başlatmayı deneyebilirsiniz.` }
|
||
} catch (error: any) {
|
||
return { success: false, message: `Sıfırlama hatası: ${error.message}` }
|
||
}
|
||
})
|
||
|
||
// MariaDB Operations
|
||
ipcMain.handle('mariadb:import-wizard:check-db', async (_event, { versionId, dbName, rootPass }) => {
|
||
const versions = await mariaDbManagerService.getVersions()
|
||
const v = versions.find(v => v.id === versionId)
|
||
if (!v) return { exists: false, error: 'MariaDB version not found' }
|
||
|
||
const binDir = path.join(configService.getBasePath(), 'bin', versionId)
|
||
const isWindows = process.platform === 'win32'
|
||
const binPath = findExecutable(binDir, isWindows ? 'mariadb.exe' : 'mariadb') || findExecutable(binDir, isWindows ? 'mysql.exe' : 'mysql')
|
||
if (!binPath) return { exists: false, error: 'mariadb/mysql executable not found' }
|
||
|
||
const exists = await mariaDbOperationService.checkDatabaseExists({
|
||
binPath,
|
||
host: '127.0.0.1',
|
||
port: v.port,
|
||
rootUser: 'root',
|
||
rootPass: rootPass || ''
|
||
}, dbName)
|
||
|
||
return { exists }
|
||
})
|
||
|
||
ipcMain.handle('mariadb:import-wizard:start', async (event, { versionId, filePath, dbName, user, pass, rootPass, overwrite }) => {
|
||
const versions = await mariaDbManagerService.getVersions()
|
||
const v = versions.find(v => v.id === versionId)
|
||
if (!v) return { success: false, message: 'MariaDB version not found' }
|
||
|
||
const binDir = path.join(configService.getBasePath(), 'bin', versionId)
|
||
const isWindows = process.platform === 'win32'
|
||
const dbBin = findExecutable(binDir, isWindows ? 'mariadb.exe' : 'mariadb') || findExecutable(binDir, isWindows ? 'mysql.exe' : 'mysql')
|
||
if (!dbBin) return { success: false, message: 'mariadb/mysql executable not found' }
|
||
|
||
const config = {
|
||
binPath: dbBin,
|
||
host: '127.0.0.1',
|
||
port: v.port,
|
||
rootUser: 'root',
|
||
rootPass: rootPass || ''
|
||
}
|
||
|
||
const log = (msg: string) => event.sender.send('mariadb:import:log', msg)
|
||
|
||
try {
|
||
if (overwrite) {
|
||
log(`Dropping existing database: ${dbName}`)
|
||
await mariaDbOperationService.dropDatabase(config, dbName)
|
||
}
|
||
|
||
log(`Creating database and user: ${dbName} / ${user}`)
|
||
const createRes = await mariaDbOperationService.createDatabaseAndUser(config, dbName, user, pass)
|
||
if (!createRes.success) return createRes
|
||
|
||
let sqlFiles: string[] = []
|
||
if (filePath.toLowerCase().endsWith('.sql')) {
|
||
sqlFiles = [filePath]
|
||
} else {
|
||
log(`Extracting archive: ${path.basename(filePath)}`)
|
||
const extractDir = path.join(app.getPath('temp'), `mariadb_import_${Date.now()}`)
|
||
sqlFiles = await mariaDbOperationService.extractArchive(filePath, extractDir)
|
||
if (sqlFiles.length === 0) return { success: false, message: 'No .sql files found in archive.' }
|
||
}
|
||
|
||
for (const sqlFile of sqlFiles) {
|
||
log(`Importing: ${path.basename(sqlFile)}`)
|
||
const importRes = await mariaDbOperationService.importSql(config, dbName, sqlFile, (msg) => log(msg))
|
||
if (!importRes.success) return importRes
|
||
}
|
||
|
||
return { success: true, message: 'Import completed successfully.' }
|
||
} catch (error: any) {
|
||
return { success: false, message: `Import error: ${error.message}` }
|
||
}
|
||
})
|
||
|
||
ipcMain.handle('mariadb:export-wizard:list-dbs', async (_event, { versionId, rootPass }) => {
|
||
const versions = await mariaDbManagerService.getVersions()
|
||
const v = versions.find(v => v.id === versionId)
|
||
if (!v) return []
|
||
|
||
const binDir = path.join(configService.getBasePath(), 'bin', versionId)
|
||
const isWindows = process.platform === 'win32'
|
||
const dbBin = findExecutable(binDir, isWindows ? 'mariadb.exe' : 'mariadb') || findExecutable(binDir, isWindows ? 'mysql.exe' : 'mysql')
|
||
if (!dbBin) return []
|
||
|
||
return await mariaDbOperationService.listDatabases({
|
||
binPath: dbBin,
|
||
host: '127.0.0.1',
|
||
port: v.port,
|
||
rootUser: 'root',
|
||
rootPass: rootPass || ''
|
||
})
|
||
})
|
||
|
||
ipcMain.handle('mariadb:export-wizard:start', async (event, { versionId, dbName, savePath, compress, rootPass }) => {
|
||
const versions = await mariaDbManagerService.getVersions()
|
||
const v = versions.find(v => v.id === versionId)
|
||
if (!v) return { success: false, message: 'MariaDB version not found' }
|
||
|
||
const binDir = path.join(configService.getBasePath(), 'bin', versionId)
|
||
const isWindows = process.platform === 'win32'
|
||
const dbBin = findExecutable(binDir, isWindows ? 'mariadb.exe' : 'mariadb') || findExecutable(binDir, isWindows ? 'mysql.exe' : 'mysql')
|
||
if (!dbBin) return { success: false, message: 'mariadb/mysql executable not found' }
|
||
|
||
const log = (msg: string) => event.sender.send('mariadb:export:log', msg)
|
||
|
||
return await mariaDbOperationService.exportSql({
|
||
binPath: dbBin,
|
||
host: '127.0.0.1',
|
||
port: v.port,
|
||
rootUser: 'root',
|
||
rootPass: rootPass || ''
|
||
}, dbName, savePath, compress, (msg) => log(msg))
|
||
})
|
||
}
|