From 2c2399a157549737a0e0a5aba630c78651151fcc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=9Cmit=20Tun=C3=A7?= Date: Thu, 2 Apr 2026 22:40:32 +0300 Subject: [PATCH] feat: implement process management service with IPC and configuration templates --- config/nginx.conf.template | 11 +- config/php.ini.template | 2 +- service_events.json | 1263 +++++++++++++++++ settings.json | 2 +- src/main/ipc/index.ts | 130 +- src/main/services/ConfigService.ts | 5 + src/main/services/ProcessManager.ts | 117 +- .../ProjectManager/ProjectDetails.tsx | 4 +- 8 files changed, 1447 insertions(+), 87 deletions(-) diff --git a/config/nginx.conf.template b/config/nginx.conf.template index 1e0becd..ceed38e 100644 --- a/config/nginx.conf.template +++ b/config/nginx.conf.template @@ -9,14 +9,14 @@ http { default_type application/octet-stream; sendfile on; keepalive_timeout 65; - server_names_hash_bucket_size 64; + + {{PROJECTS}} server { listen {{LISTEN_ADDRESS}}:{{PORT}}; server_name localhost; root "{{ROOT}}"; index index.php index.html index.htm; - autoindex on; location / { # Try global root first, fallback to @project_fallback if not found @@ -37,10 +37,8 @@ http { } return 404; } - - # Subdirectory access for projects (legacy support) - {{PROJECT_LOCATIONS}} + {{PROJECT_LOCATIONS}} location /phpmyadmin { alias "{{PHPMYADMIN_DIR}}"; @@ -60,8 +58,7 @@ http { fastcgi_pass 127.0.0.1:{{PHP_PORT}}; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $request_filename; + include "{{CONF_DIR}}/fastcgi_params"; } } - - {{PROJECTS}} } diff --git a/config/php.ini.template b/config/php.ini.template index 37b9f14..8aabfde 100644 --- a/config/php.ini.template +++ b/config/php.ini.template @@ -47,7 +47,7 @@ sys_temp_dir = "{{TEMP_DIR}}" extension_dir = "{{EXT_DIR}}" fastcgi.impersonate = 1 cgi.force_redirect = 0 -cgi.fix_pathinfo = 1 +cgi.fix_pathinfo = 0 [ExtensionList] {{EXTENSIONS}} diff --git a/service_events.json b/service_events.json index b9f854f..2dc608a 100644 --- a/service_events.json +++ b/service_events.json @@ -1,4 +1,1267 @@ [ + { + "service": "nginx", + "action": "start", + "status": "success", + "id": "ouizljgr0", + "timestamp": 1775155527854 + }, + { + "service": "nginx", + "action": "exit", + "status": "failure", + "details": "Exit code: 1", + "id": "b2pyx2lpm", + "timestamp": 1775155526493 + }, + { + "service": "mariadb:11.4.2", + "action": "start", + "status": "success", + "id": "smaqu001g", + "timestamp": 1775155500343 + }, + { + "service": "php:5.6.40", + "action": "start", + "status": "success", + "id": "zpz9v2lub", + "timestamp": 1775155496313 + }, + { + "service": "php:7.4.33", + "action": "start", + "status": "success", + "id": "4n0iz4896", + "timestamp": 1775155495423 + }, + { + "service": "php:8.2.30", + "action": "start", + "status": "success", + "id": "bl0mexdwk", + "timestamp": 1775155494877 + }, + { + "service": "php:8.5.4", + "action": "start", + "status": "success", + "id": "a773np6ap", + "timestamp": 1775155490887 + }, + { + "service": "nginx", + "action": "start", + "status": "success", + "id": "56vax2l99", + "timestamp": 1775155419891 + }, + { + "service": "nginx", + "action": "exit", + "status": "failure", + "details": "Exit code: 1", + "id": "t89dlfzt2", + "timestamp": 1775155287573 + }, + { + "service": "nginx", + "action": "start", + "status": "success", + "id": "5ucky9bwr", + "timestamp": 1775155287542 + }, + { + "service": "nginx", + "action": "exit", + "status": "failure", + "details": "Exit code: 1", + "id": "ilsplkl76", + "timestamp": 1775155280838 + }, + { + "service": "nginx", + "action": "start", + "status": "success", + "id": "t0kbwc3fw", + "timestamp": 1775155280802 + }, + { + "service": "nginx", + "action": "exit", + "status": "failure", + "details": "Exit code: 1", + "id": "waak7v6zz", + "timestamp": 1775155279429 + }, + { + "service": "php:5.6.40", + "action": "start", + "status": "success", + "id": "28tux089q", + "timestamp": 1775155218293 + }, + { + "service": "php:7.4.33", + "action": "start", + "status": "success", + "id": "n2lkf6sgx", + "timestamp": 1775155218277 + }, + { + "service": "php:8.2.30", + "action": "start", + "status": "success", + "id": "hwsai7pn9", + "timestamp": 1775155218259 + }, + { + "service": "mariadb:11.4.2", + "action": "start", + "status": "success", + "id": "c1seiuu2n", + "timestamp": 1775155218008 + }, + { + "service": "mariadb:10.11.8", + "action": "start", + "status": "success", + "id": "6pjzk3z8n", + "timestamp": 1775155217988 + }, + { + "service": "apache", + "action": "start", + "status": "success", + "id": "b8pxrxfmi", + "timestamp": 1775155217277 + }, + { + "service": "nginx", + "action": "start", + "status": "success", + "id": "85rtcc4sm", + "timestamp": 1775155217217 + }, + { + "service": "php:8.5.4", + "action": "start", + "status": "success", + "id": "m7y6o8sf3", + "timestamp": 1775155213374 + }, + { + "service": "mariadb:10.11.8", + "action": "start", + "status": "success", + "id": "qva7ayywm", + "timestamp": 1775155156517 + }, + { + "service": "mariadb:11.4.2", + "action": "start", + "status": "success", + "id": "arfrdhszx", + "timestamp": 1775155156449 + }, + { + "service": "apache", + "action": "start", + "status": "success", + "id": "1w47knyvr", + "timestamp": 1775155155731 + }, + { + "service": "nginx", + "action": "start", + "status": "success", + "id": "hb68l0woi", + "timestamp": 1775155155655 + }, + { + "service": "mariadb:11.4.2", + "action": "exit", + "status": "failure", + "details": "Exit code: null", + "id": "freuq6ewu", + "timestamp": 1775155146039 + }, + { + "service": "php:5.6.40", + "action": "exit", + "status": "failure", + "details": "Exit code: null", + "id": "enb2bhqyz", + "timestamp": 1775155146027 + }, + { + "service": "php:7.4.33", + "action": "exit", + "status": "failure", + "details": "Exit code: null", + "id": "iz5oorqz5", + "timestamp": 1775155146026 + }, + { + "service": "apache", + "action": "exit", + "status": "failure", + "details": "Exit code: null", + "id": "d1uxc8txm", + "timestamp": 1775155146009 + }, + { + "service": "nginx", + "action": "exit", + "status": "failure", + "details": "Exit code: null", + "id": "mrvsnu2d8", + "timestamp": 1775155146008 + }, + { + "service": "mariadb:11.4.2", + "action": "stop", + "status": "info", + "id": "c5pt8auo2", + "timestamp": 1775155146000 + }, + { + "service": "php:5.6.40", + "action": "stop", + "status": "info", + "id": "w3uhowg36", + "timestamp": 1775155145982 + }, + { + "service": "php:7.4.33", + "action": "stop", + "status": "info", + "id": "qcpn9tp8h", + "timestamp": 1775155145975 + }, + { + "service": "apache", + "action": "stop", + "status": "info", + "id": "l4ynwfv9f", + "timestamp": 1775155145966 + }, + { + "service": "nginx", + "action": "stop", + "status": "info", + "id": "qndsbmbyx", + "timestamp": 1775155145961 + }, + { + "service": "mariadb:11.4.2", + "action": "start", + "status": "success", + "id": "sx9yfjw8e", + "timestamp": 1775155024067 + }, + { + "service": "apache", + "action": "start", + "status": "success", + "id": "n1pvokucz", + "timestamp": 1775155004356 + }, + { + "service": "apache", + "action": "exit", + "status": "failure", + "details": "Exit code: 1", + "id": "q50nmm4bw", + "timestamp": 1775155002950 + }, + { + "service": "apache", + "action": "start", + "status": "success", + "id": "bhc2krbt9", + "timestamp": 1775154947478 + }, + { + "service": "apache", + "action": "exit", + "status": "failure", + "details": "Exit code: 1", + "id": "hb2c0uqx4", + "timestamp": 1775154934403 + }, + { + "service": "apache", + "action": "start", + "status": "success", + "id": "uylkoniru", + "timestamp": 1775154934209 + }, + { + "service": "apache", + "action": "exit", + "status": "failure", + "details": "Exit code: 1", + "id": "zcppbeewr", + "timestamp": 1775154809759 + }, + { + "service": "apache", + "action": "start", + "status": "success", + "id": "2d07eo0yo", + "timestamp": 1775154809556 + }, + { + "service": "apache", + "action": "exit", + "status": "failure", + "details": "Exit code: 1", + "id": "6izx0l5hi", + "timestamp": 1775154808136 + }, + { + "service": "apache", + "action": "start", + "status": "success", + "id": "zhosihtns", + "timestamp": 1775154770741 + }, + { + "service": "apache", + "action": "exit", + "status": "failure", + "details": "Exit code: 1", + "id": "9gjzmvy4q", + "timestamp": 1775154770712 + }, + { + "service": "apache", + "action": "start", + "status": "success", + "id": "uktpqify3", + "timestamp": 1775154766487 + }, + { + "service": "apache", + "action": "exit", + "status": "failure", + "details": "Exit code: 1", + "id": "pe3rb0rog", + "timestamp": 1775154765209 + }, + { + "service": "apache", + "action": "start", + "status": "success", + "id": "abcbphrdl", + "timestamp": 1775154711240 + }, + { + "service": "apache", + "action": "exit", + "status": "failure", + "details": "Exit code: 1", + "id": "c87x6z8se", + "timestamp": 1775154709898 + }, + { + "service": "php:5.6.40", + "action": "start", + "status": "success", + "id": "ld2848yr8", + "timestamp": 1775154697002 + }, + { + "service": "apache", + "action": "start", + "status": "success", + "id": "b8io0rfgo", + "timestamp": 1775154695762 + }, + { + "service": "php:5.6.40", + "action": "exit", + "status": "failure", + "details": "Exit code: null", + "id": "2nnr2scwk", + "timestamp": 1775154692914 + }, + { + "service": "php:5.6.40", + "action": "stop", + "status": "info", + "id": "reqse07y2", + "timestamp": 1775154692904 + }, + { + "service": "apache", + "action": "exit", + "status": "failure", + "details": "Exit code: null", + "id": "xj3h7a4hn", + "timestamp": 1775154692514 + }, + { + "service": "apache", + "action": "stop", + "status": "info", + "id": "0flezbijv", + "timestamp": 1775154692508 + }, + { + "service": "php:7.4.33", + "action": "start", + "status": "success", + "id": "63g1t55lf", + "timestamp": 1775154679591 + }, + { + "service": "nginx", + "action": "start", + "status": "success", + "id": "rtlhds1my", + "timestamp": 1775154678548 + }, + { + "service": "php:7.4.33", + "action": "exit", + "status": "failure", + "details": "Exit code: null", + "id": "v1xbmxlc5", + "timestamp": 1775154675596 + }, + { + "service": "php:7.4.33", + "action": "stop", + "status": "info", + "id": "if7fc7bvy", + "timestamp": 1775154675585 + }, + { + "service": "nginx", + "action": "exit", + "status": "failure", + "details": "Exit code: null", + "id": "yib4do8o3", + "timestamp": 1775154675194 + }, + { + "service": "nginx", + "action": "stop", + "status": "info", + "id": "yyogdsjnu", + "timestamp": 1775154675191 + }, + { + "service": "php:7.4.33", + "action": "start", + "status": "success", + "id": "9fes91bmy", + "timestamp": 1775154592532 + }, + { + "service": "nginx", + "action": "start", + "status": "success", + "id": "34y6lz875", + "timestamp": 1775154591504 + }, + { + "service": "mariadb:11.4.2", + "action": "exit", + "status": "failure", + "details": "Exit code: 1", + "id": "sjmiw449c", + "timestamp": 1775154589087 + }, + { + "service": "php:7.4.33", + "action": "exit", + "status": "failure", + "details": "Exit code: null", + "id": "ccbjv4qer", + "timestamp": 1775154588807 + }, + { + "service": "php:7.4.33", + "action": "stop", + "status": "info", + "id": "gcn5gc1lc", + "timestamp": 1775154588796 + }, + { + "service": "nginx", + "action": "exit", + "status": "failure", + "details": "Exit code: null", + "id": "tltmsmd3s", + "timestamp": 1775154588488 + }, + { + "service": "nginx", + "action": "stop", + "status": "info", + "id": "otnml26ey", + "timestamp": 1775154588484 + }, + { + "service": "nginx", + "action": "start", + "status": "success", + "id": "4zeazxzyq", + "timestamp": 1775154561793 + }, + { + "service": "nginx", + "action": "exit", + "status": "failure", + "details": "Exit code: 1", + "id": "2f6e7fppl", + "timestamp": 1775154560519 + }, + { + "service": "php:7.4.33", + "action": "start", + "status": "success", + "id": "w7fubg188", + "timestamp": 1775154544556 + }, + { + "service": "mariadb:11.4.2", + "action": "start", + "status": "success", + "id": "32647sre1", + "timestamp": 1775154444902 + }, + { + "service": "apache", + "action": "start", + "status": "success", + "id": "cg2x7l6jy", + "timestamp": 1775154403472 + }, + { + "service": "apache", + "action": "exit", + "status": "failure", + "details": "Exit code: 1", + "id": "nsvk6zlls", + "timestamp": 1775154403444 + }, + { + "service": "apache", + "action": "start", + "status": "success", + "id": "r8azwetx3", + "timestamp": 1775154401009 + }, + { + "service": "nginx", + "action": "start", + "status": "success", + "id": "7itd9xbh6", + "timestamp": 1775154399741 + }, + { + "service": "php:5.6.40", + "action": "start", + "status": "success", + "id": "7vzjhx2zs", + "timestamp": 1775154393864 + }, + { + "service": "php:5.6.40", + "action": "start", + "status": "success", + "id": "gyiagwh03", + "timestamp": 1775154259800 + }, + { + "service": "mariadb:11.4.2", + "action": "exit", + "status": "failure", + "details": "Exit code: 1", + "id": "frdmk9t51", + "timestamp": 1775154253934 + }, + { + "service": "mariadb:10.11.8", + "action": "exit", + "status": "failure", + "details": "Exit code: 1", + "id": "bulj3s4xa", + "timestamp": 1775154253926 + }, + { + "service": "php:5.6.40", + "action": "exit", + "status": "failure", + "details": "Exit code: null", + "id": "5qc9b9kgi", + "timestamp": 1775154253664 + }, + { + "service": "php:5.6.40", + "action": "stop", + "status": "info", + "id": "tcm0bvwf2", + "timestamp": 1775154253652 + }, + { + "service": "nginx", + "action": "exit", + "status": "failure", + "details": "Exit code: 1", + "id": "ixaia08dz", + "timestamp": 1775153349207 + }, + { + "service": "nginx", + "action": "start", + "status": "success", + "id": "3bamb2934", + "timestamp": 1775153349174 + }, + { + "service": "nginx", + "action": "exit", + "status": "failure", + "details": "Exit code: 1", + "id": "iggmbzl7u", + "timestamp": 1775153315379 + }, + { + "service": "nginx", + "action": "start", + "status": "success", + "id": "72e1mpuy9", + "timestamp": 1775153315347 + }, + { + "service": "nginx", + "action": "exit", + "status": "failure", + "details": "Exit code: 1", + "id": "x2wc6lhtf", + "timestamp": 1775153313197 + }, + { + "service": "nginx", + "action": "start", + "status": "success", + "id": "6zfr6unww", + "timestamp": 1775153313172 + }, + { + "service": "nginx", + "action": "exit", + "status": "failure", + "details": "Exit code: 1", + "id": "sngklp5dd", + "timestamp": 1775153297836 + }, + { + "service": "nginx", + "action": "start", + "status": "success", + "id": "p4sbzdpod", + "timestamp": 1775153297805 + }, + { + "service": "php:5.6.40", + "action": "start", + "status": "success", + "id": "1c2y623vq", + "timestamp": 1775153288955 + }, + { + "service": "php:7.4.33", + "action": "start", + "status": "success", + "id": "m5mbgqz35", + "timestamp": 1775153288935 + }, + { + "service": "php:8.2.30", + "action": "start", + "status": "success", + "id": "z323utarh", + "timestamp": 1775153288916 + }, + { + "service": "php:8.5.4", + "action": "start", + "status": "success", + "id": "ny5z1eebh", + "timestamp": 1775153288898 + }, + { + "service": "mariadb:10.11.8", + "action": "start", + "status": "success", + "id": "cp4xs1234", + "timestamp": 1775153288585 + }, + { + "service": "mariadb:11.4.2", + "action": "start", + "status": "success", + "id": "4bbii1n37", + "timestamp": 1775153288565 + }, + { + "service": "apache", + "action": "start", + "status": "success", + "id": "krno407dx", + "timestamp": 1775153287907 + }, + { + "service": "nginx", + "action": "exit", + "status": "failure", + "details": "Exit code: 1", + "id": "uk3n336wm", + "timestamp": 1775153287898 + }, + { + "service": "nginx", + "action": "start", + "status": "success", + "id": "h00s8h0pv", + "timestamp": 1775153287843 + }, + { + "service": "php:7.4.33", + "action": "start", + "status": "success", + "id": "an5cx4vra", + "timestamp": 1775153261897 + }, + { + "service": "mariadb:11.4.2", + "action": "start", + "status": "success", + "id": "jgadld40c", + "timestamp": 1775153251364 + }, + { + "service": "mariadb:11.4.2", + "action": "exit", + "status": "failure", + "details": "Exit code: 1", + "id": "jp3x8zu8j", + "timestamp": 1775153251322 + }, + { + "service": "mariadb:11.4.2", + "action": "start", + "status": "success", + "id": "sdv78j7zh", + "timestamp": 1775153247602 + }, + { + "service": "php:8.5.4", + "action": "start", + "status": "success", + "id": "wbr76wp0j", + "timestamp": 1775153245628 + }, + { + "service": "nginx", + "action": "start", + "status": "success", + "id": "l3e26n5pc", + "timestamp": 1775153236669 + }, + { + "service": "php:7.4.33", + "action": "start", + "status": "success", + "id": "8wlbn3nn8", + "timestamp": 1775152932306 + }, + { + "service": "nginx", + "action": "start", + "status": "success", + "id": "txgvdyr32", + "timestamp": 1775152931270 + }, + { + "service": "mariadb:10.11.8", + "action": "exit", + "status": "failure", + "details": "Exit code: null", + "id": "yoxeiqytx", + "timestamp": 1775152918102 + }, + { + "service": "mariadb:11.4.2", + "action": "exit", + "status": "failure", + "details": "Exit code: null", + "id": "81oxhofbf", + "timestamp": 1775152918094 + }, + { + "service": "php:5.6.40", + "action": "exit", + "status": "failure", + "details": "Exit code: null", + "id": "f9y9x8ept", + "timestamp": 1775152918085 + }, + { + "service": "php:7.4.33", + "action": "exit", + "status": "failure", + "details": "Exit code: null", + "id": "elo9j5baw", + "timestamp": 1775152918076 + }, + { + "service": "php:8.2.30", + "action": "exit", + "status": "failure", + "details": "Exit code: null", + "id": "8kth4get3", + "timestamp": 1775152918065 + }, + { + "service": "php:8.5.4", + "action": "exit", + "status": "failure", + "details": "Exit code: null", + "id": "wr7n5nmok", + "timestamp": 1775152918057 + }, + { + "service": "apache", + "action": "exit", + "status": "failure", + "details": "Exit code: null", + "id": "a1af6i1im", + "timestamp": 1775152918017 + }, + { + "service": "nginx", + "action": "exit", + "status": "failure", + "details": "Exit code: null", + "id": "yuwawc3o1", + "timestamp": 1775152918012 + }, + { + "service": "mariadb:10.11.8", + "action": "stop", + "status": "info", + "id": "xke25ggqq", + "timestamp": 1775152918005 + }, + { + "service": "mariadb:11.4.2", + "action": "stop", + "status": "info", + "id": "zgsnydc4f", + "timestamp": 1775152917993 + }, + { + "service": "php:5.6.40", + "action": "stop", + "status": "info", + "id": "0eljhazeo", + "timestamp": 1775152917980 + }, + { + "service": "php:7.4.33", + "action": "stop", + "status": "info", + "id": "vb7hko6nc", + "timestamp": 1775152917967 + }, + { + "service": "php:8.2.30", + "action": "stop", + "status": "info", + "id": "evujln438", + "timestamp": 1775152917955 + }, + { + "service": "php:8.5.4", + "action": "stop", + "status": "info", + "id": "7jb1ugudm", + "timestamp": 1775152917942 + }, + { + "service": "apache", + "action": "stop", + "status": "info", + "id": "y1vjjygry", + "timestamp": 1775152917928 + }, + { + "service": "nginx", + "action": "stop", + "status": "info", + "id": "mdog1j9g0", + "timestamp": 1775152917922 + }, + { + "service": "nginx", + "action": "start", + "status": "success", + "id": "57rpliyyl", + "timestamp": 1775152891858 + }, + { + "service": "nginx", + "action": "exit", + "status": "failure", + "details": "Exit code: 1", + "id": "fwepv3n5v", + "timestamp": 1775152890494 + }, + { + "service": "nginx", + "action": "start", + "status": "success", + "id": "y0n2gkh3c", + "timestamp": 1775152813648 + }, + { + "service": "nginx", + "action": "exit", + "status": "failure", + "details": "Exit code: 1", + "id": "aaqoukiau", + "timestamp": 1775152799636 + }, + { + "service": "nginx", + "action": "start", + "status": "success", + "id": "fpwa4wbzw", + "timestamp": 1775152799594 + }, + { + "service": "nginx", + "action": "exit", + "status": "failure", + "details": "Exit code: 1", + "id": "j3admqzeq", + "timestamp": 1775152797686 + }, + { + "service": "nginx", + "action": "start", + "status": "success", + "id": "c5e7bmu7h", + "timestamp": 1775152797649 + }, + { + "service": "nginx", + "action": "exit", + "status": "failure", + "details": "Exit code: 1", + "id": "tkvm038u3", + "timestamp": 1775152795115 + }, + { + "service": "nginx", + "action": "start", + "status": "success", + "id": "ddsjldlcw", + "timestamp": 1775152795083 + }, + { + "service": "nginx", + "action": "exit", + "status": "failure", + "details": "Exit code: null", + "id": "qrjk5c3no", + "timestamp": 1775152789325 + }, + { + "service": "nginx", + "action": "stop", + "status": "info", + "id": "c7rhccov1", + "timestamp": 1775152789320 + }, + { + "service": "php:5.6.40", + "action": "start", + "status": "success", + "id": "e6d25ecx2", + "timestamp": 1775152715861 + }, + { + "service": "php:7.4.33", + "action": "start", + "status": "success", + "id": "l5grlqv85", + "timestamp": 1775152715841 + }, + { + "service": "php:8.2.30", + "action": "start", + "status": "success", + "id": "9bmv8obs5", + "timestamp": 1775152715821 + }, + { + "service": "php:8.5.4", + "action": "start", + "status": "success", + "id": "esuing08c", + "timestamp": 1775152715800 + }, + { + "service": "mariadb:10.11.8", + "action": "start", + "status": "success", + "id": "09e2bcuhl", + "timestamp": 1775152715519 + }, + { + "service": "mariadb:11.4.2", + "action": "start", + "status": "success", + "id": "pdgupm44n", + "timestamp": 1775152715499 + }, + { + "service": "apache", + "action": "start", + "status": "success", + "id": "9kklgi5wn", + "timestamp": 1775152714806 + }, + { + "service": "nginx", + "action": "start", + "status": "success", + "id": "nr8hhx1rt", + "timestamp": 1775152711806 + }, + { + "service": "nginx", + "action": "exit", + "status": "failure", + "details": "Exit code: 1", + "id": "5w2f036q3", + "timestamp": 1775152630664 + }, + { + "service": "nginx", + "action": "start", + "status": "success", + "id": "s2rq1lqry", + "timestamp": 1775152630632 + }, + { + "service": "nginx", + "action": "exit", + "status": "failure", + "details": "Exit code: 1", + "id": "p5wkzw03w", + "timestamp": 1775152600944 + }, + { + "service": "nginx", + "action": "start", + "status": "success", + "id": "h5f3b0qq1", + "timestamp": 1775152600911 + }, + { + "service": "nginx", + "action": "exit", + "status": "failure", + "details": "Exit code: 1", + "id": "d9mxqoj6j", + "timestamp": 1775152528601 + }, + { + "service": "nginx", + "action": "start", + "status": "success", + "id": "iq7mz7s1q", + "timestamp": 1775152528568 + }, + { + "service": "nginx", + "action": "exit", + "status": "failure", + "details": "Exit code: 1", + "id": "18sq7zfxo", + "timestamp": 1775152527367 + }, + { + "service": "nginx", + "action": "start", + "status": "success", + "id": "v3l1ni29a", + "timestamp": 1775152527338 + }, + { + "service": "nginx", + "action": "exit", + "status": "failure", + "details": "Exit code: 1", + "id": "el6jgj5jn", + "timestamp": 1775152494772 + }, + { + "service": "nginx", + "action": "start", + "status": "success", + "id": "548gidnat", + "timestamp": 1775152494738 + }, + { + "service": "nginx", + "action": "exit", + "status": "failure", + "details": "Exit code: 1", + "id": "ofjslrhka", + "timestamp": 1775152463177 + }, + { + "service": "nginx", + "action": "start", + "status": "success", + "id": "ibp9lloxe", + "timestamp": 1775152463138 + }, + { + "service": "nginx", + "action": "exit", + "status": "failure", + "details": "Exit code: 1", + "id": "lrhemrfn7", + "timestamp": 1775152461311 + }, + { + "service": "nginx", + "action": "start", + "status": "success", + "id": "dofckrwiz", + "timestamp": 1775152461279 + }, + { + "service": "nginx", + "action": "exit", + "status": "failure", + "details": "Exit code: 1", + "id": "21a1ds5g2", + "timestamp": 1775152449003 + }, + { + "service": "nginx", + "action": "start", + "status": "success", + "id": "ittz05l4q", + "timestamp": 1775152448971 + }, + { + "service": "nginx", + "action": "exit", + "status": "failure", + "details": "Exit code: 1", + "id": "tt4una3ar", + "timestamp": 1775152442558 + }, + { + "service": "nginx", + "action": "start", + "status": "success", + "id": "nyxw8z282", + "timestamp": 1775152442521 + }, + { + "service": "nginx", + "action": "exit", + "status": "failure", + "details": "Exit code: null", + "id": "f0sce7l9d", + "timestamp": 1775152437858 + }, + { + "service": "nginx", + "action": "stop", + "status": "info", + "id": "kawjzexnq", + "timestamp": 1775152437854 + }, + { + "service": "php:5.6.40", + "action": "start", + "status": "success", + "id": "guez0iaiw", + "timestamp": 1775152331908 + }, + { + "service": "php:8.2.30", + "action": "start", + "status": "success", + "id": "wkjhu7zqj", + "timestamp": 1775152330908 + }, + { + "service": "php:8.5.4", + "action": "start", + "status": "success", + "id": "fi49qrxep", + "timestamp": 1775152329515 + }, + { + "service": "nginx", + "action": "start", + "status": "success", + "id": "1m3auddzn", + "timestamp": 1775152325883 + }, + { + "service": "nginx", + "action": "exit", + "status": "failure", + "details": "Exit code: null", + "id": "rmc1odxcu", + "timestamp": 1775152323677 + }, + { + "service": "nginx", + "action": "stop", + "status": "info", + "id": "ezc9rsctb", + "timestamp": 1775152323672 + }, + { + "service": "apache", + "action": "start", + "status": "success", + "id": "j69fkfhqk", + "timestamp": 1775152312841 + }, + { + "service": "apache", + "action": "exit", + "status": "failure", + "details": "Exit code: 1", + "id": "vjbhyetlj", + "timestamp": 1775152311511 + }, + { + "service": "nginx", + "action": "start", + "status": "success", + "id": "m6b9w8uc0", + "timestamp": 1775152311253 + }, { "service": "php:7.4.33", "action": "start", diff --git a/settings.json b/settings.json index 28d22f4..18188ac 100644 --- a/settings.json +++ b/settings.json @@ -1,6 +1,6 @@ { "nginxPort": "8081", - "apachePort": "8080", + "apachePort": "8082", "serverType": "nginx", "phpPort": "9001", "mariaDbPort": "3306", diff --git a/src/main/ipc/index.ts b/src/main/ipc/index.ts index 46fcdf5..5365c2e 100644 --- a/src/main/ipc/index.ts +++ b/src/main/ipc/index.ts @@ -76,7 +76,7 @@ async function rebuildNginxConfig(forceProjects: boolean = false): Promise<{ bin // Ensure directories exist const projectsConfDir = path.join(configService.getBasePath(), 'generated_configs', 'projects') - const locationsConfDir = path.join(configService.getBasePath(), 'generated_configs', 'projects_locations') + const locationsConfDir = path.join(configService.getBasePath(), 'generated_configs', 'projects_nginx') if (!fs.existsSync(projectsConfDir)) fs.mkdirSync(projectsConfDir, { recursive: true }) if (!fs.existsSync(locationsConfDir)) fs.mkdirSync(locationsConfDir, { recursive: true }) @@ -128,8 +128,9 @@ async function rebuildNginxConfig(forceProjects: boolean = false): Promise<{ bin PORT: settings.nginxPort }) - // Also generate the subdirectory/alias-style config for localhost fallback - await configService.generateConfig('nginx_project_location.conf.template', `projects_locations/${p.host}.conf`, { + // Use the correct location-based template for subdirectories + // This prevents the 'server directive not allowed here' error + await configService.generateConfig('nginx_project_location.conf.template', `projects_nginx/${p.host}.conf`, { HOST: p.host, // Use simple host slug for location path PATH: normalizedPath, PHP_PORT: phpPort.toString(), @@ -319,7 +320,7 @@ async function restartApache(): Promise<{ success: boolean; message: string }> { await processManager.stopService('apache') // Force kill port to be absolutely sure await processManager.killByPort('apache', settings.apachePort || '8080') - + await new Promise(resolve => setTimeout(resolve, 1000)) const success = await processManager.startService('apache', binPath, ['-f', configPath], false) @@ -459,6 +460,11 @@ export function registerIpcHandlers(): void { return { success: false, message: `Nginx başlatılamadı.\n\n${errorLogs.join('\n')}` } } const ready = await waitForPort(settings.nginxPort, 5000); + if (ready) { + processManager.setServiceState('nginx', 'running') + } else { + processManager.setServiceState('nginx', 'stopped') + } return { success: ready, message: ready ? 'Nginx başlatıldı.' : 'Nginx başlatıldı ancak port cevabı alınamadı.' } } @@ -473,6 +479,11 @@ export function registerIpcHandlers(): void { return { success: false, message: `Apache başlatılamadı.\n\n${errorLogs.join('\n')}` } } const ready = await waitForPort(parseInt(settings.apachePort || '8080'), 5000); + if (ready) { + processManager.setServiceState('apache', 'running') + } else { + processManager.setServiceState('apache', 'stopped') + } return { success: ready, message: ready ? 'Apache başlatıldı.' : 'Apache başlatıldı ancak port cevabı alınamadı.' } } @@ -536,7 +547,7 @@ export function registerIpcHandlers(): void { extensionsList = mustHaves.map(m => `extension=${prefix}${m}${suffix}`).join('\n') } - const configPath = await configService.generateConfig('php.ini.template', `php_${phpVersion || 'default'}.ini`, { + const configPath = await configService.generateConfig('php.ini.template', `php/${phpVersion || 'default'}/php.ini`, { EXT_DIR: extDir, GD_EXT: gdExtName, SESSION_DIR: sessionDir, @@ -548,6 +559,7 @@ export function registerIpcHandlers(): void { // Add a small delay to ensure previous instance (if any) is fully killed await sleep(1000) + // Set PHPRC to the directory containing the project-specific php.ini const success = await processManager.startService(serviceName, binPath, ['-b', `127.0.0.1:${phpPort}`, '-c', configPath], false, { PHPRC: path.dirname(configPath) }) if (!success) { @@ -556,6 +568,11 @@ export function registerIpcHandlers(): void { } const ready = await waitForPort(phpPort, 5000); + if (ready) { + processManager.setServiceState(serviceName, 'running') + } else { + processManager.setServiceState(serviceName, 'stopped') + } return { success: ready, message: ready ? `${phpVersion || ''} PHP başlatıldı.` : `${phpVersion || ''} PHP başlatıldı ancak port cevabı alınamadı.` } } @@ -635,17 +652,19 @@ export function registerIpcHandlers(): void { if (success) { // Quick health check - await new Promise(r => setTimeout(r, 1500)) - if (!processManager.isServiceRunning(serviceName)) { + const ready = await waitForPort(parseInt(dbPort), 7000); + if (!ready || !processManager.isServiceRunning(serviceName)) { const errorLogs = await processManager.getLastErrorLogs(serviceName) - return { success: false, message: `${dbVersion || ''} MariaDB başlatıldı ancak çalışma hatası verdi (ExitCode: 1).\n\n${errorLogs.length > 0 ? 'Önemli Hata Logları:\n' + errorLogs.join('\n') : 'Lütfen log sekmesini kontrol edin.'}` } + processManager.setServiceState(serviceName, 'stopped') + return { success: false, message: `${dbVersion || ''} MariaDB başlatıldı ancak çalışma hatası verdi.\n\n${errorLogs.length > 0 ? 'Önemli Hata Logları:\n' + errorLogs.join('\n') : 'Lütfen log sekmesini kontrol edin.'}` } } + processManager.setServiceState(serviceName, 'running') + return { success: true, message: `${dbVersion || ''} MariaDB başlatıldı.` } } else { const errorLogs = await processManager.getLastErrorLogs(serviceName) + processManager.setServiceState(serviceName, 'stopped') return { success: false, message: `${dbVersion || ''} MariaDB başlatılamadı.\n\n${errorLogs.join('\n')}` } } - - return { success, message: success ? `${dbVersion || ''} MariaDB başlatıldı.` : `${dbVersion || ''} MariaDB devreye alınamadı.` } } return { success: false, message: 'Bilinmeyen servis.' } @@ -656,8 +675,12 @@ export function registerIpcHandlers(): void { }) ipcMain.handle('services:stop', async (_event, serviceName: string) => { - const success = await processManager.stopService(serviceName) - return { success, message: success ? `${serviceName} durduruldu.` : `${serviceName} zaten durmuş.` } + try { + const success = await processManager.stopService(serviceName) + return { success, message: success ? `${serviceName} durduruldu.` : `${serviceName} durdurulurken hata oluştu veya zaten durmuş.` } + } catch (error: any) { + return { success: false, message: `Durdurma hatası: ${error.message}` } + } }) ipcMain.handle('services:status', async () => { @@ -743,7 +766,7 @@ export function registerIpcHandlers(): void { return { success: false, message: `MariaDB hatası: ${error.message}` } } }) - + ipcMain.handle('mariadb:config:get', async (_event, versionId: string) => { const settings = configService.getSettings() const tuning = settings.mariaDbTuning?.[versionId] || '' @@ -755,36 +778,44 @@ export function registerIpcHandlers(): void { const mariaDbTuning = settings.mariaDbTuning || {} mariaDbTuning[versionId] = content configService.updateSettings({ ...settings, mariaDbTuning }) - + const serviceName = `mariadb:${versionId.replace('mariadb-', '')}` if (processManager.isServiceRunning(serviceName)) { await processManager.stopService(serviceName) } - + return { success: true, message: 'Tuning configuration saved.' } }) ipcMain.handle('php:versions:all', async () => { return phpManagerService.getVersions() }) - + ipcMain.handle('php:config:get', async (_event, version: string) => { - const phpIniPath = path.join(configService.getBasePath(), 'generated_configs', `php_${version}.ini`) + const phpIniPath = path.join(configService.getBasePath(), 'generated_configs', 'php', version, 'php.ini') if (fs.existsSync(phpIniPath)) { return { content: fs.readFileSync(phpIniPath, 'utf8') } } + // Fallback to old path for migration support + const oldPath = path.join(configService.getBasePath(), 'generated_configs', `php_${version}.ini`) + if (fs.existsSync(oldPath)) { + return { content: fs.readFileSync(oldPath, 'utf8') } + } return { content: '' } }) ipcMain.handle('php:config:save', async (_event, { version, content }) => { - const phpIniPath = path.join(configService.getBasePath(), 'generated_configs', `php_${version}.ini`) + const phpIniDir = path.join(configService.getBasePath(), 'generated_configs', 'php', version) + if (!fs.existsSync(phpIniDir)) fs.mkdirSync(phpIniDir, { recursive: true }) + const phpIniPath = path.join(phpIniDir, 'php.ini') + // Add manual edit marker to prevent overwriting let finalContent = content if (!finalContent.includes('; MANUAL_EDIT')) { finalContent = `; MANUAL_EDIT\n${finalContent}` } fs.writeFileSync(phpIniPath, finalContent, 'utf8') - + // Restart PHP if running const serviceName = `php:${version}` if (processManager.isServiceRunning(serviceName)) { @@ -1017,46 +1048,47 @@ export function registerIpcHandlers(): void { ipcMain.handle('project:get-config', async (_event, { host, serverType }) => { const projectsDir = serverType === 'apache' ? 'projects_apache' : 'projects' - const baseDir = path.join(configService.getBasePath(), 'generated_configs', projectsDir) - // Try both host.conf and host.local.conf - const paths = [ - path.join(baseDir, `${host}.conf`), - path.join(baseDir, `${host}.local.conf`) - ] - - for (const p of paths) { - if (fs.existsSync(p)) { - return { content: fs.readFileSync(p, 'utf8'), path: p } + // Prefer projects_nginx for Nginx as requested by user, fallback to projects + const dirs = serverType === 'nginx' ? ['projects_nginx', 'projects'] : [projectsDir] + const baseDir = configService.getBasePath() + + for (const dir of dirs) { + const pathToCheck = path.join(baseDir, 'generated_configs', dir, `${host}.conf`) + if (fs.existsSync(pathToCheck)) { + return { content: fs.readFileSync(pathToCheck, 'utf8'), path: pathToCheck } + } + + // Try .local.conf as well + const localPath = path.join(baseDir, 'generated_configs', dir, `${host}.local.conf`) + if (fs.existsSync(localPath)) { + return { content: fs.readFileSync(localPath, 'utf8'), path: localPath } } } - - return { content: '', message: `Konfigürasyon dosyası bulunamadı. Aranan yollar: ${paths.join(', ')}` } + + return { content: '', message: `Konfigürasyon dosyası bulunamadı. Aranan klasörler: ${dirs.join(', ')}` } }) ipcMain.handle('project:save-config', async (_event, { host, serverType, content }) => { const projectsDir = serverType === 'apache' ? 'projects_apache' : 'projects' - const baseDir = path.join(configService.getBasePath(), 'generated_configs', projectsDir) - - // Try to find existing file first (to overwrite the right one) - const paths = [ - path.join(baseDir, `${host}.conf`), - path.join(baseDir, `${host}.local.conf`) - ] + const dirs = serverType === 'nginx' ? ['projects_nginx', 'projects'] : [projectsDir] + const baseDir = configService.getBasePath() - let configPath = paths[0] - for (const p of paths) { - if (fs.existsSync(p)) { - configPath = p - break - } - } + // Find existing file to overwrite, or default to the first folder + let configPath = path.join(baseDir, 'generated_configs', dirs[0], `${host}.conf`) + for (const dir of dirs) { + const p1 = path.join(baseDir, 'generated_configs', dir, `${host}.conf`) + const p2 = path.join(baseDir, 'generated_configs', dir, `${host}.local.conf`) + if (fs.existsSync(p1)) { configPath = p1; break; } + if (fs.existsSync(p2)) { configPath = p2; break; } + } + let finalContent = content if (!finalContent.includes('# MANUAL_EDIT')) { finalContent = `# MANUAL_EDIT\n${finalContent}` } - + fs.writeFileSync(configPath, finalContent, 'utf8') return { success: true, message: 'Konfigürasyon başarıyla kaydedildi.' } }) @@ -1369,7 +1401,7 @@ export function registerIpcHandlers(): void { try { const localeDir = path.join(process.cwd(), 'docs', 'user-guide', locale) if (!fs.existsSync(localeDir)) return [] - + const files = fs.readdirSync(localeDir).filter(f => f.endsWith('.md')) const topics = files.map(file => { const fullPath = path.join(localeDir, file) @@ -1378,14 +1410,14 @@ export function registerIpcHandlers(): void { const title = firstLine.replace(/^#\s*/, '').trim() || file return { file, title } }) - + // Reorder so index.md is first const indexIdx = topics.findIndex(t => t.file === 'index.md') if (indexIdx > -1) { const index = topics.splice(indexIdx, 1)[0] topics.unshift(index) } - + return topics } catch (error: any) { console.error('Failed to list topics:', error) diff --git a/src/main/services/ConfigService.ts b/src/main/services/ConfigService.ts index 5e408d8..859076f 100644 --- a/src/main/services/ConfigService.ts +++ b/src/main/services/ConfigService.ts @@ -58,6 +58,11 @@ export class ConfigService { content = content.replace(new RegExp(`{{${key}}}`, 'g'), () => escapedValue) } + const targetDir = path.dirname(targetPath) + if (!fs.existsSync(targetDir)) { + fs.mkdirSync(targetDir, { recursive: true }) + } + fs.writeFileSync(targetPath, content) return targetPath } diff --git a/src/main/services/ProcessManager.ts b/src/main/services/ProcessManager.ts index 9d66b13..3d4a1c0 100644 --- a/src/main/services/ProcessManager.ts +++ b/src/main/services/ProcessManager.ts @@ -9,11 +9,13 @@ import { t } from '../utils/i18n' export class ProcessManager { private processes: Map = new Map() + private states: Map = new Map() private logs: Map = new Map() private logDir: string constructor() { this.processes = new Map() + this.states = new Map() this.logDir = '' } @@ -67,6 +69,15 @@ export class ProcessManager { return this.logs.get(name) || [] } + setServiceState(name: string, state: 'starting' | 'running' | 'stopping' | 'stopped') { + console.log(`[STATE] ${name} -> ${state}`) + this.states.set(name, state) + } + + getServiceState(name: string): string { + return this.states.get(name) || (this.processes.has(name) ? 'running' : 'stopped') + } + async runOnce(binPath: string, args: string[], name?: string): Promise<{ success: boolean; output: string }> { return new Promise((resolve) => { const child = spawn(binPath, args, { stdio: 'pipe', shell: false, windowsHide: true }) @@ -102,15 +113,18 @@ export class ProcessManager { async startService(name: string, binPath: string, args: string[], shell: boolean = true, envVars: Record = {}): Promise { if (this.processes.has(name)) { + this.states.set(name, 'running') return true } if (!fs.existsSync(binPath)) { this.addLog(name, `${t('common.error')}: ${t('common.executable_not_found')} (${binPath})`) + this.states.set(name, 'stopped') return false } try { + this.states.set(name, 'starting') this.addLog(name, `${t('common.starting')}... (${binPath})`) const serviceEnv = { ...process.env, ...envVars } @@ -129,6 +143,11 @@ export class ProcessManager { env: serviceEnv }) + if (!child.pid) { + this.states.set(name, 'stopped') + return false + } + console.log(`[PROCESS] Started ${name} with PID ${child.pid}`) this.addLog(name, `${t('common.running')} (PID: ${child.pid})`) serviceEventLogger.log({ service: name, action: 'start', status: 'success' }) @@ -140,6 +159,7 @@ export class ProcessManager { this.addLog(name, `Failed to start: ${err.message}`) serviceEventLogger.log({ service: name, action: 'start', status: 'failure', details: err.message }) this.processes.delete(name) + this.states.set(name, 'stopped') }) child.on('exit', (code) => { @@ -150,38 +170,72 @@ export class ProcessManager { this.checkErrorLogs(name) } this.processes.delete(name) + this.states.set(name, 'stopped') }) this.processes.set(name, child) - + // State remains 'starting' until the IPC handler confirms port listener return true } catch (error: any) { this.addLog(name, `Spawn Error: ${error.message}`) serviceEventLogger.log({ service: name, action: 'start', status: 'failure', details: error.message }) + this.states.set(name, 'stopped') return false } } async stopService(name: string): Promise { + this.states.set(name, 'stopping') const child = this.processes.get(name) + if (child) { this.addLog(name, `${t('common.stopping')}...`) serviceEventLogger.log({ service: name, action: 'stop', status: 'info' }) - try { - if (process.platform === 'win32') { - child.kill() - } else { - // On Unix, try SIGTERM first, then SIGKILL if needed - child.kill('SIGTERM') + + return new Promise((resolve) => { + let resolved = false + const timer = setTimeout(() => { + if (!resolved) { + this.addLog(name, 'Stop timeout, forcing kill...') + this.forceKillAll(name).then(() => { + this.states.set(name, 'stopped') + resolve(true) + }) + resolved = true + } + }, 5000) + + child.on('exit', () => { + if (!resolved) { + clearTimeout(timer) + this.processes.delete(name) + this.states.set(name, 'stopped') + resolve(true) + resolved = true + } + }) + + try { + if (process.platform === 'win32') { + child.kill() + } else { + child.kill('SIGTERM') + } + } catch (e) { + this.addLog(name, `Stop signal error: ${e instanceof Error ? e.message : String(e)}`) + this.forceKillAll(name).then(() => { + this.states.set(name, 'stopped') + resolve(true) + }) + resolved = true } - } catch (e) { - this.addLog(name, `Stop error: ${e instanceof Error ? e.message : String(e)}`) - } - this.processes.delete(name) + }) } - // Also force kill lingering processes - return this.forceKillAll(name) + // Also force kill lingering processes even if not in map + const result = await this.forceKillAll(name) + this.states.set(name, 'stopped') + return result } async forceKillAll(name: string): Promise { @@ -210,13 +264,11 @@ export class ProcessManager { .on('close', () => resolve(true)) .on('error', () => resolve(true)) } else { - // Unix: pkill -f matches full command line (data directory path usually contains the dbId) spawn('pkill', ['-f', dbId], { windowsHide: true }) .on('close', () => resolve(true)) .on('error', () => resolve(true)) } } else { - const isWindows = process.platform === 'win32'; if (isWindows) { 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 }) @@ -230,7 +282,6 @@ export class ProcessManager { } } } else { - // macOS / Linux if (childProcess && childProcess.pid) { try { process.kill(childProcess.pid, 'SIGKILL') } catch (e) { } this.processes.delete(name) @@ -286,36 +337,51 @@ export class ProcessManager { } isServiceRunning(name: string): boolean { - return this.processes.has(name) + const child = this.processes.get(name) + return !!(child && child.pid) } async getStatuses(): Promise> { const statuses: Record = {} + // Add all tracked states first + for (const [name, state] of this.states.entries()) { + statuses[name] = { status: state } + } + const entries = Array.from(this.processes.entries()) for (const [name, child] of entries) { + const currentState = this.states.get(name) if (child.pid) { try { const stats = await pidusage(child.pid) statuses[name] = { - status: 'running', - cpu: Math.round(stats.cpu * 10) / 10, // Round to 1 decimal + status: currentState === 'stopping' ? 'stopping' : 'running', + cpu: Math.round(stats.cpu * 10) / 10, memory: stats.memory } + if (currentState !== 'stopping' && currentState !== 'starting') { + this.states.set(name, 'running') + } } catch (e) { - statuses[name] = { status: 'running' } - // Clean up pidusage if error (usually process died) + if (currentState !== 'starting' && currentState !== 'stopping') { + statuses[name] = { status: 'stopped' } + this.states.set(name, 'stopped') + } pidusage.clear() } } else { - statuses[name] = { status: 'running' } + if (currentState !== 'starting' && currentState !== 'stopping') { + statuses[name] = { status: 'stopped' } + this.states.set(name, 'stopped') + } } } - // Ensure defaults are present if stopped + // Ensure defaults are present const defaultServices = ['nginx', 'apache', 'mariadb'] defaultServices.forEach(s => { - if (!statuses[s]) statuses[s] = { status: 'stopped' } + if (!statuses[s]) statuses[s] = { status: this.states.get(s) || 'stopped' } }) return statuses @@ -353,7 +419,6 @@ export class ProcessManager { this.initLogs() const foundLogs: string[] = [] - // 1. Check in-memory logs for [CORE ERROR] or other error markers const memLogs = this.logs.get(name) || [] const errorMarkers = ['ERROR', 'failed', 'Fatal', 'error'] const recentErrors = memLogs.filter(line => @@ -362,7 +427,6 @@ export class ProcessManager { foundLogs.push(...recentErrors) - // 2. Check physical log files if memory logs are insufficient const sanitizedName = name.replace(/:/g, '_') const possibleFiles = [ path.join(this.logDir, `${sanitizedName}.log`), @@ -381,7 +445,6 @@ export class ProcessManager { if (!foundLogs.includes(l)) foundLogs.push(l) }) } catch (e) { - // Ignore errors } } } diff --git a/src/renderer/src/components/ProjectManager/ProjectDetails.tsx b/src/renderer/src/components/ProjectManager/ProjectDetails.tsx index e498fe6..314ef40 100644 --- a/src/renderer/src/components/ProjectManager/ProjectDetails.tsx +++ b/src/renderer/src/components/ProjectManager/ProjectDetails.tsx @@ -268,7 +268,7 @@ export default function ProjectDetails({ {projectDetailTab === 0 && ( - + @@ -313,7 +313,7 @@ export default function ProjectDetails({ )} {projectDetailTab === 1 && ( - +