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' && (
+ } onClick={() => handleDownloadApache(v.id)}>
+ İNDİR
+
+ )}
+ {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 {