feat: implement project management and service orchestration architecture

This commit is contained in:
Ümit Tunç
2026-03-30 22:16:55 +03:00
parent 46beccc484
commit e22b1872f0
10 changed files with 441 additions and 21 deletions
+118 -6
View File
@@ -7,6 +7,7 @@ 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'
@@ -55,7 +56,8 @@ async function rebuildNginxConfig(forceProjects: boolean = false): Promise<{ bin
}
// Generate project configs
const projects = await projectService.getProjects()
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)
@@ -142,12 +144,88 @@ async function reloadNginx(): Promise<{ success: boolean; message: string }> {
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(app.getAppPath(), 'www')
// 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 (shouldGenerate) {
const phpPort = portMappingService.getPhpPort(p.phpVersion)
let normalizedPath = p.path.replace(/\\/g, '/')
if (!normalizedPath.endsWith('/')) normalizedPath += '/'
await configService.generateConfig('apache_project.conf.template', `projects_apache/${p.host}.conf`, {
HOST: p.host,
PATH: normalizedPath,
PHP_PORT: phpPort.toString()
})
}
}
const projectsInclusion = projects.length > 0
? `Include "${projectsConfDir.replace(/\\/g, '/')}/*.conf"`
: ''
const configPath = 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)
await processManager.forceKillAll('apache')
await processManager.stopService('apache')
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 {
// Service management
ipcMain.handle('services:reload', async (_event, serviceName: string) => {
if (serviceName !== 'nginx') return { success: false, message: 'Yalnızca Nginx için reload desteklenmektedir.' }
return await reloadNginx()
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) => {
@@ -157,17 +235,24 @@ export function registerIpcHandlers(): void {
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 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
@@ -451,6 +536,33 @@ export function registerIpcHandlers(): void {
}
})
// 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()