feat: implement Nginx configuration management, PHP environment initialization, and extension management UI

This commit is contained in:
Ümit Tunç
2026-03-30 17:16:01 +03:00
parent 4dfc7c0834
commit d0a48e762f
3 changed files with 62 additions and 45 deletions
+2 -2
View File
@@ -14,8 +14,8 @@ expose_php = On
max_execution_time = 300 max_execution_time = 300
max_input_time = 120 max_input_time = 120
memory_limit = 512M memory_limit = 512M
error_reporting = E_ALL error_reporting = {{ERROR_REPORTING}}
display_errors = On display_errors = {{DISPLAY_ERRORS}}
display_startup_errors = On display_startup_errors = On
log_errors = On log_errors = On
log_errors_max_len = 1024 log_errors_max_len = 1024
+19 -19
View File
@@ -194,15 +194,27 @@ export function registerIpcHandlers(): void {
// --- 1. Get extensions from local php.ini --- // --- 1. Get extensions from local php.ini ---
let extensionsList = '' let extensionsList = ''
let errorReporting = 'E_ALL'
let displayErrors = 'On'
const localIniPath = path.join(phpDir, 'php.ini') const localIniPath = path.join(phpDir, 'php.ini')
const mustHaves = ['curl', 'mbstring', 'openssl', 'mysqli', 'pdo_mysql', 'gd', 'fileinfo', 'exif', 'intl', 'bz2', 'gettext', 'soap', 'xsl'] const mustHaves = ['curl', 'mbstring', 'openssl', 'mysqli', 'pdo_mysql', 'gd', 'fileinfo', 'exif', 'intl', 'bz2', 'gettext', 'soap', 'xsl']
if (fs.existsSync(localIniPath)) { if (fs.existsSync(localIniPath)) {
const localIniContent = fs.readFileSync(localIniPath, 'utf8') const localIniContent = fs.readFileSync(localIniPath, 'utf8')
// 1. Get extensions
const extMatches = localIniContent.match(/^\s*extension\s*=.*$/gm) const extMatches = localIniContent.match(/^\s*extension\s*=.*$/gm)
if (extMatches) { if (extMatches) {
extensionsList = extMatches.join('\n') extensionsList = extMatches.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 { } else {
// Fallback formatting based on version // Fallback formatting based on version
const prefix = (phpVersion?.startsWith('5.') || phpVersion?.startsWith('7.')) ? 'php_' : '' const prefix = (phpVersion?.startsWith('5.') || phpVersion?.startsWith('7.')) ? 'php_' : ''
@@ -215,7 +227,9 @@ export function registerIpcHandlers(): void {
GD_EXT: gdExtName, GD_EXT: gdExtName,
SESSION_DIR: sessionDir, SESSION_DIR: sessionDir,
TEMP_DIR: tempDir, TEMP_DIR: tempDir,
EXTENSIONS: extensionsList EXTENSIONS: extensionsList,
ERROR_REPORTING: errorReporting,
DISPLAY_ERRORS: displayErrors
}) })
// Add a small delay to ensure previous instance (if any) is fully killed // Add a small delay to ensure previous instance (if any) is fully killed
@@ -374,23 +388,10 @@ export function registerIpcHandlers(): void {
ipcMain.handle('php:extensions:toggle', async (_event, { version, extName, enabled }) => { ipcMain.handle('php:extensions:toggle', async (_event, { version, extName, enabled }) => {
const result = await phpManagerService.toggleExtension(version, extName, enabled) const result = await phpManagerService.toggleExtension(version, extName, enabled)
if (result.success) { if (result.success) {
// Automatically restart the PHP service if it's running
const serviceName = `php:${version}` const serviceName = `php:${version}`
if (processManager.isServiceRunning(serviceName)) { if (processManager.isServiceRunning(serviceName)) {
console.log(`Restarting ${serviceName} due to extension change...`)
await processManager.stopService(serviceName) await processManager.stopService(serviceName)
// We need to re-run the start logic or just let the user do it? result.message += " (Service stopped for restart)"
// The user said "Proceed with the decision that you think is the most optimal"
// Optimal is to restart it exactly as it was.
// However, the start logic in 'services:start' has many parameters (ports, ini templates).
// It's better to just stop it and tell the user it needs to be started again,
// OR I can try to trigger the same start logic.
// Given the current architecture, I'll just stop it and return a message.
// Wait, I can't easily call 'services:start' from here without the event object.
// Actually, I can just not restart it here and let the renderer handle it if they want.
// NO, I'll just return that it's updated. PHP usually needs a restart anyway.
// I'll update the message to reflect that.
result.message += ". Please restart the PHP service to apply changes."
} }
} }
return result return result
@@ -413,13 +414,12 @@ export function registerIpcHandlers(): void {
const iniPath = path.join(phpDir, 'php.ini') const iniPath = path.join(phpDir, 'php.ini')
fs.writeFileSync(iniPath, content) fs.writeFileSync(iniPath, content)
// Return a result that indicates a restart is needed
const serviceName = `php:${version}` const serviceName = `php:${version}`
const isRunning = processManager.isServiceRunning(serviceName) if (processManager.isServiceRunning(serviceName)) {
if (isRunning) {
await processManager.stopService(serviceName) await processManager.stopService(serviceName)
} }
return { success: true, message: `php.ini saved.${isRunning ? ' Current PHP service stopped. Please restart to apply.' : ''}` }
return { success: true, message: 'Configuration saved.' }
}) })
// MariaDB Versions // MariaDB Versions
@@ -71,18 +71,34 @@ const PhpExtensionManager: React.FC<PhpExtensionManagerProps> = ({ open, onClose
const handleSaveRaw = async () => { const handleSaveRaw = async () => {
setIsSavingRaw(true) setIsSavingRaw(true)
Swal.fire({
title: 'Yapılandırma Kaydediliyor',
text: 'PHP servisi yeniden başlatılıyor...',
allowOutsideClick: false,
didOpen: () => {
Swal.showLoading()
},
background: '#1e1e1e',
color: '#fff'
})
try { try {
const result = await window.api.invoke('php:config:save', { version: phpVersion, content: rawConfig }) const result = await window.api.invoke('php:config:save', { version: phpVersion, content: rawConfig })
if (result.success) { if (result.success) {
// Trigger restart
await window.api.invoke('services:start', `php:${phpVersion}`)
Swal.fire({ Swal.fire({
title: 'Başarılı', title: 'Başarılı',
text: result.message, text: 'Yapılandırma kaydedildi ve servis yeniden başlatıldı.',
icon: 'success', icon: 'success',
timer: 2000,
showConfirmButton: false,
background: '#1e1e1e', background: '#1e1e1e',
color: '#fff' color: '#fff'
}) })
setIsRawEditorOpen(false) setIsRawEditorOpen(false)
fetchExtensions() // Refresh extension list too fetchExtensions()
} }
} catch (error: any) { } catch (error: any) {
Swal.fire({ title: 'Hata', text: error.message, icon: 'error', background: '#1e1e1e', color: '#fff' }) Swal.fire({ title: 'Hata', text: error.message, icon: 'error', background: '#1e1e1e', color: '#fff' })
@@ -99,36 +115,37 @@ const PhpExtensionManager: React.FC<PhpExtensionManagerProps> = ({ open, onClose
const handleToggle = async (extName: string, enabled: boolean) => { const handleToggle = async (extName: string, enabled: boolean) => {
setSaving(extName) setSaving(extName)
// Show a non-blocking toast or a small overlay if we want, but for "Restart" let's be more explicit if it was running
const wasRunning = await window.api.invoke('service:status', `php:${phpVersion}`) === 'running'
if (wasRunning) {
Swal.fire({
title: 'Eklenti Ayarlanıyor',
text: `${extName} ${enabled ? 'aktif ediliyor' : 'devre dışı bırakılıyor'} ve PHP yeniden başlatılıyor...`,
allowOutsideClick: false,
didOpen: () => {
Swal.showLoading()
},
background: '#1e1e1e',
color: '#fff'
})
}
try { try {
const result = await window.api.invoke('php:extensions:toggle', { version: phpVersion, extName, enabled }) const result = await window.api.invoke('php:extensions:toggle', { version: phpVersion, extName, enabled })
if (result.success) { if (result.success) {
setExtensions(prev => prev.map(ext => ext.name === extName ? { ...ext, enabled } : ext)) setExtensions(prev => prev.map(ext => ext.name === extName ? { ...ext, enabled } : ext))
if (result.message.includes('restart')) {
Swal.fire({ if (wasRunning) {
title: 'Bilgi', await window.api.invoke('services:start', `php:${phpVersion}`)
text: result.message, Swal.close()
icon: 'info',
background: '#1e1e1e',
color: '#fff'
})
} }
} else { } else {
Swal.fire({ Swal.fire({ title: 'Hata', text: result.message, icon: 'error', background: '#1e1e1e', color: '#fff' })
title: 'Hata',
text: result.message,
icon: 'error',
background: '#1e1e1e',
color: '#fff'
})
} }
} catch (error: any) { } catch (error: any) {
Swal.fire({ Swal.fire({ title: 'Hata', text: error.message, icon: 'error', background: '#1e1e1e', color: '#fff' })
title: 'Hata',
text: error.message,
icon: 'error',
background: '#1e1e1e',
color: '#fff'
})
} finally { } finally {
setSaving(null) setSaving(null)
} }