From ee4e3660f0a38b9ef60d555e633f487ca82eecba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=9Cmit=20Tun=C3=A7?= Date: Fri, 3 Apr 2026 07:06:39 +0300 Subject: [PATCH] feat: implement dashboard UI components, IPC handlers, and service configuration templates --- config/apache_project.conf.template | 2 +- config/apache_project_alias.conf.template | 3 + config/php.ini.template.bak | 56 ++++ service_events.json | 313 ++++++++++++++++++ src/main/ipc/index.ts | 3 + .../components/Dashboard/DashboardDetails.tsx | 21 +- .../components/Dashboard/DashboardManager.tsx | 48 ++- .../components/Dashboard/DashboardSidebar.tsx | 11 +- 8 files changed, 436 insertions(+), 21 deletions(-) create mode 100644 config/php.ini.template.bak diff --git a/config/apache_project.conf.template b/config/apache_project.conf.template index 7c781d4..85fb296 100644 --- a/config/apache_project.conf.template +++ b/config/apache_project.conf.template @@ -8,7 +8,7 @@ Alias /{{HOST}} "{{PATH}}" # --- KRİTİK EKLEME BURASI --- # PHP'ye php.ini dosyasını {{PHP_DIR}} klasöründe aramasını söyler - SetEnv PHPRC "{{PHP_DIR}}" + SetEnv PHPRC "{{PHP_INI_DIR}}" # ---------------------------- # Passing Authorization header to PHP diff --git a/config/apache_project_alias.conf.template b/config/apache_project_alias.conf.template index 527efed..86182ac 100644 --- a/config/apache_project_alias.conf.template +++ b/config/apache_project_alias.conf.template @@ -6,6 +6,9 @@ Alias /{{HOST}} "{{PATH}}" AllowOverride All Require all granted + # PHP'ye php.ini dosyasını generated_configs klasöründe aramasını söyler + SetEnv PHPRC "{{PHP_INI_DIR}}" + # Passing Authorization header to PHP CGIPassAuth On diff --git a/config/php.ini.template.bak b/config/php.ini.template.bak new file mode 100644 index 0000000..bc950d1 --- /dev/null +++ b/config/php.ini.template.bak @@ -0,0 +1,56 @@ +[PHP] +engine = On +short_open_tag = Off +precision = 14 +output_buffering = 4096 +zlib.output_compression = Off +implicit_flush = Off +unserialize_callback_func = +serialize_precision = -1 +disable_functions = +disable_classes = +zend.enable_gc = On +expose_php = On +max_execution_time = 300 +max_input_time = 120 +memory_limit = 512M +error_reporting = {{ERROR_REPORTING}}asdasd +display_errors = {{DISPLAY_ERRORS}} +display_startup_errors = On +log_errors = On +log_errors_max_len = 1024 +ignore_repeated_errors = Off +ignore_repeated_source = Off +report_memleaks = On +html_errors = On +variables_order = "GPCS" +request_order = "GP" +register_argc_argv = Off +auto_globals_jit = On +post_max_size = 100M +auto_prepend_file = +auto_append_file = +default_mimetype = "text/html" +default_charset = "UTF-8" +doc_root = +user_dir = +enable_dl = Off +file_uploads = On +upload_max_filesize = 100M +max_file_uploads = 20 +allow_url_fopen = On +allow_url_include = Off +default_socket_timeout = 60 +session.save_path = "{{SESSION_DIR}}" +upload_tmp_dir = "{{TEMP_DIR}}" +sys_temp_dir = "{{TEMP_DIR}}" +extension_dir = "{{EXT_DIR}}" +fastcgi.impersonate = 1 +cgi.force_redirect = 0 +cgi.fix_pathinfo = 0 + +[ExtensionList] +{{EXTENSIONS}} + +[Date] +date.timezone = UTC diff --git a/service_events.json b/service_events.json index 2752594..8f0988e 100644 --- a/service_events.json +++ b/service_events.json @@ -1,4 +1,317 @@ [ + { + "service": "php:5.6.40", + "action": "start", + "status": "success", + "id": "200rgoskm", + "timestamp": 1775189179480 + }, + { + "service": "php:7.4.33", + "action": "start", + "status": "success", + "id": "lb7cc11oe", + "timestamp": 1775189179463 + }, + { + "service": "php:8.2.30", + "action": "start", + "status": "success", + "id": "etjx8kz61", + "timestamp": 1775189179447 + }, + { + "service": "php:8.5.4", + "action": "start", + "status": "success", + "id": "jw31owy76", + "timestamp": 1775189179430 + }, + { + "service": "mariadb:10.11.8", + "action": "start", + "status": "success", + "id": "905twpheo", + "timestamp": 1775189179167 + }, + { + "service": "mariadb:11.4.2", + "action": "start", + "status": "success", + "id": "3cpe3886a", + "timestamp": 1775189179146 + }, + { + "service": "php:7.4.33", + "action": "exit", + "status": "failure", + "details": "Exit code: null", + "id": "1puz4ewk4", + "timestamp": 1775189136125 + }, + { + "service": "php:7.4.33", + "action": "stop", + "status": "info", + "id": "cp1skav2y", + "timestamp": 1775189136113 + }, + { + "service": "php:7.4.33", + "action": "start", + "status": "success", + "id": "5ssshkepi", + "timestamp": 1775189128458 + }, + { + "service": "nginx", + "action": "start", + "status": "success", + "id": "2w0dgrom5", + "timestamp": 1775189121191 + }, + { + "service": "apache", + "action": "start", + "status": "success", + "id": "ce020djd5", + "timestamp": 1775189116024 + }, + { + "service": "mariadb:11.4.2", + "action": "exit", + "status": "failure", + "details": "Exit code: null", + "id": "f22mfjz6j", + "timestamp": 1775189109611 + }, + { + "service": "mariadb:10.11.8", + "action": "exit", + "status": "failure", + "details": "Exit code: null", + "id": "bj1yhdd0b", + "timestamp": 1775189109603 + }, + { + "service": "php:5.6.40", + "action": "exit", + "status": "failure", + "details": "Exit code: null", + "id": "dwo60nc99", + "timestamp": 1775189109591 + }, + { + "service": "php:7.4.33", + "action": "exit", + "status": "failure", + "details": "Exit code: null", + "id": "vc72hwpnw", + "timestamp": 1775189109584 + }, + { + "service": "php:8.2.30", + "action": "exit", + "status": "failure", + "details": "Exit code: null", + "id": "dmk8j6iad", + "timestamp": 1775189109577 + }, + { + "service": "php:8.5.4", + "action": "exit", + "status": "failure", + "details": "Exit code: null", + "id": "ck9ctfdg9", + "timestamp": 1775189109571 + }, + { + "service": "apache", + "action": "exit", + "status": "failure", + "details": "Exit code: null", + "id": "f4qs2eaz5", + "timestamp": 1775189109550 + }, + { + "service": "nginx", + "action": "exit", + "status": "failure", + "details": "Exit code: null", + "id": "m1fkgarlg", + "timestamp": 1775189109547 + }, + { + "service": "mariadb:11.4.2", + "action": "stop", + "status": "info", + "id": "1cjzxt5i8", + "timestamp": 1775189109537 + }, + { + "service": "mariadb:10.11.8", + "action": "stop", + "status": "info", + "id": "ss8vlatqg", + "timestamp": 1775189109529 + }, + { + "service": "php:5.6.40", + "action": "stop", + "status": "info", + "id": "38on99hkk", + "timestamp": 1775189109520 + }, + { + "service": "php:7.4.33", + "action": "stop", + "status": "info", + "id": "qinl5mvi9", + "timestamp": 1775189109513 + }, + { + "service": "php:8.2.30", + "action": "stop", + "status": "info", + "id": "tc2wax0xc", + "timestamp": 1775189109502 + }, + { + "service": "php:8.5.4", + "action": "stop", + "status": "info", + "id": "vlawi3k8e", + "timestamp": 1775189109494 + }, + { + "service": "apache", + "action": "stop", + "status": "info", + "id": "6ujdvt7j1", + "timestamp": 1775189109479 + }, + { + "service": "nginx", + "action": "stop", + "status": "info", + "id": "k7rezgoaj", + "timestamp": 1775189109477 + }, + { + "service": "apache", + "action": "start", + "status": "success", + "id": "70tu2y5as", + "timestamp": 1775189102787 + }, + { + "service": "apache", + "action": "exit", + "status": "failure", + "details": "Exit code: 1", + "id": "9lfnqbgwe", + "timestamp": 1775189101318 + }, + { + "service": "php:5.6.40", + "action": "start", + "status": "success", + "id": "wgqvhqwe2", + "timestamp": 1775189054380 + }, + { + "service": "php:7.4.33", + "action": "start", + "status": "success", + "id": "cz9laoefl", + "timestamp": 1775189054362 + }, + { + "service": "php:8.2.30", + "action": "start", + "status": "success", + "id": "81z3uz9zw", + "timestamp": 1775189054344 + }, + { + "service": "php:8.5.4", + "action": "start", + "status": "success", + "id": "0hegp84kv", + "timestamp": 1775189054324 + }, + { + "service": "mariadb:11.4.2", + "action": "start", + "status": "success", + "id": "r2fm405gd", + "timestamp": 1775189054117 + }, + { + "service": "mariadb:10.11.8", + "action": "start", + "status": "success", + "id": "knua7kh8u", + "timestamp": 1775189054097 + }, + { + "service": "apache", + "action": "start", + "status": "success", + "id": "l4paq57hz", + "timestamp": 1775189053345 + }, + { + "service": "nginx", + "action": "start", + "status": "success", + "id": "khg59iy1d", + "timestamp": 1775189053276 + }, + { + "service": "php:7.4.33", + "action": "start", + "status": "success", + "id": "hg9972rds", + "timestamp": 1775188622685 + }, + { + "service": "php:7.4.33", + "action": "exit", + "status": "failure", + "details": "Exit code: null", + "id": "1knv5xq4h", + "timestamp": 1775188618390 + }, + { + "service": "php:7.4.33", + "action": "stop", + "status": "info", + "id": "7132k0rd3", + "timestamp": 1775188618378 + }, + { + "service": "php:7.4.33", + "action": "start", + "status": "success", + "id": "j8dmzg0qj", + "timestamp": 1775188599983 + }, + { + "service": "php:7.4.33", + "action": "exit", + "status": "failure", + "details": "Exit code: null", + "id": "o26b3cigi", + "timestamp": 1775188594701 + }, + { + "service": "php:7.4.33", + "action": "stop", + "status": "info", + "id": "ox74ttj4y", + "timestamp": 1775188594690 + }, { "service": "php:5.6.40", "action": "start", diff --git a/src/main/ipc/index.ts b/src/main/ipc/index.ts index 009955d..6572bd1 100644 --- a/src/main/ipc/index.ts +++ b/src/main/ipc/index.ts @@ -258,6 +258,7 @@ async function rebuildApacheConfig(forceProjects: boolean = false): Promise<{ bi if (!normalizedPath.endsWith('/')) normalizedPath += '/' const phpDir = path.join(configService.getBasePath(), 'bin', `php-${p.phpVersion}`) + const phpIniDir = path.join(configService.getBasePath(), 'generated_configs', 'php', p.phpVersion) const phpCgiBinary = findExecutable(phpDir, process.platform === 'win32' ? 'php-cgi.exe' : 'php-cgi') || '' // Auto-suffix hostname if it's a simple slug @@ -268,6 +269,7 @@ async function rebuildApacheConfig(forceProjects: boolean = false): Promise<{ bi PATH: normalizedPath, PHP_PORT: phpPort.toString(), PHP_DIR: phpDir.replace(/\\/g, '/'), + PHP_INI_DIR: phpIniDir.replace(/\\/g, '/'), PHP_CGI_PATH: phpCgiBinary.replace(/\\/g, '/'), PHP_BIN_URL: `/php-bin-${p.host}`, PORT: settings.apachePort || '8080' @@ -279,6 +281,7 @@ async function rebuildApacheConfig(forceProjects: boolean = false): Promise<{ bi PATH: normalizedPath, PHP_PORT: phpPort.toString(), PHP_DIR: phpDir.replace(/\\/g, '/'), + PHP_INI_DIR: phpIniDir.replace(/\\/g, '/'), PHP_CGI_PATH: phpCgiBinary.replace(/\\/g, '/'), PHP_BIN_URL: `/php-bin-alias-${p.host}`, PORT: settings.apachePort || '8080' diff --git a/src/renderer/src/components/Dashboard/DashboardDetails.tsx b/src/renderer/src/components/Dashboard/DashboardDetails.tsx index 3bb84c6..bfea0d8 100644 --- a/src/renderer/src/components/Dashboard/DashboardDetails.tsx +++ b/src/renderer/src/components/Dashboard/DashboardDetails.tsx @@ -202,6 +202,9 @@ export default function DashboardDetails({ return 'n/a'; }; const isRunning = currentStatus.status === 'running'; + const isStarting = currentStatus.status === 'starting'; + const isStopping = currentStatus.status === 'stopping'; + const isTransitioning = isStarting || isStopping; const serviceProjects = projects.filter(p => { if (serviceId === 'nginx') return !p.serverType || p.serverType === 'nginx'; @@ -244,11 +247,14 @@ export default function DashboardDetails({ {serviceId.toUpperCase()} - {isRunning ? t('common.running', 'Çalışıyor') : t('common.stopped', 'Durduruldu')} + {isStarting && {t('common.starting', 'Başlatılıyor...')}} + {isStopping && {t('common.stopping', 'Durduruluyor...')}} + {isRunning && {t('common.running', 'Çalışıyor')}} + {!isRunning && !isStarting && !isStopping && {t('common.stopped', 'Durduruldu')}} - {isRunning && ( + {isRunning && !isTransitioning && ( onReloadService(serviceId)} sx={{ bgcolor: 'rgba(57, 167, 255, 0.1)', color: 'primary.main' }}> @@ -256,11 +262,16 @@ export default function DashboardDetails({ diff --git a/src/renderer/src/components/Dashboard/DashboardManager.tsx b/src/renderer/src/components/Dashboard/DashboardManager.tsx index a72d6fe..4349687 100644 --- a/src/renderer/src/components/Dashboard/DashboardManager.tsx +++ b/src/renderer/src/components/Dashboard/DashboardManager.tsx @@ -26,29 +26,51 @@ export default function DashboardManager({ onSelectProject }: DashboardManagerProps) { const [selectedServiceId, setSelectedServiceId] = useState('nginx'); + const [isToggling, setIsToggling] = useState(null); + const [toggledStates, setToggledStates] = useState>({}); const handleToggleService = async (service: string) => { if (!window.api) return; + if (isToggling) return; const currentStatus = status[service]?.status || 'stopped'; const isRunning = currentStatus === 'running'; + + setIsToggling(service); + // Optimistically set transition state if not already there + setToggledStates(prev => ({ ...prev, [service]: isRunning ? 'stopping' : 'starting' })); - const result = isRunning - ? await window.api.stopService(service) - : await window.api.startService(service); + try { + const result = isRunning + ? await window.api.stopService(service) + : await window.api.startService(service); - if (result && !result.success) { - Swal.fire({ - title: t('common.error'), - text: result.message, - icon: 'error', - background: '#1e1e1e', - color: '#fff' + if (result && !result.success) { + Swal.fire({ + title: t('common.error'), + text: result.message, + icon: 'error', + background: '#1e1e1e', + color: '#fff' + }); + } + } finally { + setIsToggling(null); + setToggledStates(prev => { + const updated = { ...prev }; + delete updated[service]; + return updated; }); + onFetchStatus(); } - onFetchStatus(); }; + // Merge actual status with our local transition states + const mergedStatus = { ...status }; + Object.keys(toggledStates).forEach(service => { + mergedStatus[service] = { ...mergedStatus[service], status: toggledStates[service] }; + }); + const handleReloadService = async (service: string) => { if (!window.api) return; const result = await window.api.invoke('services:reload', service); @@ -67,7 +89,7 @@ export default function DashboardManager({ return ( {isStarting && }