diff --git a/config/apache_project.conf.template b/config/apache_project.conf.template
index 3cbb832..aaad8f8 100644
--- a/config/apache_project.conf.template
+++ b/config/apache_project.conf.template
@@ -1,38 +1,33 @@
-# Apache Project Configuration (CGI Mode)
-# Accessed via: http://localhost:{{PORT}}/{{HOST}}/
+
+ ServerName {{HOST}}
+ DocumentRoot "{{PATH}}"
-# 1. Alias definition (restores localhost/projectname access)
-Alias /{{HOST}} "{{PATH}}"
+
+ Options Indexes FollowSymLinks ExecCGI
+ AllowOverride All
+ Require all granted
+
+ # Passing Authorization header to PHP
+ CGIPassAuth On
+
+ # Directory index handling
+ DirectoryIndex index.php index.html
+
+ # PHP Handling via mod_actions and CGI
+
+ SetHandler application/x-httpd-php-{{HOST}}
+
+
+ # Action maps the handler to the binary via the ScriptAlias URL
+ Action application/x-httpd-php-{{HOST}} "{{PHP_BIN_URL}}/php-cgi.exe"
+
-# 2. ScriptAlias definition for the project's PHP CGI binary
-# This maps a virtual URL path to the actual PHP folder.
-ScriptAlias {{PHP_BIN_URL}}/ "{{PHP_DIR}}/"
-
-# 3. Directory settings for the project
-
- Options Indexes FollowSymLinks ExecCGI
- AllowOverride All
- Require all granted
+ # ScriptAlias definition for the project's PHP CGI binary
+ ScriptAlias {{PHP_BIN_URL}}/ "{{PHP_DIR}}/"
- # Passing Authorization header to PHP
- CGIPassAuth On
-
- # Directory index handling
- DirectoryIndex index.php index.html
-
- # PHP Handling via mod_actions and CGI
-
- SetHandler application/x-httpd-php-{{HOST}}
-
-
- # Action maps the handler to the binary via the ScriptAlias URL
- # This is more robust on Windows than using absolute local paths.
- Action application/x-httpd-php-{{HOST}} "{{PHP_BIN_URL}}/php-cgi.exe"
-
-
-# 4. Permissions for the PHP binary directory
-
- AllowOverride None
- Options None
- Require all granted
-
\ No newline at end of file
+
+ AllowOverride None
+ Options None
+ Require all granted
+
+
\ No newline at end of file
diff --git a/config/apache_project_alias.conf.template b/config/apache_project_alias.conf.template
new file mode 100644
index 0000000..527efed
--- /dev/null
+++ b/config/apache_project_alias.conf.template
@@ -0,0 +1,32 @@
+# Subdirectory Alias for {{HOST}}
+Alias /{{HOST}} "{{PATH}}"
+
+
+ Options Indexes FollowSymLinks ExecCGI
+ AllowOverride All
+ Require all granted
+
+ # Passing Authorization header to PHP
+ CGIPassAuth On
+
+ # Directory index handling
+ DirectoryIndex index.php index.html
+
+ # PHP Handling via mod_actions and CGI
+
+ SetHandler application/x-httpd-php-alias-{{HOST}}
+
+
+ # Action maps the handler to the binary via the ScriptAlias URL
+ # We use a unique handler name to avoid conflict with VirtualHost config
+ Action application/x-httpd-php-alias-{{HOST}} "{{PHP_BIN_URL}}/php-cgi.exe"
+
+
+# ScriptAlias definition for the project's PHP CGI binary
+ScriptAlias {{PHP_BIN_URL}}/ "{{PHP_DIR}}/"
+
+
+ AllowOverride None
+ Options None
+ Require all granted
+
diff --git a/config/httpd.conf.template b/config/httpd.conf.template
index f807e18..c7940f4 100644
--- a/config/httpd.conf.template
+++ b/config/httpd.conf.template
@@ -83,7 +83,10 @@ Alias /phpmyadmin "{{PHPMYADMIN_DIR}}"
CGIPassAuth On
-# Project configurations
+# Project configurations (Subdirectory Access)
+{{PROJECT_ALIASES}}
+
+# Project configurations (Virtual Host Access)
{{PROJECTS}}
# Global PHP handler (Manual Nginx-Style SCRIPT_FILENAME)
diff --git a/config/nginx.conf.template b/config/nginx.conf.template
index 386a13a..1e0becd 100644
--- a/config/nginx.conf.template
+++ b/config/nginx.conf.template
@@ -9,12 +9,14 @@ http {
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
+ server_names_hash_bucket_size 64;
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
@@ -35,8 +37,10 @@ http {
}
return 404;
}
+
+ # Subdirectory access for projects (legacy support)
+ {{PROJECT_LOCATIONS}}
- {{PROJECTS}}
location /phpmyadmin {
alias "{{PHPMYADMIN_DIR}}";
@@ -58,4 +62,6 @@ http {
fastcgi_param SCRIPT_FILENAME $request_filename;
}
}
+
+ {{PROJECTS}}
}
diff --git a/config/nginx_project.conf.template b/config/nginx_project.conf.template
index 1311849..dc60f93 100644
--- a/config/nginx_project.conf.template
+++ b/config/nginx_project.conf.template
@@ -1,31 +1,24 @@
-location ^~ /{{HOST}}/ {
- alias "{{PATH}}";
+server {
+ listen {{PORT}};
+ server_name {{HOST}};
+ root "{{PATH}}";
index index.php index.html;
autoindex on;
-
- # Use a named location for reliable alias fallback
- try_files $uri $uri/ @{{HOST}};
- # PHP handling nested INSIDE the alias location to maintain context
+ # Support for clean URLs
+ location / {
+ try_files $uri $uri/ /index.php?$query_string;
+ }
+
+ # PHP handling
location ~ \.php$ {
fastcgi_pass 127.0.0.1:{{PHP_PORT}};
fastcgi_index index.php;
-
include "{{CONF_DIR}}/fastcgi_params";
-
- # Normal files matched correctly by URI don't suffer the try_files bug
- fastcgi_param SCRIPT_FILENAME $request_filename;
+ fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
-}
-# The named location handles requests that don't match physical files
-location @{{HOST}} {
- # Directly execute PHP without URI mutation to preserve framework routing paths
- fastcgi_pass 127.0.0.1:{{PHP_PORT}};
- include "{{CONF_DIR}}/fastcgi_params";
-
- # Hardcode the essential execution parameters
- fastcgi_param SCRIPT_FILENAME "{{PATH}}index.php";
- fastcgi_param SCRIPT_NAME "/{{HOST}}/index.php";
- fastcgi_param DOCUMENT_ROOT "{{PATH}}";
+ # Custom log files for the project
+ # access_log "{{LOG_DIR}}/{{HOST}}_access.log";
+ # error_log "{{LOG_DIR}}/{{HOST}}_error.log";
}
diff --git a/config/nginx_project_location.conf.template b/config/nginx_project_location.conf.template
new file mode 100644
index 0000000..894eee5
--- /dev/null
+++ b/config/nginx_project_location.conf.template
@@ -0,0 +1,17 @@
+location ^~ /{{HOST}}/ {
+ alias "{{PATH}}";
+ index index.php index.html;
+ autoindex on;
+
+ if (!-e $request_filename) {
+ rewrite ^/{{HOST}}/(.*)$ /{{HOST}}/index.php?$1 last;
+ }
+
+ location ~ \.php$ {
+ # Using $request_filename to match the alias path correctly
+ fastcgi_pass 127.0.0.1:{{PHP_PORT}};
+ fastcgi_index index.php;
+ include "{{CONF_DIR}}/fastcgi_params";
+ fastcgi_param SCRIPT_FILENAME $request_filename;
+ }
+}
diff --git a/service_events.json b/service_events.json
index 0fbd3c9..8c8d5fa 100644
--- a/service_events.json
+++ b/service_events.json
@@ -1,4 +1,1657 @@
[
+ {
+ "service": "nginx",
+ "action": "start",
+ "status": "success",
+ "id": "x9hvv7hsb",
+ "timestamp": 1775150982761
+ },
+ {
+ "service": "nginx",
+ "action": "exit",
+ "status": "failure",
+ "details": "Exit code: 1",
+ "id": "v4ipyg2i4",
+ "timestamp": 1775150981476
+ },
+ {
+ "service": "apache",
+ "action": "start",
+ "status": "success",
+ "id": "g92zn6zie",
+ "timestamp": 1775150981155
+ },
+ {
+ "service": "apache",
+ "action": "exit",
+ "status": "failure",
+ "details": "Exit code: 1",
+ "id": "vy58xvhc1",
+ "timestamp": 1775150979799
+ },
+ {
+ "service": "apache",
+ "action": "start",
+ "status": "success",
+ "id": "mgee4y1sc",
+ "timestamp": 1775150648837
+ },
+ {
+ "service": "apache",
+ "action": "exit",
+ "status": "failure",
+ "details": "Exit code: 1",
+ "id": "t8knokmhz",
+ "timestamp": 1775150647450
+ },
+ {
+ "service": "apache",
+ "action": "start",
+ "status": "success",
+ "id": "6z5sa3xgr",
+ "timestamp": 1775150624672
+ },
+ {
+ "service": "apache",
+ "action": "exit",
+ "status": "failure",
+ "details": "Exit code: 1",
+ "id": "h2ksioetl",
+ "timestamp": 1775150624643
+ },
+ {
+ "service": "nginx",
+ "action": "start",
+ "status": "success",
+ "id": "jgghsoxiz",
+ "timestamp": 1775150619025
+ },
+ {
+ "service": "nginx",
+ "action": "exit",
+ "status": "failure",
+ "details": "Exit code: 1",
+ "id": "6l0dmc7wm",
+ "timestamp": 1775150617630
+ },
+ {
+ "service": "apache",
+ "action": "start",
+ "status": "success",
+ "id": "751akrtrj",
+ "timestamp": 1775150617228
+ },
+ {
+ "service": "nginx",
+ "action": "start",
+ "status": "success",
+ "id": "zj21cgh19",
+ "timestamp": 1775150605139
+ },
+ {
+ "service": "nginx",
+ "action": "exit",
+ "status": "failure",
+ "details": "Exit code: 1",
+ "id": "igr1oplt7",
+ "timestamp": 1775150603787
+ },
+ {
+ "service": "php:7.4.33",
+ "action": "start",
+ "status": "success",
+ "id": "xpautv5vl",
+ "timestamp": 1775150557540
+ },
+ {
+ "service": "php:5.6.40",
+ "action": "start",
+ "status": "success",
+ "id": "f8lxlllqb",
+ "timestamp": 1775150549382
+ },
+ {
+ "service": "nginx",
+ "action": "start",
+ "status": "success",
+ "id": "1qj5mer0v",
+ "timestamp": 1775150548357
+ },
+ {
+ "service": "mariadb:10.11.8",
+ "action": "exit",
+ "status": "failure",
+ "details": "Exit code: 1",
+ "id": "oabjf0kmp",
+ "timestamp": 1775150539513
+ },
+ {
+ "service": "mariadb:11.4.2",
+ "action": "exit",
+ "status": "failure",
+ "details": "Exit code: 1",
+ "id": "cdqeyl8hb",
+ "timestamp": 1775150539498
+ },
+ {
+ "service": "php:8.5.4",
+ "action": "exit",
+ "status": "failure",
+ "details": "Exit code: null",
+ "id": "1dze9cw1u",
+ "timestamp": 1775150539159
+ },
+ {
+ "service": "php:8.5.4",
+ "action": "stop",
+ "status": "info",
+ "id": "ad1fefv4o",
+ "timestamp": 1775150539145
+ },
+ {
+ "service": "nginx",
+ "action": "exit",
+ "status": "failure",
+ "details": "Exit code: null",
+ "id": "ql4gzicux",
+ "timestamp": 1775150538791
+ },
+ {
+ "service": "nginx",
+ "action": "stop",
+ "status": "info",
+ "id": "dx8v5zdrv",
+ "timestamp": 1775150538786
+ },
+ {
+ "service": "nginx",
+ "action": "start",
+ "status": "success",
+ "id": "h29e59m7n",
+ "timestamp": 1775150451034
+ },
+ {
+ "service": "nginx",
+ "action": "exit",
+ "status": "failure",
+ "details": "Exit code: 1",
+ "id": "hiot2wp8k",
+ "timestamp": 1775150441780
+ },
+ {
+ "service": "nginx",
+ "action": "start",
+ "status": "success",
+ "id": "bulq7bi3a",
+ "timestamp": 1775150441743
+ },
+ {
+ "service": "nginx",
+ "action": "exit",
+ "status": "failure",
+ "details": "Exit code: 1",
+ "id": "rne4abiyv",
+ "timestamp": 1775150422032
+ },
+ {
+ "service": "nginx",
+ "action": "start",
+ "status": "success",
+ "id": "k5yg7epmg",
+ "timestamp": 1775150421994
+ },
+ {
+ "service": "nginx",
+ "action": "exit",
+ "status": "failure",
+ "details": "Exit code: 1",
+ "id": "8gfojjrsj",
+ "timestamp": 1775150417309
+ },
+ {
+ "service": "nginx",
+ "action": "start",
+ "status": "success",
+ "id": "ymqzjsiek",
+ "timestamp": 1775150417263
+ },
+ {
+ "service": "php:5.6.40",
+ "action": "start",
+ "status": "success",
+ "id": "hqo3ku3t9",
+ "timestamp": 1775150404864
+ },
+ {
+ "service": "php:7.4.33",
+ "action": "start",
+ "status": "success",
+ "id": "8ew1w9buv",
+ "timestamp": 1775150404846
+ },
+ {
+ "service": "php:8.2.30",
+ "action": "start",
+ "status": "success",
+ "id": "bqwlyk3g6",
+ "timestamp": 1775150404828
+ },
+ {
+ "service": "php:8.5.4",
+ "action": "start",
+ "status": "success",
+ "id": "o2uqetljl",
+ "timestamp": 1775150404811
+ },
+ {
+ "service": "mariadb:11.4.2",
+ "action": "start",
+ "status": "success",
+ "id": "33doizwg6",
+ "timestamp": 1775150404599
+ },
+ {
+ "service": "mariadb:10.11.8",
+ "action": "start",
+ "status": "success",
+ "id": "7cdxzt48p",
+ "timestamp": 1775150404578
+ },
+ {
+ "service": "apache",
+ "action": "start",
+ "status": "success",
+ "id": "pwh2trf2p",
+ "timestamp": 1775150403826
+ },
+ {
+ "service": "nginx",
+ "action": "exit",
+ "status": "failure",
+ "details": "Exit code: 1",
+ "id": "589qt6skk",
+ "timestamp": 1775150403819
+ },
+ {
+ "service": "nginx",
+ "action": "start",
+ "status": "success",
+ "id": "m89coborm",
+ "timestamp": 1775150403765
+ },
+ {
+ "service": "php:7.4.33",
+ "action": "start",
+ "status": "success",
+ "id": "scuy98pqd",
+ "timestamp": 1775144396353
+ },
+ {
+ "service": "apache",
+ "action": "start",
+ "status": "success",
+ "id": "ngriq552n",
+ "timestamp": 1775144395325
+ },
+ {
+ "service": "mariadb:10.11.8",
+ "action": "exit",
+ "status": "failure",
+ "details": "Exit code: 1",
+ "id": "90ivxjxp7",
+ "timestamp": 1775144390098
+ },
+ {
+ "service": "mariadb:11.4.2",
+ "action": "exit",
+ "status": "failure",
+ "details": "Exit code: 1",
+ "id": "ewt9obb9p",
+ "timestamp": 1775144390088
+ },
+ {
+ "service": "php:7.4.33",
+ "action": "exit",
+ "status": "failure",
+ "details": "Exit code: null",
+ "id": "z2ytaa9z2",
+ "timestamp": 1775144389800
+ },
+ {
+ "service": "php:7.4.33",
+ "action": "stop",
+ "status": "info",
+ "id": "ks4vjl0a8",
+ "timestamp": 1775144389788
+ },
+ {
+ "service": "apache",
+ "action": "exit",
+ "status": "failure",
+ "details": "Exit code: null",
+ "id": "otecei6vx",
+ "timestamp": 1775144389492
+ },
+ {
+ "service": "apache",
+ "action": "stop",
+ "status": "info",
+ "id": "zqg6cv3gm",
+ "timestamp": 1775144389485
+ },
+ {
+ "service": "nginx",
+ "action": "start",
+ "status": "success",
+ "id": "72u53glzq",
+ "timestamp": 1775144370843
+ },
+ {
+ "service": "nginx",
+ "action": "exit",
+ "status": "failure",
+ "details": "Exit code: 1",
+ "id": "cqbvd5ex6",
+ "timestamp": 1775144369526
+ },
+ {
+ "service": "apache",
+ "action": "start",
+ "status": "success",
+ "id": "mglm8ddoj",
+ "timestamp": 1775144369213
+ },
+ {
+ "service": "apache",
+ "action": "exit",
+ "status": "failure",
+ "details": "Exit code: 1",
+ "id": "so2zrdeff",
+ "timestamp": 1775144367875
+ },
+ {
+ "service": "nginx",
+ "action": "start",
+ "status": "success",
+ "id": "6sk4bsx1z",
+ "timestamp": 1775144338063
+ },
+ {
+ "service": "nginx",
+ "action": "exit",
+ "status": "failure",
+ "details": "Exit code: 1",
+ "id": "cawnpv1bt",
+ "timestamp": 1775144336760
+ },
+ {
+ "service": "apache",
+ "action": "start",
+ "status": "success",
+ "id": "cmiiuyys3",
+ "timestamp": 1775144336453
+ },
+ {
+ "service": "apache",
+ "action": "exit",
+ "status": "failure",
+ "details": "Exit code: 1",
+ "id": "ikbkgh6jm",
+ "timestamp": 1775144335172
+ },
+ {
+ "service": "php:5.6.40",
+ "action": "start",
+ "status": "success",
+ "id": "pqxdm20c9",
+ "timestamp": 1775144323509
+ },
+ {
+ "service": "php:7.4.33",
+ "action": "start",
+ "status": "success",
+ "id": "ysuye4pqj",
+ "timestamp": 1775144323480
+ },
+ {
+ "service": "php:8.2.30",
+ "action": "start",
+ "status": "success",
+ "id": "8hrfyvdkj",
+ "timestamp": 1775144323458
+ },
+ {
+ "service": "php:8.5.4",
+ "action": "start",
+ "status": "success",
+ "id": "japix37kq",
+ "timestamp": 1775144323441
+ },
+ {
+ "service": "mariadb:10.11.8",
+ "action": "start",
+ "status": "success",
+ "id": "lbawdrnfj",
+ "timestamp": 1775144323192
+ },
+ {
+ "service": "mariadb:11.4.2",
+ "action": "start",
+ "status": "success",
+ "id": "5w48592ov",
+ "timestamp": 1775144323175
+ },
+ {
+ "service": "apache",
+ "action": "start",
+ "status": "success",
+ "id": "9ytif9zck",
+ "timestamp": 1775144322476
+ },
+ {
+ "service": "nginx",
+ "action": "start",
+ "status": "success",
+ "id": "4fgo16wcu",
+ "timestamp": 1775144322396
+ },
+ {
+ "service": "mariadb:10.11.8",
+ "action": "exit",
+ "status": "failure",
+ "details": "Exit code: null",
+ "id": "zcq3mllla",
+ "timestamp": 1775144321231
+ },
+ {
+ "service": "mariadb:11.4.2",
+ "action": "exit",
+ "status": "failure",
+ "details": "Exit code: null",
+ "id": "bfayna5kl",
+ "timestamp": 1775144321215
+ },
+ {
+ "service": "php:5.6.40",
+ "action": "exit",
+ "status": "failure",
+ "details": "Exit code: null",
+ "id": "kyiq1bq1v",
+ "timestamp": 1775144321206
+ },
+ {
+ "service": "php:7.4.33",
+ "action": "exit",
+ "status": "failure",
+ "details": "Exit code: null",
+ "id": "w16aahco6",
+ "timestamp": 1775144321199
+ },
+ {
+ "service": "php:8.2.30",
+ "action": "exit",
+ "status": "failure",
+ "details": "Exit code: null",
+ "id": "v0538f9pc",
+ "timestamp": 1775144321191
+ },
+ {
+ "service": "php:8.5.4",
+ "action": "exit",
+ "status": "failure",
+ "details": "Exit code: null",
+ "id": "fo92zhlw5",
+ "timestamp": 1775144321183
+ },
+ {
+ "service": "apache",
+ "action": "exit",
+ "status": "failure",
+ "details": "Exit code: null",
+ "id": "0ba7vtelc",
+ "timestamp": 1775144321145
+ },
+ {
+ "service": "nginx",
+ "action": "exit",
+ "status": "failure",
+ "details": "Exit code: null",
+ "id": "fohbng7fg",
+ "timestamp": 1775144321144
+ },
+ {
+ "service": "mariadb:10.11.8",
+ "action": "stop",
+ "status": "info",
+ "id": "mbnmye7uu",
+ "timestamp": 1775144321135
+ },
+ {
+ "service": "mariadb:11.4.2",
+ "action": "stop",
+ "status": "info",
+ "id": "cu4e7t4b3",
+ "timestamp": 1775144321121
+ },
+ {
+ "service": "php:5.6.40",
+ "action": "stop",
+ "status": "info",
+ "id": "wepe2jsur",
+ "timestamp": 1775144321108
+ },
+ {
+ "service": "php:7.4.33",
+ "action": "stop",
+ "status": "info",
+ "id": "55y40xqzl",
+ "timestamp": 1775144321096
+ },
+ {
+ "service": "php:8.2.30",
+ "action": "stop",
+ "status": "info",
+ "id": "kb3q8v1j1",
+ "timestamp": 1775144321083
+ },
+ {
+ "service": "php:8.5.4",
+ "action": "stop",
+ "status": "info",
+ "id": "oqx1wr67q",
+ "timestamp": 1775144321068
+ },
+ {
+ "service": "apache",
+ "action": "stop",
+ "status": "info",
+ "id": "w6gbjp5ln",
+ "timestamp": 1775144321054
+ },
+ {
+ "service": "nginx",
+ "action": "stop",
+ "status": "info",
+ "id": "flhslsx8m",
+ "timestamp": 1775144321047
+ },
+ {
+ "service": "php:5.6.40",
+ "action": "start",
+ "status": "success",
+ "id": "je15n99xp",
+ "timestamp": 1775144291473
+ },
+ {
+ "service": "php:7.4.33",
+ "action": "start",
+ "status": "success",
+ "id": "mzg4zspgw",
+ "timestamp": 1775144291453
+ },
+ {
+ "service": "php:8.2.30",
+ "action": "start",
+ "status": "success",
+ "id": "q8u9qvlbv",
+ "timestamp": 1775144291432
+ },
+ {
+ "service": "php:8.5.4",
+ "action": "start",
+ "status": "success",
+ "id": "f1p0gil1s",
+ "timestamp": 1775144291406
+ },
+ {
+ "service": "mariadb:10.11.8",
+ "action": "start",
+ "status": "success",
+ "id": "x28rxovfd",
+ "timestamp": 1775144291122
+ },
+ {
+ "service": "mariadb:11.4.2",
+ "action": "start",
+ "status": "success",
+ "id": "qnk49zop6",
+ "timestamp": 1775144291102
+ },
+ {
+ "service": "apache",
+ "action": "start",
+ "status": "success",
+ "id": "smhza5ym3",
+ "timestamp": 1775144290439
+ },
+ {
+ "service": "nginx",
+ "action": "start",
+ "status": "success",
+ "id": "5g8fifvcr",
+ "timestamp": 1775144290367
+ },
+ {
+ "service": "php:5.6.40",
+ "action": "start",
+ "status": "success",
+ "id": "64rk9a8jl",
+ "timestamp": 1775144234681
+ },
+ {
+ "service": "php:7.4.33",
+ "action": "start",
+ "status": "success",
+ "id": "o8qxofvhm",
+ "timestamp": 1775144234661
+ },
+ {
+ "service": "php:8.2.30",
+ "action": "start",
+ "status": "success",
+ "id": "audz6van5",
+ "timestamp": 1775144234641
+ },
+ {
+ "service": "php:8.5.4",
+ "action": "start",
+ "status": "success",
+ "id": "62m0s8z80",
+ "timestamp": 1775144234623
+ },
+ {
+ "service": "mariadb:10.11.8",
+ "action": "start",
+ "status": "success",
+ "id": "uhh9nlsrw",
+ "timestamp": 1775144234367
+ },
+ {
+ "service": "mariadb:11.4.2",
+ "action": "start",
+ "status": "success",
+ "id": "uhp60xly4",
+ "timestamp": 1775144234343
+ },
+ {
+ "service": "apache",
+ "action": "start",
+ "status": "success",
+ "id": "6xskm29tx",
+ "timestamp": 1775144233649
+ },
+ {
+ "service": "nginx",
+ "action": "start",
+ "status": "success",
+ "id": "lj7koig8f",
+ "timestamp": 1775144233579
+ },
+ {
+ "service": "php:5.6.40",
+ "action": "start",
+ "status": "success",
+ "id": "hligqdqbg",
+ "timestamp": 1775143682791
+ },
+ {
+ "service": "php:7.4.33",
+ "action": "start",
+ "status": "success",
+ "id": "69pbd55hh",
+ "timestamp": 1775143682765
+ },
+ {
+ "service": "php:8.2.30",
+ "action": "start",
+ "status": "success",
+ "id": "uolp904tg",
+ "timestamp": 1775143682745
+ },
+ {
+ "service": "php:8.5.4",
+ "action": "start",
+ "status": "success",
+ "id": "sltc3b8i6",
+ "timestamp": 1775143682727
+ },
+ {
+ "service": "mariadb:11.4.2",
+ "action": "start",
+ "status": "success",
+ "id": "1on6t391k",
+ "timestamp": 1775143682356
+ },
+ {
+ "service": "mariadb:10.11.8",
+ "action": "start",
+ "status": "success",
+ "id": "5wg4nf0nl",
+ "timestamp": 1775143682338
+ },
+ {
+ "service": "apache",
+ "action": "start",
+ "status": "success",
+ "id": "y3c7yro4y",
+ "timestamp": 1775143681744
+ },
+ {
+ "service": "nginx",
+ "action": "start",
+ "status": "success",
+ "id": "b9xo3i1ox",
+ "timestamp": 1775143681683
+ },
+ {
+ "service": "php:5.6.40",
+ "action": "exit",
+ "status": "failure",
+ "details": "Exit code: null",
+ "id": "aakeq52dp",
+ "timestamp": 1775143568597
+ },
+ {
+ "service": "php:7.4.33",
+ "action": "exit",
+ "status": "failure",
+ "details": "Exit code: null",
+ "id": "rixx6genu",
+ "timestamp": 1775143568590
+ },
+ {
+ "service": "php:8.2.30",
+ "action": "exit",
+ "status": "failure",
+ "details": "Exit code: null",
+ "id": "r38zl4zuu",
+ "timestamp": 1775143568582
+ },
+ {
+ "service": "php:8.5.4",
+ "action": "exit",
+ "status": "failure",
+ "details": "Exit code: null",
+ "id": "grxpsvjhg",
+ "timestamp": 1775143568574
+ },
+ {
+ "service": "mariadb:10.11.8",
+ "action": "exit",
+ "status": "failure",
+ "details": "Exit code: null",
+ "id": "lg6o4ibbf",
+ "timestamp": 1775143568566
+ },
+ {
+ "service": "mariadb:11.4.2",
+ "action": "exit",
+ "status": "failure",
+ "details": "Exit code: null",
+ "id": "vswdvrzt4",
+ "timestamp": 1775143568559
+ },
+ {
+ "service": "apache",
+ "action": "exit",
+ "status": "failure",
+ "details": "Exit code: null",
+ "id": "h9dnmkhgj",
+ "timestamp": 1775143568521
+ },
+ {
+ "service": "nginx",
+ "action": "exit",
+ "status": "failure",
+ "details": "Exit code: null",
+ "id": "cdt1dk7oj",
+ "timestamp": 1775143568519
+ },
+ {
+ "service": "php:5.6.40",
+ "action": "stop",
+ "status": "info",
+ "id": "kcp2j9esp",
+ "timestamp": 1775143568502
+ },
+ {
+ "service": "php:7.4.33",
+ "action": "stop",
+ "status": "info",
+ "id": "f7sqck0l8",
+ "timestamp": 1775143568491
+ },
+ {
+ "service": "php:8.2.30",
+ "action": "stop",
+ "status": "info",
+ "id": "bv0eokrdn",
+ "timestamp": 1775143568477
+ },
+ {
+ "service": "php:8.5.4",
+ "action": "stop",
+ "status": "info",
+ "id": "6bx01cztj",
+ "timestamp": 1775143568465
+ },
+ {
+ "service": "mariadb:10.11.8",
+ "action": "stop",
+ "status": "info",
+ "id": "y68edblfs",
+ "timestamp": 1775143568452
+ },
+ {
+ "service": "mariadb:11.4.2",
+ "action": "stop",
+ "status": "info",
+ "id": "p8jhnv2ya",
+ "timestamp": 1775143568442
+ },
+ {
+ "service": "apache",
+ "action": "stop",
+ "status": "info",
+ "id": "rhsnvp7ly",
+ "timestamp": 1775143568427
+ },
+ {
+ "service": "nginx",
+ "action": "stop",
+ "status": "info",
+ "id": "fahkm2kyw",
+ "timestamp": 1775143568421
+ },
+ {
+ "service": "php:5.6.40",
+ "action": "start",
+ "status": "success",
+ "id": "z0ry0k0k7",
+ "timestamp": 1775143501975
+ },
+ {
+ "service": "php:7.4.33",
+ "action": "start",
+ "status": "success",
+ "id": "61tszw0xn",
+ "timestamp": 1775143501949
+ },
+ {
+ "service": "php:8.2.30",
+ "action": "start",
+ "status": "success",
+ "id": "bia7p9f9v",
+ "timestamp": 1775143501931
+ },
+ {
+ "service": "php:8.5.4",
+ "action": "start",
+ "status": "success",
+ "id": "k6lcmiyl5",
+ "timestamp": 1775143501912
+ },
+ {
+ "service": "mariadb:10.11.8",
+ "action": "start",
+ "status": "success",
+ "id": "gnk65u5ap",
+ "timestamp": 1775143501647
+ },
+ {
+ "service": "mariadb:11.4.2",
+ "action": "start",
+ "status": "success",
+ "id": "98t47ve63",
+ "timestamp": 1775143501606
+ },
+ {
+ "service": "apache",
+ "action": "start",
+ "status": "success",
+ "id": "vnmqn0rrq",
+ "timestamp": 1775143500920
+ },
+ {
+ "service": "nginx",
+ "action": "start",
+ "status": "success",
+ "id": "341eo8e00",
+ "timestamp": 1775143500866
+ },
+ {
+ "service": "mariadb:11.4.2",
+ "action": "exit",
+ "status": "failure",
+ "details": "Exit code: null",
+ "id": "vjpzdhxwg",
+ "timestamp": 1775143433818
+ },
+ {
+ "service": "php:5.6.40",
+ "action": "exit",
+ "status": "failure",
+ "details": "Exit code: null",
+ "id": "pc1vvmqto",
+ "timestamp": 1775143433793
+ },
+ {
+ "service": "php:7.4.33",
+ "action": "exit",
+ "status": "failure",
+ "details": "Exit code: null",
+ "id": "4cph2zy9r",
+ "timestamp": 1775143433785
+ },
+ {
+ "service": "php:8.2.30",
+ "action": "exit",
+ "status": "failure",
+ "details": "Exit code: null",
+ "id": "6fcacmn91",
+ "timestamp": 1775143433774
+ },
+ {
+ "service": "php:8.5.4",
+ "action": "exit",
+ "status": "failure",
+ "details": "Exit code: null",
+ "id": "66gmzyj2b",
+ "timestamp": 1775143433764
+ },
+ {
+ "service": "mariadb:10.11.8",
+ "action": "exit",
+ "status": "failure",
+ "details": "Exit code: null",
+ "id": "xv5p9bdsn",
+ "timestamp": 1775143433757
+ },
+ {
+ "service": "apache",
+ "action": "exit",
+ "status": "failure",
+ "details": "Exit code: null",
+ "id": "rayaa4o0h",
+ "timestamp": 1775143433717
+ },
+ {
+ "service": "nginx",
+ "action": "exit",
+ "status": "failure",
+ "details": "Exit code: null",
+ "id": "tu0rebj5r",
+ "timestamp": 1775143433715
+ },
+ {
+ "service": "php:5.6.40",
+ "action": "stop",
+ "status": "info",
+ "id": "afjy6gk5a",
+ "timestamp": 1775143433687
+ },
+ {
+ "service": "php:7.4.33",
+ "action": "stop",
+ "status": "info",
+ "id": "rlvru3y4u",
+ "timestamp": 1775143433669
+ },
+ {
+ "service": "php:8.2.30",
+ "action": "stop",
+ "status": "info",
+ "id": "kzkyyigtb",
+ "timestamp": 1775143433652
+ },
+ {
+ "service": "php:8.5.4",
+ "action": "stop",
+ "status": "info",
+ "id": "flijmjctw",
+ "timestamp": 1775143433633
+ },
+ {
+ "service": "mariadb:10.11.8",
+ "action": "stop",
+ "status": "info",
+ "id": "h1zul1uti",
+ "timestamp": 1775143433612
+ },
+ {
+ "service": "mariadb:11.4.2",
+ "action": "stop",
+ "status": "info",
+ "id": "4iy7wdckh",
+ "timestamp": 1775143433599
+ },
+ {
+ "service": "apache",
+ "action": "stop",
+ "status": "info",
+ "id": "65kqv87pb",
+ "timestamp": 1775143433584
+ },
+ {
+ "service": "nginx",
+ "action": "stop",
+ "status": "info",
+ "id": "j25ifpr5s",
+ "timestamp": 1775143433578
+ },
+ {
+ "service": "php:5.6.40",
+ "action": "start",
+ "status": "success",
+ "id": "9up5u5ywc",
+ "timestamp": 1775143193051
+ },
+ {
+ "service": "php:7.4.33",
+ "action": "start",
+ "status": "success",
+ "id": "kuqgzsj0b",
+ "timestamp": 1775143193031
+ },
+ {
+ "service": "php:8.2.30",
+ "action": "start",
+ "status": "success",
+ "id": "00sjap3r2",
+ "timestamp": 1775143193007
+ },
+ {
+ "service": "php:8.5.4",
+ "action": "start",
+ "status": "success",
+ "id": "je65w6t91",
+ "timestamp": 1775143192979
+ },
+ {
+ "service": "mariadb:10.11.8",
+ "action": "start",
+ "status": "success",
+ "id": "7y3b8ant5",
+ "timestamp": 1775143192795
+ },
+ {
+ "service": "mariadb:11.4.2",
+ "action": "start",
+ "status": "success",
+ "id": "gzyh7shwf",
+ "timestamp": 1775143192771
+ },
+ {
+ "service": "apache",
+ "action": "start",
+ "status": "success",
+ "id": "gh7ngadcy",
+ "timestamp": 1775143191998
+ },
+ {
+ "service": "nginx",
+ "action": "start",
+ "status": "success",
+ "id": "jvnhlgrej",
+ "timestamp": 1775143191937
+ },
+ {
+ "service": "nginx",
+ "action": "start",
+ "status": "success",
+ "id": "6qwfc6xxe",
+ "timestamp": 1775143002954
+ },
+ {
+ "service": "nginx",
+ "action": "exit",
+ "status": "failure",
+ "details": "Exit code: 1",
+ "id": "68bv8y8uc",
+ "timestamp": 1775143001572
+ },
+ {
+ "service": "php:7.4.33",
+ "action": "start",
+ "status": "success",
+ "id": "era4415c5",
+ "timestamp": 1775142913122
+ },
+ {
+ "service": "php:8.2.30",
+ "action": "start",
+ "status": "success",
+ "id": "ylnxpe014",
+ "timestamp": 1775142913100
+ },
+ {
+ "service": "php:8.5.4",
+ "action": "start",
+ "status": "success",
+ "id": "04d0v9wr4",
+ "timestamp": 1775142913078
+ },
+ {
+ "service": "mariadb:10.11.8",
+ "action": "start",
+ "status": "success",
+ "id": "magvgbbsw",
+ "timestamp": 1775142912815
+ },
+ {
+ "service": "mariadb:11.4.2",
+ "action": "start",
+ "status": "success",
+ "id": "wt4whxsj0",
+ "timestamp": 1775142912792
+ },
+ {
+ "service": "apache",
+ "action": "start",
+ "status": "success",
+ "id": "80emjncsz",
+ "timestamp": 1775142912095
+ },
+ {
+ "service": "nginx",
+ "action": "start",
+ "status": "success",
+ "id": "6b1jpu41d",
+ "timestamp": 1775142912021
+ },
+ {
+ "service": "nginx",
+ "action": "exit",
+ "status": "failure",
+ "details": "Exit code: 1",
+ "id": "sijsuavf4",
+ "timestamp": 1775142869149
+ },
+ {
+ "service": "nginx",
+ "action": "start",
+ "status": "success",
+ "id": "tbga3ugmp",
+ "timestamp": 1775142869120
+ },
+ {
+ "service": "nginx",
+ "action": "exit",
+ "status": "failure",
+ "details": "Exit code: 1",
+ "id": "mdo9t7pvn",
+ "timestamp": 1775142847681
+ },
+ {
+ "service": "nginx",
+ "action": "start",
+ "status": "success",
+ "id": "ts6kfp9dr",
+ "timestamp": 1775142847648
+ },
+ {
+ "service": "php:5.6.40",
+ "action": "start",
+ "status": "success",
+ "id": "vl9jmbalt",
+ "timestamp": 1775142837332
+ },
+ {
+ "service": "nginx",
+ "action": "exit",
+ "status": "failure",
+ "details": "Exit code: 1",
+ "id": "s8tjspdk5",
+ "timestamp": 1775142836335
+ },
+ {
+ "service": "nginx",
+ "action": "start",
+ "status": "success",
+ "id": "2ufvwxt8u",
+ "timestamp": 1775142836304
+ },
+ {
+ "service": "nginx",
+ "action": "exit",
+ "status": "failure",
+ "details": "Exit code: 1",
+ "id": "penppitxf",
+ "timestamp": 1775142834342
+ },
+ {
+ "service": "nginx",
+ "action": "start",
+ "status": "success",
+ "id": "htcsimfit",
+ "timestamp": 1775142834306
+ },
+ {
+ "service": "nginx",
+ "action": "exit",
+ "status": "failure",
+ "details": "Exit code: 1",
+ "id": "z76k0bw25",
+ "timestamp": 1775142789972
+ },
+ {
+ "service": "nginx",
+ "action": "start",
+ "status": "success",
+ "id": "baes7toxm",
+ "timestamp": 1775142789936
+ },
+ {
+ "service": "php:5.6.40",
+ "action": "start",
+ "status": "success",
+ "id": "022oillmk",
+ "timestamp": 1775142780726
+ },
+ {
+ "service": "nginx",
+ "action": "exit",
+ "status": "failure",
+ "details": "Exit code: 1",
+ "id": "l88y6d0v2",
+ "timestamp": 1775142779708
+ },
+ {
+ "service": "nginx",
+ "action": "start",
+ "status": "success",
+ "id": "zmujdd0pg",
+ "timestamp": 1775142779677
+ },
+ {
+ "service": "nginx",
+ "action": "exit",
+ "status": "failure",
+ "details": "Exit code: 1",
+ "id": "5nj4pt44o",
+ "timestamp": 1775142775865
+ },
+ {
+ "service": "nginx",
+ "action": "start",
+ "status": "success",
+ "id": "6nhqa75ti",
+ "timestamp": 1775142775829
+ },
+ {
+ "service": "apache",
+ "action": "start",
+ "status": "success",
+ "id": "1rzo1kmu5",
+ "timestamp": 1775142748429
+ },
+ {
+ "service": "nginx",
+ "action": "exit",
+ "status": "failure",
+ "details": "Exit code: 1",
+ "id": "6l9vyd62l",
+ "timestamp": 1775142692713
+ },
+ {
+ "service": "nginx",
+ "action": "start",
+ "status": "success",
+ "id": "nx53rk1x4",
+ "timestamp": 1775142692680
+ },
+ {
+ "service": "nginx",
+ "action": "exit",
+ "status": "failure",
+ "details": "Exit code: 1",
+ "id": "3rrl38cuc",
+ "timestamp": 1775142684006
+ },
+ {
+ "service": "nginx",
+ "action": "start",
+ "status": "success",
+ "id": "qgm12pq29",
+ "timestamp": 1775142683973
+ },
+ {
+ "service": "nginx",
+ "action": "exit",
+ "status": "failure",
+ "details": "Exit code: 1",
+ "id": "j6uk4e2f8",
+ "timestamp": 1775142613402
+ },
+ {
+ "service": "nginx",
+ "action": "start",
+ "status": "success",
+ "id": "5k8onfmkz",
+ "timestamp": 1775142613368
+ },
+ {
+ "service": "nginx",
+ "action": "exit",
+ "status": "failure",
+ "details": "Exit code: 1",
+ "id": "vb5elfiji",
+ "timestamp": 1775142536182
+ },
+ {
+ "service": "nginx",
+ "action": "start",
+ "status": "success",
+ "id": "87zxbn8td",
+ "timestamp": 1775142536150
+ },
+ {
+ "service": "nginx",
+ "action": "exit",
+ "status": "failure",
+ "details": "Exit code: 1",
+ "id": "xlr096zx9",
+ "timestamp": 1775142518127
+ },
+ {
+ "service": "nginx",
+ "action": "start",
+ "status": "success",
+ "id": "kn1jgqpdw",
+ "timestamp": 1775142518090
+ },
+ {
+ "service": "nginx",
+ "action": "exit",
+ "status": "failure",
+ "details": "Exit code: 1",
+ "id": "lcii7xp4g",
+ "timestamp": 1775142496016
+ },
+ {
+ "service": "nginx",
+ "action": "start",
+ "status": "success",
+ "id": "3o3q506oe",
+ "timestamp": 1775142495986
+ },
+ {
+ "service": "nginx",
+ "action": "exit",
+ "status": "failure",
+ "details": "Exit code: 1",
+ "id": "rg9lrvp9s",
+ "timestamp": 1775142494090
+ },
+ {
+ "service": "nginx",
+ "action": "start",
+ "status": "success",
+ "id": "x4anort95",
+ "timestamp": 1775142494051
+ },
+ {
+ "service": "apache",
+ "action": "start",
+ "status": "success",
+ "id": "tpypbu4a2",
+ "timestamp": 1775142463063
+ },
+ {
+ "service": "apache",
+ "action": "exit",
+ "status": "failure",
+ "details": "Exit code: 1",
+ "id": "cibkw9iht",
+ "timestamp": 1775142461628
+ },
+ {
+ "service": "nginx",
+ "action": "exit",
+ "status": "failure",
+ "details": "Exit code: 1",
+ "id": "4vjm17x7m",
+ "timestamp": 1775142346271
+ },
+ {
+ "service": "nginx",
+ "action": "start",
+ "status": "success",
+ "id": "a2lgfzufq",
+ "timestamp": 1775142346240
+ },
+ {
+ "service": "nginx",
+ "action": "exit",
+ "status": "failure",
+ "details": "Exit code: 1",
+ "id": "adxmi5o2h",
+ "timestamp": 1775142345209
+ },
+ {
+ "service": "nginx",
+ "action": "start",
+ "status": "success",
+ "id": "6dkhjh8hj",
+ "timestamp": 1775142345173
+ },
+ {
+ "service": "nginx",
+ "action": "exit",
+ "status": "failure",
+ "details": "Exit code: 1",
+ "id": "yq2h3d1pe",
+ "timestamp": 1775142343008
+ },
+ {
+ "service": "nginx",
+ "action": "start",
+ "status": "success",
+ "id": "0vvot89i7",
+ "timestamp": 1775142342969
+ },
+ {
+ "service": "nginx",
+ "action": "exit",
+ "status": "failure",
+ "details": "Exit code: 1",
+ "id": "umytdimhg",
+ "timestamp": 1775142333906
+ },
+ {
+ "service": "nginx",
+ "action": "start",
+ "status": "success",
+ "id": "tggk9txe0",
+ "timestamp": 1775142333872
+ },
+ {
+ "service": "nginx",
+ "action": "exit",
+ "status": "failure",
+ "details": "Exit code: 1",
+ "id": "nicwfu4qq",
+ "timestamp": 1775142332841
+ },
+ {
+ "service": "nginx",
+ "action": "start",
+ "status": "success",
+ "id": "mrvde3fi3",
+ "timestamp": 1775142332805
+ },
+ {
+ "service": "nginx",
+ "action": "exit",
+ "status": "failure",
+ "details": "Exit code: 1",
+ "id": "husmdcfzd",
+ "timestamp": 1775142316335
+ },
+ {
+ "service": "nginx",
+ "action": "start",
+ "status": "success",
+ "id": "2c15mlt5r",
+ "timestamp": 1775142316297
+ },
+ {
+ "service": "nginx",
+ "action": "exit",
+ "status": "failure",
+ "details": "Exit code: 1",
+ "id": "jlvnl4ibm",
+ "timestamp": 1775142307210
+ },
+ {
+ "service": "nginx",
+ "action": "start",
+ "status": "success",
+ "id": "dosb7oy4w",
+ "timestamp": 1775142307177
+ },
+ {
+ "service": "nginx",
+ "action": "exit",
+ "status": "failure",
+ "details": "Exit code: 1",
+ "id": "00aj71xlj",
+ "timestamp": 1775142303019
+ },
+ {
+ "service": "nginx",
+ "action": "start",
+ "status": "success",
+ "id": "3k615afiz",
+ "timestamp": 1775142302985
+ },
+ {
+ "service": "php:5.6.40",
+ "action": "start",
+ "status": "success",
+ "id": "syy1zj7yy",
+ "timestamp": 1775142289974
+ },
+ {
+ "service": "php:7.4.33",
+ "action": "start",
+ "status": "success",
+ "id": "u4z4rrc2h",
+ "timestamp": 1775142289944
+ },
+ {
+ "service": "php:8.2.30",
+ "action": "start",
+ "status": "success",
+ "id": "g5y31kff0",
+ "timestamp": 1775142289923
+ },
+ {
+ "service": "php:8.5.4",
+ "action": "start",
+ "status": "success",
+ "id": "iffrkdzg2",
+ "timestamp": 1775142289901
+ },
+ {
+ "service": "mariadb:11.4.2",
+ "action": "start",
+ "status": "success",
+ "id": "1o9qrkuv7",
+ "timestamp": 1775142289792
+ },
+ {
+ "service": "mariadb:10.11.8",
+ "action": "start",
+ "status": "success",
+ "id": "j80370nza",
+ "timestamp": 1775142289771
+ },
+ {
+ "service": "apache",
+ "action": "start",
+ "status": "success",
+ "id": "u40buu1dw",
+ "timestamp": 1775142288919
+ },
+ {
+ "service": "nginx",
+ "action": "exit",
+ "status": "failure",
+ "details": "Exit code: 1",
+ "id": "8uq76phpb",
+ "timestamp": 1775142288903
+ },
+ {
+ "service": "nginx",
+ "action": "start",
+ "status": "success",
+ "id": "attwi93sv",
+ "timestamp": 1775142288840
+ },
+ {
+ "service": "php:5.6.40",
+ "action": "start",
+ "status": "success",
+ "id": "p1ipjfvmt",
+ "timestamp": 1775141316766
+ },
+ {
+ "service": "php:7.4.33",
+ "action": "start",
+ "status": "success",
+ "id": "qhsh2ol3o",
+ "timestamp": 1775141316746
+ },
+ {
+ "service": "php:8.2.30",
+ "action": "start",
+ "status": "success",
+ "id": "vbu4gxs6w",
+ "timestamp": 1775141316726
+ },
+ {
+ "service": "php:8.5.4",
+ "action": "start",
+ "status": "success",
+ "id": "33ox6fh1v",
+ "timestamp": 1775141316704
+ },
+ {
+ "service": "mariadb:11.4.2",
+ "action": "start",
+ "status": "success",
+ "id": "odzgjwnbi",
+ "timestamp": 1775141316402
+ },
+ {
+ "service": "mariadb:10.11.8",
+ "action": "start",
+ "status": "success",
+ "id": "5oyqe4gkz",
+ "timestamp": 1775141316340
+ },
+ {
+ "service": "apache",
+ "action": "start",
+ "status": "success",
+ "id": "ecuuyspno",
+ "timestamp": 1775141315714
+ },
+ {
+ "service": "nginx",
+ "action": "start",
+ "status": "success",
+ "id": "qg9upkvg2",
+ "timestamp": 1775141315654
+ },
+ {
+ "service": "mariadb:11.4.2",
+ "action": "start",
+ "status": "success",
+ "id": "o6j9i3dev",
+ "timestamp": 1775141081322
+ },
{
"service": "apache",
"action": "start",
diff --git a/settings.json b/settings.json
index 9ca7e43..28d22f4 100644
--- a/settings.json
+++ b/settings.json
@@ -1,5 +1,5 @@
{
- "nginxPort": "80",
+ "nginxPort": "8081",
"apachePort": "8080",
"serverType": "nginx",
"phpPort": "9001",
diff --git a/src/main/ipc/index.ts b/src/main/ipc/index.ts
index ffec901..e908206 100644
--- a/src/main/ipc/index.ts
+++ b/src/main/ipc/index.ts
@@ -1,4 +1,5 @@
import { ipcMain, app, dialog, shell } from 'electron'
+import { exec } from 'child_process'
import fs from 'fs'
import { processManager } from '../services/ProcessManager'
import { configService } from '../services/ConfigService'
@@ -15,6 +16,7 @@ import { mariaDbOperationService } from '../services/MariaDbOperationService'
import { cliAliasService } from '../services/CliAliasService'
import { cliBinService } from '../services/CliBinService'
import { serviceEventLogger } from '../services/ServiceEventLogger'
+import { hostsService } from '../services/HostsService'
function findExecutable(dir: string, name: string): string | null {
if (!fs.existsSync(dir)) return null
@@ -44,13 +46,14 @@ async function rebuildNginxConfig(forceProjects: boolean = false): Promise<{ bin
const isWindows = process.platform === 'win32';
const binPath = path.join(nginxPrefix, isWindows ? 'nginx.exe' : 'nginx')
const confDir = path.join(nginxPrefix, 'conf')
- const logDir = path.join(configService.getBasePath(), 'logs')
const rootPath = path.join(configService.getBasePath(), 'www')
if (!fs.existsSync(rootPath)) fs.mkdirSync(rootPath, { recursive: true })
// Ensure directories exist
const projectsConfDir = path.join(configService.getBasePath(), 'generated_configs', 'projects')
+ const locationsConfDir = path.join(configService.getBasePath(), 'generated_configs', 'projects_locations')
if (!fs.existsSync(projectsConfDir)) fs.mkdirSync(projectsConfDir, { recursive: true })
+ if (!fs.existsSync(locationsConfDir)) fs.mkdirSync(locationsConfDir, { recursive: true })
const tempDirs = ['temp', 'temp/client_body_temp', 'temp/proxy_temp', 'temp/fastcgi_temp', 'temp/uwsgi_temp', 'temp/scgi_temp', 'logs']
for (const dir of tempDirs) {
@@ -59,8 +62,15 @@ async function rebuildNginxConfig(forceProjects: boolean = false): Promise<{ bin
}
// Generate project configs
- const allProjects = await projectService.getProjects()
- const projects = allProjects.filter(p => !p.serverType || p.serverType === 'nginx')
+ const projects = projectService.getProjects().filter(p => !p.serverType || p.serverType === 'nginx')
+
+ if (forceProjects) {
+ const files = fs.readdirSync(projectsConfDir)
+ for (const file of files) {
+ if (file.endsWith('.conf')) fs.unlinkSync(path.join(projectsConfDir, file))
+ }
+ }
+
for (const p of projects) {
const projectConfPath = path.join(projectsConfDir, `${p.host}.conf`)
let shouldGenerate = forceProjects || !fs.existsSync(projectConfPath)
@@ -82,11 +92,24 @@ async function rebuildNginxConfig(forceProjects: boolean = false): Promise<{ bin
normalizedPath += '/'
}
+ // Auto-suffix hostname if it's a simple slug
+ const finalHost = p.host.includes('.') ? p.host : `${p.host}.local`
+
await configService.generateConfig('nginx_project.conf.template', `projects/${p.host}.conf`, {
- HOST: p.host,
+ HOST: finalHost,
PATH: normalizedPath,
PHP_PORT: phpPort.toString(),
- CONF_DIR: confDir.replace(/\\/g, '/')
+ CONF_DIR: confDir.replace(/\\/g, '/'),
+ 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`, {
+ HOST: p.host, // Use simple host slug for location path
+ PATH: normalizedPath,
+ PHP_PORT: phpPort.toString(),
+ CONF_DIR: confDir.replace(/\\/g, '/'),
+ PORT: settings.nginxPort
})
}
}
@@ -95,15 +118,20 @@ async function rebuildNginxConfig(forceProjects: boolean = false): Promise<{ bin
? `include "${projectsConfDir.replace(/\\/g, '/')}/*.conf";`
: ''
+ const locationsInclusion = projects.length > 0
+ ? `include "${locationsConfDir.replace(/\\/g, '/')}/*.conf";`
+ : ''
+
const configPath = await configService.generateConfig('nginx.conf.template', 'nginx.conf', {
- PORT: settings.nginxPort,
+ PORT: settings.nginxPort.toString(),
+ ROOT: rootPath.replace(/\\/g, '/'),
LISTEN_ADDRESS: settings.allowRemoteAccess ? '0.0.0.0' : '127.0.0.1',
- PHP_PORT: settings.phpPort,
- ROOT: rootPath,
- LOG_DIR: logDir.replace(/\\/g, '/'),
+ PHP_PORT: settings.phpPort.toString(),
+ LOG_DIR: path.join(nginxPrefix, 'logs').replace(/\\/g, '/'),
CONF_DIR: confDir.replace(/\\/g, '/'),
- PHPMYADMIN_DIR: phpMyAdminService.getPath().replace(/\\/g, '/'),
- PROJECTS: projectsInclusion
+ PHPMYADMIN_DIR: path.join(configService.getBasePath(), 'bin', 'phpmyadmin').replace(/\\/g, '/'),
+ PROJECTS: projectsInclusion,
+ PROJECT_LOCATIONS: locationsInclusion
})
return { binPath, nginxPrefix, configPath }
@@ -111,7 +139,7 @@ async function rebuildNginxConfig(forceProjects: boolean = false): Promise<{ bin
async function restartNginx(): Promise<{ success: boolean; message: string }> {
try {
- const { binPath, nginxPrefix, configPath } = await rebuildNginxConfig(false)
+ const { binPath, nginxPrefix, configPath } = await rebuildNginxConfig(true)
// Force kill Nginx to ensure it picks up new config on Windows
await processManager.forceKillAll('nginx')
@@ -156,8 +184,6 @@ async function rebuildApacheConfig(forceProjects: boolean = false): Promise<{ bi
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(configService.getBasePath(), 'www')
if (!fs.existsSync(rootPath)) fs.mkdirSync(rootPath, { recursive: true })
@@ -175,11 +201,19 @@ async function rebuildApacheConfig(forceProjects: boolean = false): Promise<{ bi
// Ensure directories exist
const projectsConfDir = path.join(configService.getBasePath(), 'generated_configs', 'projects_apache')
+ const aliasesConfDir = path.join(configService.getBasePath(), 'generated_configs', 'projects_aliases_apache')
if (!fs.existsSync(projectsConfDir)) fs.mkdirSync(projectsConfDir, { recursive: true })
+ if (!fs.existsSync(aliasesConfDir)) fs.mkdirSync(aliasesConfDir, { recursive: true })
- // Generate project configs
- const allProjects = await projectService.getProjects()
- const projects = allProjects.filter(p => p.serverType === 'apache')
+ if (forceProjects) {
+ const files = fs.readdirSync(projectsConfDir)
+ for (const file of files) {
+ if (file.endsWith('.conf')) fs.unlinkSync(path.join(projectsConfDir, file))
+ }
+ }
+
+ // Ensure projects exist
+ const projects = projectService.getProjects().filter(p => p.serverType === 'apache')
for (const p of projects) {
const projectConfPath = path.join(projectsConfDir, `${p.host}.conf`)
let shouldGenerate = forceProjects || !fs.existsSync(projectConfPath)
@@ -200,13 +234,28 @@ async function rebuildApacheConfig(forceProjects: boolean = false): Promise<{ bi
const phpDir = path.join(configService.getBasePath(), 'bin', `php-${p.phpVersion}`)
const phpCgiBinary = findExecutable(phpDir, process.platform === 'win32' ? 'php-cgi.exe' : 'php-cgi') || ''
+ // Auto-suffix hostname if it's a simple slug
+ const finalHost = p.host.includes('.') ? p.host : `${p.host}.local`
+
await configService.generateConfig('apache_project.conf.template', `projects_apache/${p.host}.conf`, {
+ HOST: finalHost,
+ PATH: normalizedPath,
+ PHP_PORT: phpPort.toString(),
+ PHP_DIR: phpDir.replace(/\\/g, '/'),
+ PHP_CGI_PATH: phpCgiBinary.replace(/\\/g, '/'),
+ PHP_BIN_URL: `/php-bin-${p.host}`,
+ PORT: settings.apachePort || '8080'
+ })
+
+ // Also generate the subdirectory/alias-style config for localhost fallback
+ await configService.generateConfig('apache_project_alias.conf.template', `projects_aliases_apache/${p.host}.conf`, {
HOST: p.host,
PATH: normalizedPath,
PHP_PORT: phpPort.toString(),
PHP_DIR: phpDir.replace(/\\/g, '/'),
PHP_CGI_PATH: phpCgiBinary.replace(/\\/g, '/'),
- PHP_BIN_URL: `/php-bin-${p.host}`
+ PHP_BIN_URL: `/php-bin-alias-${p.host}`,
+ PORT: settings.apachePort || '8080'
})
}
}
@@ -216,15 +265,20 @@ async function rebuildApacheConfig(forceProjects: boolean = false): Promise<{ bi
? `Include "${projectsConfDir.replace(/\\/g, '/')}/*.conf"`
: ''
+ const aliasesInclusion = projects.length > 0
+ ? `Include "${aliasesConfDir.replace(/\\/g, '/')}/*.conf"`
+ : ''
+
await configService.generateConfig('httpd.conf.template', 'httpd.conf', {
- PORT: settings.apachePort || '8080',
+ PORT: settings.apachePort.toString(),
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
+ PHP_PORT: settings.phpPort.toString(),
+ LOG_DIR: path.join(configService.getBasePath(), 'logs').replace(/\\/g, '/'),
+ SRVROOT: path.join(configService.getBasePath(), 'bin', 'apache', 'Apache24').replace(/\\/g, '/'),
+ PHPMYADMIN_DIR: path.join(configService.getBasePath(), 'bin', 'phpmyadmin').replace(/\\/g, '/'),
+ PROJECTS: projectsInclusion,
+ PROJECT_ALIASES: aliasesInclusion
})
}
@@ -233,7 +287,7 @@ async function rebuildApacheConfig(forceProjects: boolean = false): Promise<{ bi
async function restartApache(): Promise<{ success: boolean; message: string }> {
try {
- const { binPath, configPath } = await rebuildApacheConfig(false)
+ const { binPath, configPath } = await rebuildApacheConfig(true)
const settings = configService.getSettings()
await processManager.forceKillAll('apache')
@@ -796,10 +850,32 @@ export function registerIpcHandlers(): void {
return result.filePath
})
- ipcMain.handle('shell:open-path', async (_event, pathStr: string) => {
+ ipcMain.handle('open-path', async (_event, pathStr: string) => {
return await shell.openPath(pathStr)
})
+ ipcMain.handle('open-vscode', async (_event, pathStr: string) => {
+ return new Promise((resolve) => {
+ exec(`code "${pathStr}"`, (error) => {
+ if (error) {
+ console.error('Failed to open VS Code:', error)
+ resolve({ success: false, message: error.message })
+ } else {
+ resolve({ success: true })
+ }
+ })
+ })
+ })
+
+ // Hosts file management
+ ipcMain.handle('hosts:check', async (_event, hostname: string) => {
+ return await hostsService.checkEntry(hostname)
+ })
+
+ ipcMain.handle('hosts:add', async (_event, hostname: string) => {
+ return await hostsService.addEntry(hostname)
+ })
+
ipcMain.handle('binaries:download', async (_event, { name, url }) => {
const result = await downloadService.downloadAndExtract(url, name, (p) => {
// TODO: Emit progress to renderer via webContents.send
diff --git a/src/main/services/HostsService.ts b/src/main/services/HostsService.ts
new file mode 100644
index 0000000..d45c9f7
--- /dev/null
+++ b/src/main/services/HostsService.ts
@@ -0,0 +1,73 @@
+import fs from 'fs'
+import path from 'path'
+import { exec } from 'child_process'
+
+export class HostsService {
+ private hostsPath: string
+
+ constructor() {
+ // Windows: C:\Windows\System32\drivers\etc\hosts
+ // Linux/macOS: /etc/hosts
+ this.hostsPath = process.platform === 'win32'
+ ? path.join(process.env.windir || 'C:\\Windows', 'System32', 'drivers', 'etc', 'hosts')
+ : '/etc/hosts'
+ }
+
+ async checkEntry(hostname: string): Promise {
+ try {
+ if (!fs.existsSync(this.hostsPath)) return false
+ const content = fs.readFileSync(this.hostsPath, 'utf8')
+
+ // Look for 127.0.0.1 or ::1
+ const regex = new RegExp(`(^|\\s)(127\\.0\\.0\\.1|::1)\\s+${hostname.replace(/\./g, '\\.')}(\\s|$)`, 'm')
+ return regex.test(content)
+ } catch (error) {
+ console.error('Failed to read hosts file:', error)
+ return false
+ }
+ }
+
+ async addEntry(hostname: string): Promise<{ success: boolean; message: string }> {
+ if (await this.checkEntry(hostname)) {
+ return { success: true, message: 'Entry already exists' }
+ }
+
+ return new Promise((resolve) => {
+ if (process.platform === 'win32') {
+ // Windows: Use PowerShell with -Verb RunAs for UAC prompt
+ // Using `n (newline) to ensure we don't append to the last line's end
+ const cmd = `powershell -Command "Start-Process powershell -ArgumentList '-Command Add-Content -Path $env:windir\\System32\\drivers\\etc\\hosts -Value \"\`n127.0.0.1 ${hostname}\" -Force' -Verb RunAs"`
+
+ exec(cmd, (error) => {
+ if (error) {
+ console.error('Failed to update hosts file:', error)
+ resolve({ success: false, message: 'Yönetici izni (UAC) reddedildi veya bir hata oluştu.' })
+ } else {
+ // Success doesn't mean the entry was actually added (could have been cancelled in UAC)
+ // So we wait a bit and re-check
+ setTimeout(async () => {
+ const verified = await this.checkEntry(hostname)
+ if (verified) {
+ resolve({ success: true, message: 'Hosts dosyası başarıyla güncellendi.' })
+ } else {
+ resolve({ success: false, message: 'Kayıt eklenemedi. Lütfen yönetici iznini onaylayın.' })
+ }
+ }, 2000)
+ }
+ })
+ } else {
+ // Linux/macOS: Use sudo
+ const cmd = `echo "127.0.0.1 ${hostname}" | sudo tee -a ${this.hostsPath}`
+ exec(cmd, (error) => {
+ if (error) {
+ resolve({ success: false, message: error.message })
+ } else {
+ resolve({ success: true, message: 'Hosts file updated.' })
+ }
+ })
+ }
+ })
+ }
+}
+
+export const hostsService = new HostsService()
diff --git a/src/main/services/PhpManagerService.ts b/src/main/services/PhpManagerService.ts
index a5eca3c..85909f3 100644
--- a/src/main/services/PhpManagerService.ts
+++ b/src/main/services/PhpManagerService.ts
@@ -268,11 +268,15 @@ export class PhpManagerService {
const iniPath = path.join(phpDir, 'php.ini')
if (!fs.existsSync(iniPath)) return false
+ const sessionsDir = path.join(configService.getBasePath(), 'sessions').replace(/\\/g, '/')
+ if (!fs.existsSync(sessionsDir)) fs.mkdirSync(sessionsDir, { recursive: true })
+
let content = fs.readFileSync(iniPath, 'utf8')
const lines = content.split(/\r?\n/)
let forceRedirectSet = false
let fixPathinfoSet = false
+ let sessionPathSet = false
const newLines = lines.map(line => {
const trimmed = line.trim()
@@ -284,11 +288,16 @@ export class PhpManagerService {
fixPathinfoSet = true
return 'cgi.fix_pathinfo = 1'
}
+ if (trimmed.startsWith('session.save_path') || trimmed.startsWith(';session.save_path')) {
+ sessionPathSet = true
+ return `session.save_path = "${sessionsDir}"`
+ }
return line
})
if (!forceRedirectSet) newLines.push('cgi.force_redirect = 0')
if (!fixPathinfoSet) newLines.push('cgi.fix_pathinfo = 1')
+ if (!sessionPathSet) newLines.push(`session.save_path = "${sessionsDir}"`)
fs.writeFileSync(iniPath, newLines.join('\n'), 'utf8')
return true
diff --git a/src/main/services/ProjectService.ts b/src/main/services/ProjectService.ts
index 2bd365c..3a010f0 100644
--- a/src/main/services/ProjectService.ts
+++ b/src/main/services/ProjectService.ts
@@ -34,7 +34,17 @@ export class ProjectService {
}
private saveProjects() {
- fs.writeFileSync(this.projectsFile, JSON.stringify(this.projects, null, 2))
+ const tempFile = `${this.projectsFile}.tmp`
+ try {
+ fs.writeFileSync(tempFile, JSON.stringify(this.projects, null, 2))
+ if (fs.existsSync(tempFile)) {
+ fs.renameSync(tempFile, this.projectsFile)
+ }
+ } catch (e) {
+ console.error('Failed to save projects safely:', e)
+ // Fallback to direct write if rename fails
+ fs.writeFileSync(this.projectsFile, JSON.stringify(this.projects, null, 2))
+ }
}
getProjects(): Project[] {
diff --git a/src/renderer/src/App.tsx b/src/renderer/src/App.tsx
index e190334..3786169 100644
--- a/src/renderer/src/App.tsx
+++ b/src/renderer/src/App.tsx
@@ -350,10 +350,6 @@ function App(): JSX.Element {
const interval = setInterval(() => {
fetchStatus()
- // fetchProjects() // Reduced frequency for these
- // fetchPhpVersions()
- // fetchMariaDbVersions()
- // fetchNginxVersions()
}, 3000)
let logInterval: NodeJS.Timeout | null = null
@@ -1135,8 +1131,6 @@ function App(): JSX.Element {
{activeTab === 'user_guide' && }
{activeTab === 'settings' && (
-
-
setSettingsTab(v)} textColor="primary" indicatorColor="primary" variant="scrollable" scrollButtons="auto">
} iconPosition="start" />
@@ -1537,8 +1531,6 @@ function App(): JSX.Element {
{activeTab === 'dashboard' && (
<>
-
-
setDashboardTab(v)} textColor="primary" indicatorColor="primary">
@@ -1986,11 +1978,28 @@ function App(): JSX.Element {
}
fetchStatus();
}}
- onOpenSite={(p: any) => {
+ onOpenSite={(p: any, isHostMapped: boolean) => {
const port = p.serverType === 'apache' ? settings.apachePort : settings.nginxPort
- window.open(`http://localhost:${port}/${p.host}/`, '_blank')
+ if (isHostMapped) {
+ const finalHost = p.host && p.host.includes('.') ? p.host : `${p.host}.local`
+ window.open(`http://${finalHost}:${port}/`, '_blank')
+ } else {
+ window.open(`http://localhost:${port}/${p.host}/`, '_blank')
+ }
}}
onOpenFolder={(path: string) => window.api.invoke('open-path', path)}
+ onOpenVSCode={(path: string) => window.api.invoke('open-vscode', path)}
+ onAddHost={async (hostname: string) => {
+ const result = await window.api.invoke('hosts:add', hostname);
+ if (result.success) {
+ // Refresh current project to check mapping again
+ const currentId = selectedProjectId;
+ setSelectedProjectId(null);
+ setTimeout(() => setSelectedProjectId(currentId), 10);
+ } else {
+ alert(result.message);
+ }
+ }}
onManageDb={() => setActiveTab('db_manager')}
/>
)}
diff --git a/src/renderer/src/components/ProjectManager/ProjectDetails.tsx b/src/renderer/src/components/ProjectManager/ProjectDetails.tsx
index b66e758..8c8bc14 100644
--- a/src/renderer/src/components/ProjectManager/ProjectDetails.tsx
+++ b/src/renderer/src/components/ProjectManager/ProjectDetails.tsx
@@ -10,8 +10,10 @@ import {
Chip,
Divider,
Stack,
- Tooltip
+ Tooltip,
+ CircularProgress
} from '@mui/material'
+import { useState, useEffect } from 'react'
import {
Folder as ProjectIcon,
FolderOpen as FolderOpenIcon,
@@ -21,7 +23,8 @@ import {
Edit as EditIcon,
Delete as DeleteIcon,
Tune as TuningIcon,
- OpenInNew as OpenIcon
+ OpenInNew as OpenIcon,
+ Code as CodeIcon
} from '@mui/icons-material'
import ProjectResourceMonitor from './ProjectResourceMonitor'
@@ -35,9 +38,13 @@ interface ProjectDetailsProps {
onEdit: () => void;
onDelete: () => void;
onToggleStatus: () => void;
- onOpenSite: () => void;
+ onOpenSite: (useHost: boolean) => void;
onOpenFolder: () => void;
+ onOpenVSCode: () => void;
onManageDb: () => void;
+ onAddHost: (hostname: string) => void;
+ isTransitioning?: boolean;
+ setTransitioning?: (v: boolean) => void;
}
export default function ProjectDetails({
@@ -52,8 +59,20 @@ export default function ProjectDetails({
onToggleStatus,
onOpenSite,
onOpenFolder,
- onManageDb
+ onOpenVSCode,
+ onManageDb,
+ onAddHost,
+ isTransitioning = false,
+ setTransitioning
}: ProjectDetailsProps) {
+ const [isHostMapped, setIsHostMapped] = useState(true);
+
+ useEffect(() => {
+ if (p?.host) {
+ const finalHost = p.host.includes('.') ? p.host : `${p.host}.local`;
+ window.api.invoke('hosts:check', finalHost).then(setIsHostMapped);
+ }
+ }, [p]);
if (!p) {
return (
@@ -70,12 +89,12 @@ export default function ProjectDetails({
return (
-
-
-
-
-
-
+
+
+
+
+
+
{p.name}
@@ -86,35 +105,96 @@ export default function ProjectDetails({
-
-
- : }
- color={isRunning ? 'error' : 'primary'}
- onClick={onToggleStatus}
- >
- {isRunning ? t('projects.stop_site') : t('projects.start_site')}
-
- }
- onClick={onOpenSite}
- >
- {t('projects.open_site')}
-
-
-
-
-
-
-
-
-
-
-
+
+
+ : (isRunning ? : )}
+ color={isRunning ? 'error' : 'primary'}
+ onClick={async () => {
+ if (setTransitioning) setTransitioning(true);
+ try {
+ await onToggleStatus();
+ } finally {
+ if (setTransitioning) {
+ // Short delay to ensure status update is reflected
+ setTimeout(() => setTransitioning(false), 500);
+ }
+ }
+ }}
+ sx={{
+ px: 2,
+ height: 36,
+ fontWeight: 'bold',
+ transition: 'all 0.1s ease-in-out',
+ '&:active': { transform: 'scale(0.96)' }
+ }}
+ >
+ {isTransitioning ? t('common.processing') : (isRunning ? t('projects.stop_site') : t('projects.start_site'))}
+
+ }
+ onClick={() => onOpenSite(isHostMapped)}
+ sx={{
+ px: 2,
+ height: 36,
+ transition: 'all 0.1s ease-in-out',
+ '&:active': { transform: 'scale(0.96)' }
+ }}
+ >
+ {t('projects.open_site')}
+
+ }
+ onClick={onOpenVSCode}
+ sx={{
+ px: 2,
+ height: 36,
+ borderColor: 'rgba(255,255,255,0.1)',
+ color: 'rgba(255,255,255,0.7)',
+ transition: 'all 0.1s ease-in-out',
+ '&:active': { transform: 'scale(0.96)' }
+ }}
+ >
+ VS Code Aç
+
+ {!isHostMapped && (
+
+ )}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/renderer/src/components/ProjectManager/ProjectManager.tsx b/src/renderer/src/components/ProjectManager/ProjectManager.tsx
index b36e972..700ed61 100644
--- a/src/renderer/src/components/ProjectManager/ProjectManager.tsx
+++ b/src/renderer/src/components/ProjectManager/ProjectManager.tsx
@@ -17,8 +17,10 @@ interface ProjectManagerProps {
onEditProject: (p: any) => void;
onRemoveProject: (id: string) => void;
onToggleStatus: (p: any) => void;
- onOpenSite: (p: any) => void;
+ onOpenSite: (p: any, useHost: boolean) => void;
onOpenFolder: (path: string) => void;
+ onOpenVSCode: (path: string) => void;
+ onAddHost: (hostname: string) => void;
onManageDb: () => void;
}
@@ -39,6 +41,8 @@ export default function ProjectManager({
onToggleStatus,
onOpenSite,
onOpenFolder,
+ onOpenVSCode,
+ onAddHost,
onManageDb
}: ProjectManagerProps) {
@@ -74,8 +78,10 @@ export default function ProjectManager({
onEdit={() => onEditProject(p)}
onDelete={() => onRemoveProject(p.id)}
onToggleStatus={() => onToggleStatus(p)}
- onOpenSite={() => onOpenSite(p)}
+ onOpenSite={(useHost) => onOpenSite(p, useHost)}
onOpenFolder={() => onOpenFolder(p.path)}
+ onOpenVSCode={() => onOpenVSCode(p.path)}
+ onAddHost={onAddHost}
onManageDb={onManageDb}
/>
diff --git a/src/renderer/src/components/ProjectManager/ProjectResourceMonitor.tsx b/src/renderer/src/components/ProjectManager/ProjectResourceMonitor.tsx
index f98ea8b..0dd5cdd 100644
--- a/src/renderer/src/components/ProjectManager/ProjectResourceMonitor.tsx
+++ b/src/renderer/src/components/ProjectManager/ProjectResourceMonitor.tsx
@@ -1,4 +1,5 @@
-import { Box, Typography, LinearProgress } from '@mui/material'
+import { Box, Typography } from '@mui/material'
+import { Gauge, gaugeClasses } from '@mui/x-charts/Gauge'
interface ServiceInfo {
status: string;
@@ -13,7 +14,7 @@ interface ProjectResourceMonitorProps {
}
const formatMemory = (bytes?: number) => {
- if (!bytes) return ''
+ if (!bytes) return '0 MB'
const mb = bytes / (1024 * 1024)
if (mb > 1024) return `${(mb / 1024).toFixed(1)} GB`
return `${Math.round(mb)} MB`
@@ -25,54 +26,96 @@ export default function ProjectResourceMonitor({ serverStats, phpStats, serverTy
if (!hasServerStat && !hasPhpStat) return null;
- const renderMiniStats = (stats?: ServiceInfo, label: string = 'Service') => {
+ const renderServiceCard = (stats?: ServiceInfo, label: string = 'Service') => {
if (!stats) return null;
- const cpu = stats.cpu || 0;
+ const cpu = Math.max(0, Math.min(100, Math.round(stats.cpu || 0)));
const memory = stats.memory || 0;
const ramMB = Math.round(memory / (1024 * 1024));
const ramPercent = Math.min((ramMB / 1024) * 100, 100);
+
+ const getColor = (val: number) => {
+ if (val < 30) return '#33d9b2';
+ if (val < 70) return '#ffb142';
+ return '#ff5252';
+ }
return (
-
-
- {label.toUpperCase()}
-
- {cpu}%
- {formatMemory(memory)}
+
+
+ {label}
+
+
+
+ {/* CPU Gauge */}
+
+
+ ({
+ [`& .${gaugeClasses.valueText}`]: { fontSize: 14, fontWeight: 800, fill: getColor(cpu) },
+ [`& .${gaugeClasses.valueArc}`]: { fill: getColor(cpu) },
+ [`& .${gaugeClasses.referenceArc}`]: { fill: 'rgba(255,255,255,0.05)' },
+ })}
+ text={({ value }) => `${value}%`}
+ />
+
+ CPU
+
+
+ {/* RAM Gauge */}
+
+
+ ({
+ [`& .${gaugeClasses.valueText}`]: { fontSize: 12, fontWeight: 800, fill: 'secondary.light' },
+ [`& .${gaugeClasses.valueArc}`]: { fill: 'secondary.main' },
+ [`& .${gaugeClasses.referenceArc}`]: { fill: 'rgba(255,255,255,0.05)' },
+ })}
+ text={() => formatMemory(memory)}
+ />
+
+ RAM
-
-
-
-
);
};
return (
-
- {renderMiniStats(serverStats, serverType)}
- {renderMiniStats(phpStats, 'PHP')}
+
+ {renderServiceCard(serverStats, serverType)}
+ {renderServiceCard(phpStats, 'PHP')}
);
}
diff --git a/src/renderer/src/components/ProjectManager/ProjectSidebar.tsx b/src/renderer/src/components/ProjectManager/ProjectSidebar.tsx
index 5b3b23b..d30e64f 100644
--- a/src/renderer/src/components/ProjectManager/ProjectSidebar.tsx
+++ b/src/renderer/src/components/ProjectManager/ProjectSidebar.tsx
@@ -10,7 +10,7 @@ import {
} from '@mui/material'
import {
Search as SearchIcon,
- Folder as ProjectIcon
+ Add as AddIcon
} from '@mui/icons-material'
interface ProjectSidebarProps {
@@ -66,7 +66,7 @@ export default function ProjectSidebar({
-
+