From e22b1872f010ce734fb0047ccea6159b61450099 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=9Cmit=20Tun=C3=A7?= Date: Mon, 30 Mar 2026 22:16:55 +0300 Subject: [PATCH] feat: implement project management and service orchestration architecture --- config/apache_project.conf.template | 10 ++ config/httpd.conf.template | 60 ++++++++ src/main/index.ts | 2 +- src/main/ipc/index.ts | 124 +++++++++++++++- src/main/services/ApacheManagerService.ts | 86 ++++++++++++ src/main/services/ConfigService.ts | 2 + src/main/services/ProcessManager.ts | 10 +- src/main/services/ProjectService.ts | 1 + src/preload/index.ts | 3 + src/renderer/src/App.tsx | 164 ++++++++++++++++++++-- 10 files changed, 441 insertions(+), 21 deletions(-) create mode 100644 config/apache_project.conf.template create mode 100644 config/httpd.conf.template create mode 100644 src/main/services/ApacheManagerService.ts diff --git a/config/apache_project.conf.template b/config/apache_project.conf.template new file mode 100644 index 0000000..f8d7a90 --- /dev/null +++ b/config/apache_project.conf.template @@ -0,0 +1,10 @@ +Alias /{{HOST}} "{{PATH}}" + + Options Indexes FollowSymLinks + AllowOverride All + Require all granted + + + SetHandler "proxy:fcgi://127.0.0.1:{{PHP_PORT}}" + + diff --git a/config/httpd.conf.template b/config/httpd.conf.template new file mode 100644 index 0000000..14551b9 --- /dev/null +++ b/config/httpd.conf.template @@ -0,0 +1,60 @@ +Define SRVROOT "{{SRVROOT}}" + +Listen {{LISTEN_ADDRESS}}:{{PORT}} + +LoadModule mpm_winnt_module modules/mod_mpm_winnt.so +LoadModule authz_core_module modules/mod_authz_core.so +LoadModule authz_host_module modules/mod_authz_host.so +LoadModule dir_module modules/mod_dir.so +LoadModule mime_module modules/mod_mime.so +LoadModule alias_module modules/mod_alias.so +LoadModule log_config_module modules/mod_log_config.so +LoadModule rewrite_module modules/mod_rewrite.so +LoadModule proxy_module modules/mod_proxy.so +LoadModule proxy_fcgi_module modules/mod_proxy_fcgi.so + +ServerAdmin admin@localhost +ServerName localhost:{{PORT}} + +DocumentRoot "{{ROOT}}" + + Options Indexes FollowSymLinks + AllowOverride All + Require all granted + + +ErrorLog "{{LOG_DIR}}/apache_error.log" +LogLevel warn + + + LogFormat "%h %l %u %t \"%r\" %>s %b" common + CustomLog "{{LOG_DIR}}/apache_access.log" common + + + + DirectoryIndex index.php index.html index.htm + + + + Require all denied + + +# phpMyAdmin configuration +Alias /phpmyadmin "{{PHPMYADMIN_DIR}}" + + Options Indexes FollowSymLinks + AllowOverride All + Require all granted + + + SetHandler "proxy:fcgi://127.0.0.1:{{PHP_PORT}}" + + + +# Global PHP handler + + SetHandler "proxy:fcgi://127.0.0.1:{{PHP_PORT}}" + + +# Project configurations +{{PROJECTS}} diff --git a/src/main/index.ts b/src/main/index.ts index 0805685..49497ab 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -84,9 +84,9 @@ app.whenReady().then(() => { optimizer.watchWindowShortcuts(window) }) + registerIpcHandlers() createWindow() createTray() - registerIpcHandlers() // Generate CLI aliases on start cliAliasService.generateAliases().catch(console.error) diff --git a/src/main/ipc/index.ts b/src/main/ipc/index.ts index 9a91648..a3e36f4 100644 --- a/src/main/ipc/index.ts +++ b/src/main/ipc/index.ts @@ -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() diff --git a/src/main/services/ApacheManagerService.ts b/src/main/services/ApacheManagerService.ts new file mode 100644 index 0000000..f48ed59 --- /dev/null +++ b/src/main/services/ApacheManagerService.ts @@ -0,0 +1,86 @@ +import fs from 'fs' +import path from 'path' +import { configService } from './ConfigService' + +export interface ApacheVersion { + id: string + version: string + url: string + status: 'available' | 'downloading' | 'installed' + progress: number +} + +export class ApacheManagerService { + private versions: ApacheVersion[] = [] + private binDir: string + + constructor() { + this.binDir = path.join(configService.getBasePath(), 'bin') + this.init() + } + + private async init() { + if (!fs.existsSync(this.binDir)) { + fs.mkdirSync(this.binDir, { recursive: true }) + } + } + + async getVersions(): Promise { + // Apache Lounge VS17 Win64 version + const stableVersion = '2.4.66' + const url = `https://www.apachelounge.com/download/VS18/binaries/httpd-2.4.66-260223-Win64-VS18.zip` + + const id = 'apache' + const targetDir = path.join(this.binDir, id) + + // Check if installed (look for httpd.exe anywhere in the zip extraction folder) + const isInstalled = this.isInstalled(targetDir) + + const v: ApacheVersion = { + id, + version: stableVersion, + url, + status: isInstalled ? 'installed' : 'available', + progress: isInstalled ? 100 : 0 + } + + // Maintain internal state for progress updates + const existing = this.versions.find(ev => ev.id === id) + if (existing && existing.status === 'downloading') { + return [existing] + } + + this.versions = [v] + return this.versions + } + + private isInstalled(dir: string): boolean { + if (!fs.existsSync(dir)) return false + + const findHttpd = (d: string): boolean => { + const files = fs.readdirSync(d) + for (const f of files) { + const p = path.join(d, f) + const s = fs.statSync(p) + if (s.isDirectory()) { + if (findHttpd(p)) return true + } else if (f.toLowerCase() === 'httpd.exe') { + return true + } + } + return false + } + + return findHttpd(dir) + } + + updateProgress(id: string, progress: number, status: ApacheVersion['status'] = 'downloading') { + const v = this.versions.find(ev => ev.id === id) + if (v) { + v.progress = progress + v.status = status + } + } +} + +export const apacheManagerService = new ApacheManagerService() diff --git a/src/main/services/ConfigService.ts b/src/main/services/ConfigService.ts index 066cbb1..f8f382d 100644 --- a/src/main/services/ConfigService.ts +++ b/src/main/services/ConfigService.ts @@ -8,6 +8,8 @@ export class ConfigService { private settingsPath: string private defaultSettings = { nginxPort: '80', + apachePort: '8080', + serverType: 'nginx' as 'nginx' | 'apache', phpPort: '9001', mariaDbPort: '3306', mariaDbPorts: {} as Record, diff --git a/src/main/services/ProcessManager.ts b/src/main/services/ProcessManager.ts index 8a80fd6..20b9205 100644 --- a/src/main/services/ProcessManager.ts +++ b/src/main/services/ProcessManager.ts @@ -22,7 +22,7 @@ export class ProcessManager { fs.mkdirSync(this.logDir, { recursive: true }) } // Initialize with default logs - ['nginx', 'php', 'mariadb'].forEach(s => { + ['nginx', 'apache', 'php', 'mariadb'].forEach(s => { if (!this.logs.has(s)) { this.logs.set(s, [`[${new Date().toLocaleTimeString()}] Sistem hazır. İşlem bekleniyor...`]) } @@ -209,12 +209,12 @@ export class ProcessManager { } else { const isWindows = process.platform === 'win32'; if (isWindows) { - const executableName = name === 'nginx' ? 'nginx.exe' : name === 'php' ? 'php-cgi.exe' : 'mariadbd.exe' + const executableName = name === 'nginx' ? 'nginx.exe' : name === 'apache' ? 'httpd.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' + const processName = name === 'nginx' ? 'nginx' : name === 'apache' ? 'httpd' : name === 'php' ? 'php-cgi' : 'mariadbd' spawn('pkill', ['-f', processName], { windowsHide: true }) .on('close', () => resolve(true)) .on('error', () => resolve(true)) @@ -227,7 +227,7 @@ export class ProcessManager { this.processes.delete(name) } - const procName = name === 'nginx' ? 'nginx' : name === 'php' ? 'php-fpm' : 'mariadbd' + const procName = name === 'nginx' ? 'nginx' : name === 'apache' ? 'httpd' : name === 'php' ? 'php-fpm' : 'mariadbd' spawn('pkill', ['-9', '-f', procName]) .on('close', () => resolve(true)) .on('error', () => resolve(true)) @@ -287,6 +287,7 @@ export class ProcessManager { } // Ensure defaults are present if stopped if (!statuses['nginx']) statuses['nginx'] = 'stopped' + if (!statuses['apache']) statuses['apache'] = 'stopped' if (!statuses['mariadb']) statuses['mariadb'] = 'stopped' return statuses } @@ -295,6 +296,7 @@ export class ProcessManager { if (!this.logDir) return const logPaths: Record = { nginx: path.join(this.logDir, 'nginx_error.log'), + apache: path.join(this.logDir, 'apache_error.log'), mariadb: path.join(this.logDir, 'mariadb_error.log') } diff --git a/src/main/services/ProjectService.ts b/src/main/services/ProjectService.ts index 56c26ba..2bd365c 100644 --- a/src/main/services/ProjectService.ts +++ b/src/main/services/ProjectService.ts @@ -9,6 +9,7 @@ export interface Project { phpVersion: string mariaDbVersion: string host: string + serverType?: 'nginx' | 'apache' } export class ProjectService { diff --git a/src/preload/index.ts b/src/preload/index.ts index e82359e..c28717c 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -25,6 +25,9 @@ const api = { getNginxVersions: () => electronAPI.ipcRenderer.invoke('nginx:versions'), downloadNginxVersion: (id: string) => electronAPI.ipcRenderer.invoke('nginx:download', id), onNginxProgress: (callback: any) => electronAPI.ipcRenderer.on('nginx:progress', (_event, data) => callback(data)), + getApacheVersions: () => electronAPI.ipcRenderer.invoke('apache:versions'), + downloadApacheVersion: (id: string) => electronAPI.ipcRenderer.invoke('apache:download', id), + onApacheProgress: (callback: any) => electronAPI.ipcRenderer.on('apache:progress', (_event, data) => callback(data)), pmaStatus: () => electronAPI.ipcRenderer.invoke('pma:status'), pmaSetup: (mariaDbPort: string) => electronAPI.ipcRenderer.invoke('pma:setup', mariaDbPort), onPmaProgress: (callback: any) => electronAPI.ipcRenderer.on('pma:progress', (_event, data) => callback(data)), diff --git a/src/renderer/src/App.tsx b/src/renderer/src/App.tsx index b6c7d13..546debe 100644 --- a/src/renderer/src/App.tsx +++ b/src/renderer/src/App.tsx @@ -91,6 +91,8 @@ interface ServiceStatus { interface AppSettings { nginxPort: string + apachePort: string + serverType: 'nginx' | 'apache' phpPort: string mariaDbPort: string mariaDbPorts?: Record @@ -102,6 +104,7 @@ function App(): JSX.Element { const { t, i18n } = useTranslation() const [status, setStatus] = useState({ nginx: 'stopped', + apache: 'stopped', php: 'stopped', mariadb: 'stopped' }) @@ -112,11 +115,14 @@ function App(): JSX.Element { const [phpVersions, setPhpVersions] = useState([]) const [mariaDbVersions, setMariaDbVersions] = useState([]) const [nginxVersions, setNginxVersions] = useState([]) + const [apacheVersions, setApacheVersions] = useState([]) const [isProjectDialogOpen, setIsProjectDialogOpen] = useState(false) const slugify = (text: string) => text.toLowerCase().trim().replace(/[^\w\s-]/g, '').replace(/[\s_-]+/g, '-').replace(/^-+|-+$/g, ''); - const [newProject, setNewProject] = useState({ name: '', path: '', phpVersion: '', mariaDbVersion: '', host: 'localhost' }) + const [newProject, setNewProject] = useState({ name: '', path: '', phpVersion: '', mariaDbVersion: '', host: 'localhost', serverType: 'nginx' }) const [settings, setSettings] = useState({ nginxPort: '80', + apachePort: '8080', + serverType: 'nginx', phpPort: '9001', mariaDbPort: '3306', allowRemoteAccess: false, @@ -151,7 +157,7 @@ function App(): JSX.Element { const filePath = match[1].replace(/\\/g, '/') const line = parseInt(match[2]) const fileName = filePath.split('/').pop() || '' - const isProjectConfig = filePath.includes('/generated_configs/projects/') + const isProjectConfig = filePath.includes('/generated_configs/projects/') || filePath.includes('/generated_configs/projects_apache/') const host = isProjectConfig ? fileName.replace('.conf', '') : null return { filePath, line, host } } @@ -198,6 +204,12 @@ function App(): JSX.Element { } } + const fetchApacheVersions = async () => { + if (!window.api || !window.api.getApacheVersions) return + const v = await window.api.getApacheVersions() + setApacheVersions(v || []) + } + const fetchLogs = async (service: string) => { if (window.api && window.api.getServiceLogs) { const list = await window.api.getServiceLogs(service) @@ -238,6 +250,7 @@ function App(): JSX.Element { fetchPhpVersions() fetchMariaDbVersions() fetchNginxVersions() + fetchApacheVersions() fetchSettings() fetchPmaStatus() @@ -259,6 +272,12 @@ function App(): JSX.Element { setNginxVersions(prev => prev.map(v => v.id === id ? { ...v, progress, status: progress === 100 ? 'installed' : 'downloading' } : v)) }) : null + const removeApacheListener = window.api.onApacheProgress ? window.api.onApacheProgress((data) => { + if (!data) return + const { id, progress } = data + setApacheVersions(prev => prev.map(v => v.id === id ? { ...v, progress, status: progress === 100 ? 'installed' : 'downloading' } : v)) + }) : null + const removePmaListener = window.api.onPmaProgress ? window.api.onPmaProgress((p: number) => { setPmaProgress(p) }) : null @@ -284,6 +303,7 @@ function App(): JSX.Element { if (typeof removePhpListener === 'function') removePhpListener() if (typeof removeMariaDbListener === 'function') removeMariaDbListener() if (typeof removeNginxListener === 'function') removeNginxListener() + if (typeof removeApacheListener === 'function') removeApacheListener() if (typeof removePmaListener === 'function') removePmaListener() } }, [logDialogOpen, selectedService]) @@ -392,7 +412,7 @@ function App(): JSX.Element { const handleStartAll = async () => { if (!window.api) return - const criticalServices = ['nginx'] + const criticalServices = [settings.serverType] for (const s of criticalServices) { if (status[s] === 'stopped') setStatus(prev => ({ ...prev, [s]: 'starting' })) } @@ -419,7 +439,7 @@ function App(): JSX.Element { const handleStopAll = async () => { if (!window.api) return - const criticalServices = ['nginx'] + const criticalServices = ['nginx', 'apache'] const phpToStop = Object.keys(status).filter(s => s.startsWith('php:') && (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')) @@ -587,6 +607,17 @@ function App(): JSX.Element { fetchNginxVersions() } + const handleDownloadApache = async (id: string) => { + if (!window.api) return + const result = await window.api.downloadApacheVersion(id) + setNotification({ + open: true, + message: result.message, + severity: result.success ? 'success' : 'error' + }) + fetchApacheVersions() + } + const handleSetupPma = async () => { if (!window.api) return setPmaLoading(true) @@ -605,7 +636,8 @@ function App(): JSX.Element { } const handleOpenPma = () => { - window.open(`http://localhost:${settings.nginxPort}/phpmyadmin`, '_blank') + const port = settings.serverType === 'apache' ? settings.apachePort : settings.nginxPort + window.open(`http://localhost:${port}/phpmyadmin`, '_blank') } const handleOpenPhpExtensions = (version: string) => { @@ -712,6 +744,7 @@ function App(): JSX.Element { } iconPosition="start" /> } iconPosition="start" /> } iconPosition="start" /> + } iconPosition="start" /> } iconPosition="start" /> @@ -865,10 +898,58 @@ function App(): JSX.Element { )} {settingsTab === 3 && ( + + + Apache Web Sunucusu + Apache HTTP Server binary dosyalarını buradan yönetebilirsiniz. + + + {apacheVersions.map((v) => ( + + + {v.status === 'installed' ? : } + + + + {v.status === 'available' && ( + + )} + {v.status === 'downloading' && ( + + + + )} + {v.status === 'installed' && ( + + )} + + + ))} + + + )} + + {settingsTab === 4 && ( Port Ayarları + + Varsayılan Web Sunucusu + + + setSettings({ ...settings, apachePort: e.target.value })} + variant="filled" + size="small" + helperText="Varsayılan: 8080" + /> setDashboardTab(v)} textColor="primary" indicatorColor="primary"> + @@ -991,8 +1081,41 @@ function App(): JSX.Element { )} + {/* Apache Box */} + {(dashboardTab === 0 || dashboardTab === 2) && ( + + + + + {getStatusChip(status.apache)} + + Apache + Port: {settings.apachePort} + + + handleOpenLogs('apache')} title="Hata Logları" size="small" sx={{ bgcolor: 'rgba(255,255,255,0.05)' }}> + + + + handleToggleService('apache')} + disabled={status.apache === 'starting'} + /> + + + + )} + {/* PHP Boxes */} - {(dashboardTab === 0 || dashboardTab === 2) && installedPhpVersions.map(v => ( + {(dashboardTab === 0 || dashboardTab === 3) && installedPhpVersions.map(v => ( v.status === 'installed').map(v => ( + {(dashboardTab === 0 || dashboardTab === 4) && mariaDbVersions.filter(v => v.status === 'installed').map(v => ( + : } + label={project.serverType === 'apache' ? 'Apache' : 'Nginx'} + sx={{ bgcolor: 'rgba(255, 255, 255, 0.05)', color: '#fff', fontWeight: 600 }} + /> } - onClick={() => window.open(`http://localhost:${settings.nginxPort}/${project.host}/`, '_blank')} + onClick={() => { + const port = project.serverType === 'apache' ? settings.apachePort : settings.nginxPort + window.open(`http://localhost:${port}/${project.host}/`, '_blank') + }} sx={{ color: '#39A7FF', textTransform: 'none', @@ -1422,7 +1554,7 @@ function App(): JSX.Element { { setIsProjectDialogOpen(false) - setNewProject({ name: '', path: '', phpVersion: '', mariaDbVersion: '', host: 'localhost' }) + setNewProject({ name: '', path: '', phpVersion: '', mariaDbVersion: '', host: 'localhost', serverType: 'nginx' }) }} fullWidth maxWidth="sm"> {newProject.id ? 'Proje Düzenle' : 'Yeni Proje Ekle'} @@ -1471,12 +1603,24 @@ function App(): JSX.Element { )} + + Web Sunucusu + +