瀏覽代碼

Merge remote-tracking branch 'upstream/main' into ipv6

asmcc 3 年之前
父節點
當前提交
14c4e0dbc2

+ 2 - 0
CHANGELOG.md

@@ -69,6 +69,8 @@ All notable changes to this project will be documented in this file.
 - Updated Filegator to 7.9.2
 - Updated Filegator to 7.9.2
 - Updated phpMyAdmin to 5.2.21
 - Updated phpMyAdmin to 5.2.21
 - Updated phpPgAdmin to 7.3.14-hestiacp
 - Updated phpPgAdmin to 7.3.14-hestiacp
+- Update MediaWiki to 1.39.2
+- Update Prestashop to 8.0.1
 
 
 ## [1.6.14] - Service release
 ## [1.6.14] - Service release
 
 

+ 1 - 1
bin/v-add-database

@@ -24,7 +24,7 @@ password=$4
 HIDE=4
 HIDE=4
 type=${5-mysql}
 type=${5-mysql}
 host=$6
 host=$6
-charset=${7-UTF8}
+charset=${7-UTF8MB4}
 charset=$(echo "$charset" | tr '[:lower:]' '[:upper:]')
 charset=$(echo "$charset" | tr '[:lower:]' '[:upper:]')
 
 
 # Includes
 # Includes

+ 21 - 0
func/upgrade.sh

@@ -550,6 +550,27 @@ upgrade_b2_tool() {
 	fi
 	fi
 }
 }
 
 
+upgrade_cloudflare_ip() {
+	echo "[ * ] Update Cloudflare IP"
+	# https://github.com/ergin/nginx-cloudflare-real-ip/
+	CLOUDFLARE_FILE_PATH='/etc/nginx/conf.d/cloudflare.inc'
+	echo "#Cloudflare" > $CLOUDFLARE_FILE_PATH
+	echo "" >> $CLOUDFLARE_FILE_PATH
+
+	echo "# - IPv4" >> $CLOUDFLARE_FILE_PATH
+	for i in $(curl -s -L https://www.cloudflare.com/ips-v4); do
+		echo "set_real_ip_from $i;" >> $CLOUDFLARE_FILE_PATH
+	done
+	echo "" >> $CLOUDFLARE_FILE_PATH
+	echo "# - IPv6" >> $CLOUDFLARE_FILE_PATH
+	for i in $(curl -s -L https://www.cloudflare.com/ips-v6); do
+		echo "set_real_ip_from $i;" >> $CLOUDFLARE_FILE_PATH
+	done
+
+	echo "" >> $CLOUDFLARE_FILE_PATH
+	echo "real_ip_header CF-Connecting-IP;" >> $CLOUDFLARE_FILE_PATH
+}
+
 upgrade_phppgadmin() {
 upgrade_phppgadmin() {
 	if [ -n "$(echo $DB_SYSTEM | grep -w 'pgsql')" ]; then
 	if [ -n "$(echo $DB_SYSTEM | grep -w 'pgsql')" ]; then
 		pga_release=$(cat /usr/share/phppgadmin/libraries/lib.inc.php | grep appVersion | head -n1 | cut -f2 -d\' | cut -f1 -d-)
 		pga_release=$(cat /usr/share/phppgadmin/libraries/lib.inc.php | grep appVersion | head -n1 | cut -f2 -d\' | cut -f1 -d-)

+ 8 - 1
install/deb/mysql/my-large.cnf

@@ -1,6 +1,10 @@
 [client]
 [client]
 port=3306
 port=3306
 socket=/run/mysqld/mysqld.sock
 socket=/run/mysqld/mysqld.sock
+default-character-set=utf8mb4
+
+[mysql]
+default-character-set=utf8mb4
 
 
 [mysqld_safe]
 [mysqld_safe]
 socket=/run/mysqld/mysqld.sock
 socket=/run/mysqld/mysqld.sock
@@ -15,6 +19,9 @@ datadir=/var/lib/mysql
 tmpdir=/tmp
 tmpdir=/tmp
 lc-messages-dir=/usr/share/mysql
 lc-messages-dir=/usr/share/mysql
 log_error=/var/log/mysql/error.log
 log_error=/var/log/mysql/error.log
+collation-server = utf8mb4_unicode_520_ci
+init-connect='SET NAMES utf8mb4'
+character-set-server = utf8mb4
 
 
 symbolic-links=0
 symbolic-links=0
 local-infile=0
 local-infile=0
@@ -40,4 +47,4 @@ interactive_timeout=50
 long_query_time=5
 long_query_time=5
 
 
 !includedir /etc/mysql/conf.d/
 !includedir /etc/mysql/conf.d/
-!includedir /etc/mysql/mariadb.conf.d/
+!includedir /etc/mysql/mariadb.conf.d/

+ 8 - 1
install/deb/mysql/my-medium.cnf

@@ -1,6 +1,10 @@
 [client]
 [client]
 port=3306
 port=3306
 socket=/run/mysqld/mysqld.sock
 socket=/run/mysqld/mysqld.sock
+default-character-set=utf8mb4
+
+[mysql]
+default-character-set=utf8mb4
 
 
 [mysqld_safe]
 [mysqld_safe]
 socket=/run/mysqld/mysqld.sock
 socket=/run/mysqld/mysqld.sock
@@ -15,6 +19,9 @@ datadir=/var/lib/mysql
 tmpdir=/tmp
 tmpdir=/tmp
 lc-messages-dir=/usr/share/mysql
 lc-messages-dir=/usr/share/mysql
 log_error=/var/log/mysql/error.log
 log_error=/var/log/mysql/error.log
+collation-server = utf8mb4_unicode_520_ci
+init-connect='SET NAMES utf8mb4'
+character-set-server = utf8mb4
 
 
 symbolic-links=0
 symbolic-links=0
 local-infile=0
 local-infile=0
@@ -39,4 +46,4 @@ interactive_timeout=50
 long_query_time=5
 long_query_time=5
 
 
 !includedir /etc/mysql/conf.d/
 !includedir /etc/mysql/conf.d/
-!includedir /etc/mysql/mariadb.conf.d/
+!includedir /etc/mysql/mariadb.conf.d/

+ 8 - 1
install/deb/mysql/my-small.cnf

@@ -1,6 +1,10 @@
 [client]
 [client]
 port=3306
 port=3306
 socket=/run/mysqld/mysqld.sock
 socket=/run/mysqld/mysqld.sock
+default-character-set=utf8mb4
+
+[mysql]
+default-character-set=utf8mb4
 
 
 [mysqld_safe]
 [mysqld_safe]
 socket=/run/mysqld/mysqld.sock
 socket=/run/mysqld/mysqld.sock
@@ -15,6 +19,9 @@ datadir=/var/lib/mysql
 tmpdir=/tmp
 tmpdir=/tmp
 lc-messages-dir=/usr/share/mysql
 lc-messages-dir=/usr/share/mysql
 log_error=/var/log/mysql/error.log
 log_error=/var/log/mysql/error.log
+collation-server = utf8mb4_unicode_520_ci
+init-connect='SET NAMES utf8mb4'
+character-set-server = utf8mb4
 
 
 symbolic-links=0
 symbolic-links=0
 local-infile=0
 local-infile=0
@@ -39,4 +46,4 @@ interactive_timeout=50
 long_query_time=5
 long_query_time=5
 
 
 !includedir /etc/mysql/conf.d/
 !includedir /etc/mysql/conf.d/
-!includedir /etc/mysql/mariadb.conf.d/
+!includedir /etc/mysql/mariadb.conf.d/

+ 2 - 23
install/deb/nginx/nginx.conf

@@ -78,29 +78,8 @@ http {
 	  application/xml+rss application/x-font-ttf image/svg+xml font/opentype;
 	  application/xml+rss application/x-font-ttf image/svg+xml font/opentype;
 	gzip_proxied                    any;
 	gzip_proxied                    any;
 	gzip_disable                    "MSIE [1-6]\.";
 	gzip_disable                    "MSIE [1-6]\.";
-	# Cloudflare https://www.cloudflare.com/ips
-	set_real_ip_from                103.21.244.0/22;
-	set_real_ip_from                103.22.200.0/22;
-	set_real_ip_from                103.31.4.0/22;
-	set_real_ip_from                104.16.0.0/13;
-	set_real_ip_from                104.24.0.0/14;
-	set_real_ip_from                108.162.192.0/18;
-	set_real_ip_from                131.0.72.0/22;
-	set_real_ip_from                141.101.64.0/18;
-	set_real_ip_from                162.158.0.0/15;
-	set_real_ip_from                172.64.0.0/13;
-	set_real_ip_from                173.245.48.0/20;
-	set_real_ip_from                188.114.96.0/20;
-	set_real_ip_from                190.93.240.0/20;
-	set_real_ip_from                197.234.240.0/22;
-	set_real_ip_from                198.41.128.0/17;
-	# set_real_ip_from 2400:cb00::/32;
-	# set_real_ip_from 2405:8100::/32;
-	# set_real_ip_from 2405:b500::/32;
-	# set_real_ip_from 2606:4700::/32;
-	# set_real_ip_from 2803:f800::/32;
-	# set_real_ip_from 2a06:98c0::/29;
-	# set_real_ip_from 2c0f:f248::/32;
+	# Cloudflare ips
+	include                         /etc/nginx/conf.d/domains/cloudflare.inc;
 	real_ip_header                  CF-Connecting-IP;
 	real_ip_header                  CF-Connecting-IP;
 	# SSL PCI compliance
 	# SSL PCI compliance
 	ssl_session_cache               shared:SSL:20m;
 	ssl_session_cache               shared:SSL:20m;

+ 112 - 0
install/deb/templates/web/nginx/php-fpm/flarum.stpl

@@ -0,0 +1,112 @@
+#=========================================================================#
+# Default Web Domain Template                                             #
+# DO NOT MODIFY THIS FILE! CHANGES WILL BE LOST WHEN REBUILDING DOMAINS   #
+# https://docs.hestiacp.com/admin_docs/web.html#how-do-web-templates-work #
+#=========================================================================#
+
+server {
+	listen      %ip%:%web_ssl_port% ssl http2;
+	server_name %domain_idn% %alias_idn%;
+	root        %docroot%;
+	index       index.php index.html index.htm;
+	access_log  /var/log/nginx/domains/%domain%.log combined;
+	access_log  /var/log/nginx/domains/%domain%.bytes bytes;
+	error_log   /var/log/nginx/domains/%domain%.error.log error;
+
+
+	include %home%/%user%/conf/web/%domain%/nginx.forcessl.conf*;
+
+	# Pass requests that don't refer directly to files in the filesystem to index.php
+	location / {
+	  try_files $uri $uri/ /index.php?$query_string;
+	}
+
+	location ~ \.php$ {
+		fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
+		try_files $uri =404;
+		fastcgi_pass %backend_lsnr%;
+		fastcgi_index index.php;
+		include /etc/nginx/fastcgi_params;
+		include %home%/%user%/conf/web/%domain%/nginx.fastcgi_cache.conf*;
+	}
+
+	#Uncomment the following lines if you are not using a `public` directory
+	#to prevent sensitive resources from being exposed.
+	location ~* ^/(\.git|composer\.(json|lock)|auth\.json|config\.php|flarum|storage|vendor) {
+	   deny all;
+	   return 404;
+	}
+
+	# The following directives are based on best practices from H5BP Nginx Server Configs
+	# https://github.com/h5bp/server-configs-nginx
+
+	# Expire rules for static content
+	location ~* \.(?:manifest|appcache|html?|xml|json)$ {
+	  add_header Cache-Control "max-age=0";
+	}
+
+	location ~* \.(?:rss|atom)$ {
+	  add_header Cache-Control "max-age=3600";
+	}
+
+	location ~* \.(?:jpg|jpeg|gif|png|ico|cur|gz|svg|mp4|ogg|ogv|webm|htc)$ {
+	  add_header Cache-Control "max-age=2592000";
+	  access_log off;
+	}
+
+	location ~* \.(?:css|js)$ {
+	  add_header Cache-Control "max-age=31536000";
+	  access_log off;
+	}
+
+	location ~* \.(?:ttf|ttc|otf|eot|woff|woff2)$ {
+	  add_header Cache-Control "max-age=2592000";
+	  access_log off;
+	}
+
+	# Gzip compression
+	gzip on;
+	gzip_comp_level 5;
+	gzip_min_length 256;
+	gzip_proxied any;
+	gzip_vary on;
+	gzip_types
+		application/atom+xml
+		application/javascript
+		application/json
+		application/ld+json
+		application/manifest+json
+		application/rss+xml
+		application/vnd.geo+json
+		application/vnd.ms-fontobject
+		application/x-font-ttf
+		application/x-web-app-manifest+json
+		application/xhtml+xml
+		application/xml
+		font/opentype
+		image/bmp
+		image/svg+xml
+		image/x-icon
+		text/cache-manifest
+		text/css
+		text/javascript
+		text/plain
+		text/vcard
+		text/vnd.rim.location.xloc
+		text/vtt
+		text/x-component
+		text/x-cross-domain-policy;
+
+	location /error/ {
+		alias   %home%/%user%/web/%domain%/document_errors/;
+	}
+
+	location /vstats/ {
+		alias   %home%/%user%/web/%domain%/stats/;
+		include %home%/%user%/web/%domain%/stats/auth.conf*;
+	}
+
+	include     /etc/nginx/conf.d/phpmyadmin.inc*;
+	include     /etc/nginx/conf.d/phppgadmin.inc*;
+	include     %home%/%user%/conf/web/%domain%/nginx.conf_*;
+}

+ 116 - 0
install/deb/templates/web/nginx/php-fpm/flarum.tpl

@@ -0,0 +1,116 @@
+#=========================================================================#
+# Default Web Domain Template                                             #
+# DO NOT MODIFY THIS FILE! CHANGES WILL BE LOST WHEN REBUILDING DOMAINS   #
+# https://docs.hestiacp.com/admin_docs/web.html#how-do-web-templates-work #
+#=========================================================================#
+
+server {
+	listen      %ip%:%web_port%;
+	server_name %domain_idn% %alias_idn%;
+	root        %docroot%;
+	index       index.php index.html index.htm;
+	access_log  /var/log/nginx/domains/%domain%.log combined;
+	access_log  /var/log/nginx/domains/%domain%.bytes bytes;
+	error_log   /var/log/nginx/domains/%domain%.error.log error;
+
+	ssl_certificate      %ssl_pem%;
+	ssl_certificate_key  %ssl_key%;
+	ssl_stapling on;
+	ssl_stapling_verify on;
+
+	include %home%/%user%/conf/web/%domain%/nginx.forcessl.conf*;
+
+	# Pass requests that don't refer directly to files in the filesystem to index.php
+	location / {
+	  try_files $uri $uri/ /index.php?$query_string;
+	}
+
+	location ~ \.php$ {
+		fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
+		try_files $uri =404;
+		fastcgi_pass %backend_lsnr%;
+		fastcgi_index index.php;
+		include /etc/nginx/fastcgi_params;
+		include %home%/%user%/conf/web/%domain%/nginx.fastcgi_cache.conf*;
+	}
+
+	#Uncomment the following lines if you are not using a `public` directory
+	#to prevent sensitive resources from being exposed.
+	location ~* ^/(\.git|composer\.(json|lock)|auth\.json|config\.php|flarum|storage|vendor) {
+	   deny all;
+	   return 404;
+	}
+
+	# The following directives are based on best practices from H5BP Nginx Server Configs
+	# https://github.com/h5bp/server-configs-nginx
+
+	# Expire rules for static content
+	location ~* \.(?:manifest|appcache|html?|xml|json)$ {
+	  add_header Cache-Control "max-age=0";
+	}
+
+	location ~* \.(?:rss|atom)$ {
+	  add_header Cache-Control "max-age=3600";
+	}
+
+	location ~* \.(?:jpg|jpeg|gif|png|ico|cur|gz|svg|mp4|ogg|ogv|webm|htc)$ {
+	  add_header Cache-Control "max-age=2592000";
+	  access_log off;
+	}
+
+	location ~* \.(?:css|js)$ {
+	  add_header Cache-Control "max-age=31536000";
+	  access_log off;
+	}
+
+	location ~* \.(?:ttf|ttc|otf|eot|woff|woff2)$ {
+	  add_header Cache-Control "max-age=2592000";
+	  access_log off;
+	}
+
+	# Gzip compression
+	gzip on;
+	gzip_comp_level 5;
+	gzip_min_length 256;
+	gzip_proxied any;
+	gzip_vary on;
+	gzip_types
+		application/atom+xml
+		application/javascript
+		application/json
+		application/ld+json
+		application/manifest+json
+		application/rss+xml
+		application/vnd.geo+json
+		application/vnd.ms-fontobject
+		application/x-font-ttf
+		application/x-web-app-manifest+json
+		application/xhtml+xml
+		application/xml
+		font/opentype
+		image/bmp
+		image/svg+xml
+		image/x-icon
+		text/cache-manifest
+		text/css
+		text/javascript
+		text/plain
+		text/vcard
+		text/vnd.rim.location.xloc
+		text/vtt
+		text/x-component
+		text/x-cross-domain-policy;
+
+	location /error/ {
+		alias   %home%/%user%/web/%domain%/document_errors/;
+	}
+
+	location /vstats/ {
+		alias   %home%/%user%/web/%domain%/stats/;
+		include %home%/%user%/web/%domain%/stats/auth.conf*;
+	}
+
+	include     /etc/nginx/conf.d/phpmyadmin.inc*;
+	include     /etc/nginx/conf.d/phppgadmin.inc*;
+	include     %home%/%user%/conf/web/%domain%/nginx.conf_*;
+}

+ 18 - 0
install/hst-install-debian.sh

@@ -1368,6 +1368,24 @@ if [ -n "$resolver" ]; then
 	fi
 	fi
 fi
 fi
 
 
+# https://github.com/ergin/nginx-cloudflare-real-ip/
+CLOUDFLARE_FILE_PATH='/etc/nginx/conf.d/cloudflare.inc'
+echo "#Cloudflare" > $CLOUDFLARE_FILE_PATH
+echo "" >> $CLOUDFLARE_FILE_PATH
+
+echo "# - IPv4" >> $CLOUDFLARE_FILE_PATH
+for i in $(curl -s -L https://www.cloudflare.com/ips-v4); do
+	echo "set_real_ip_from $i;" >> $CLOUDFLARE_FILE_PATH
+done
+echo "" >> $CLOUDFLARE_FILE_PATH
+echo "# - IPv6" >> $CLOUDFLARE_FILE_PATH
+for i in $(curl -s -L https://www.cloudflare.com/ips-v6); do
+	echo "set_real_ip_from $i;" >> $CLOUDFLARE_FILE_PATH
+done
+
+echo "" >> $CLOUDFLARE_FILE_PATH
+echo "real_ip_header CF-Connecting-IP;" >> $CLOUDFLARE_FILE_PATH
+
 update-rc.d nginx defaults > /dev/null 2>&1
 update-rc.d nginx defaults > /dev/null 2>&1
 systemctl start nginx >> $LOG
 systemctl start nginx >> $LOG
 check_result $? "nginx start failed"
 check_result $? "nginx start failed"

+ 18 - 0
install/hst-install-ubuntu.sh

@@ -1452,6 +1452,24 @@ if [ -n "$resolver" ]; then
 	fi
 	fi
 fi
 fi
 
 
+# https://github.com/ergin/nginx-cloudflare-real-ip/
+CLOUDFLARE_FILE_PATH='/etc/nginx/conf.d/cloudflare.inc'
+echo "#Cloudflare" > $CLOUDFLARE_FILE_PATH
+echo "" >> $CLOUDFLARE_FILE_PATH
+
+echo "# - IPv4" >> $CLOUDFLARE_FILE_PATH
+for i in $(curl -s -L https://www.cloudflare.com/ips-v4); do
+	echo "set_real_ip_from $i;" >> $CLOUDFLARE_FILE_PATH
+done
+echo "" >> $CLOUDFLARE_FILE_PATH
+echo "# - IPv6" >> $CLOUDFLARE_FILE_PATH
+for i in $(curl -s -L https://www.cloudflare.com/ips-v6); do
+	echo "set_real_ip_from $i;" >> $CLOUDFLARE_FILE_PATH
+done
+
+echo "" >> $CLOUDFLARE_FILE_PATH
+echo "real_ip_header CF-Connecting-IP;" >> $CLOUDFLARE_FILE_PATH
+
 update-rc.d nginx defaults > /dev/null 2>&1
 update-rc.d nginx defaults > /dev/null 2>&1
 systemctl start nginx >> $LOG
 systemctl start nginx >> $LOG
 check_result $? "nginx start failed"
 check_result $? "nginx start failed"

+ 8 - 0
install/upgrade/versions/1.7.0.sh

@@ -111,3 +111,11 @@ if [ "$PHPMYADMIN_KEY" != "" ]; then
 	$BIN/v-delete-sys-pma-sso quiet
 	$BIN/v-delete-sys-pma-sso quiet
 	$BIN/v-add-sys-pma-sso quiet
 	$BIN/v-add-sys-pma-sso quiet
 fi
 fi
+
+if [ -f /etc/nginx/nginx.conf ] && [ ! -f /etc/nginx/conf.d/cloudflare.inc ]; then
+	echo "[ * ] Enable support for updating Cloudflare Ips"
+	sed -i '/set_real_ip_from/d' /etc/nginx/nginx.conf
+	sed -i '/real_ip_header/d' /etc/nginx/nginx.conf
+	sed -i 's|# Cloudflare https://www.cloudflare.com/ips|# Cloudflare https://www.cloudflare.com/ips\n    include /etc/nginx/conf.d/cloudflare.inc;|g' /etc/nginx/nginx.conf
+	# At a later stage a function  will run and will load all the new rules
+fi

+ 3 - 0
src/deb/hestia/postinst

@@ -86,6 +86,9 @@ upgrade_roundcube | tee -a $LOG
 # Upgrade PHP php dependencies
 # Upgrade PHP php dependencies
 upgrade_dependencies | tee -a $LOG
 upgrade_dependencies | tee -a $LOG
 
 
+# Upgrade Cloudflare IPs if applicable
+upgrade_cloudflare_ip | tee -a $LOG
+
 # Upgrade phpMyAdmin if applicable
 # Upgrade phpMyAdmin if applicable
 upgrade_phpmyadmin | tee -a $LOG
 upgrade_phpmyadmin | tee -a $LOG
 
 

+ 5 - 2
web/css/src/themes/default.css

@@ -1924,7 +1924,8 @@
 
 
 .button-circle {
 .button-circle {
 	border-radius: 50%;
 	border-radius: 50%;
-	min-width: 28px;
+	min-width: 30px;
+	min-height: 30px;
 	padding: 3px;
 	padding: 3px;
 
 
 	& .fas {
 	& .fas {
@@ -1936,7 +1937,9 @@
 	display: none;
 	display: none;
 
 
 	@media (--viewport-medium) {
 	@media (--viewport-medium) {
-		display: block;
+		display: flex;
+		justify-content: center;
+		align-items: center;
 		position: fixed;
 		position: fixed;
 		z-index: 1;
 		z-index: 1;
 		bottom: 70px;
 		bottom: 70px;

文件差異過大導致無法顯示
+ 0 - 0
web/css/themes/default.min.css


+ 1 - 5
web/edit/server/index.php

@@ -1043,11 +1043,7 @@ if (!empty($_POST["save"])) {
 
 
 	// Change remote backup host
 	// Change remote backup host
 	if (empty($_SESSION["error_msg"])) {
 	if (empty($_SESSION["error_msg"])) {
-		if (
-			!empty($_POST["v_backup_host"]) &&
-			$_POST["v_backup_type"] == $v_backup_type &&
-			!isset($v_backup_new)
-		) {
+		if ($_POST["v_backup_type"] == $v_backup_type && !isset($v_backup_new)) {
 			if (in_array($_POST["v_backup_type"], ["ftp", "sftp"])) {
 			if (in_array($_POST["v_backup_type"], ["ftp", "sftp"])) {
 				if (
 				if (
 					$_POST["v_backup_host"] != $v_backup_host ||
 					$_POST["v_backup_host"] != $v_backup_host ||

+ 214 - 0
web/src/app/WebApp/Installers/Flarum/FlarumSetup.php

@@ -0,0 +1,214 @@
+<?php
+namespace Hestia\WebApp\Installers\Flarum;
+
+use Hestia\System\Util;
+use Hestia\WebApp\Installers\BaseSetup as BaseSetup;
+
+class FlarumSetup extends BaseSetup {
+	protected $appInfo = [
+		"name" => "Flarum",
+		"group" => "forum",
+		"enabled" => true,
+		"version" => "latest",
+		"thumbnail" => "fl-thumb.png",
+	];
+
+	protected $appname = "flarum";
+
+	protected $config = [
+		"form" => [
+			"forum_title" => ["type" => "text", "value" => "Flarum Forum"],
+			"admin_username" => ["value" => "fladmin"],
+			"admin_email" => "text",
+			"admin_password" => "password",
+			"install_directory" => ["type" => "text", "value" => "", "placeholder" => "/"],
+		],
+		"database" => true,
+		"resources" => [
+			"composer" => ["src" => "flarum/flarum"],
+		],
+		"server" => [
+			"nginx" => [
+				"template" => "flarum",
+			],
+			"php" => [
+				"supported" => ["8.0", "8.1", "8.2"],
+			],
+		],
+	];
+
+	// Our updateFile routine done the 'Hestia way'
+	public function updateFile($file, $search, $replace) {
+		$result = null;
+		$this->appcontext->runUser("v-open-fs-file", [$file], $result);
+		foreach ($result->raw as $line_num => $line) {
+			if (strpos($line, $search) !== false) {
+				$result->raw[$line_num] = str_replace($search, $replace, $line);
+			}
+		}
+		$tmp = $this->saveTempFile(implode("\r\n", $result->raw));
+		if (!$this->appcontext->runUser("v-move-fs-file", [$tmp, $file], $result)) {
+			throw new \Exception("Error updating file in: " . $tmp . " " . $result->text);
+		}
+		return $result;
+	}
+
+	public function install(array $options = null): bool {
+		parent::setAppDirInstall($options["install_directory"]);
+		parent::install($options);
+		parent::setup($options);
+		$result = null;
+
+		// Move public folder content (https://docs.flarum.org/install/#customizing-paths)
+		if (
+			!$this->appcontext->runUser(
+				"v-list-fs-directory",
+				[$this->getDocRoot("public")],
+				$result,
+			)
+		) {
+			throw new \Exception(
+				"Error listing folder at: " . $this->getDocRoot("public") . $result->text,
+			);
+		}
+		foreach ($result->raw as $line_num => $line) {
+			$detail = explode("|", $line);
+			$type = $detail[0];
+			$name = end($detail);
+			if ($name != "") {
+				if ($type == "d") {
+					// Directory
+					if (
+						!$this->appcontext->runUser(
+							"v-move-fs-directory",
+							[
+								$this->getDocRoot("public") . "/" . $name,
+								$this->getDocRoot() . "/" . $name,
+							],
+							$result,
+						)
+					) {
+						throw new \Exception(
+							"Error moving folder at: " .
+								$this->getDocRoot("public") .
+								"/" .
+								$name .
+								$result->text,
+						);
+					}
+				} else {
+					if (
+						!$this->appcontext->runUser(
+							"v-move-fs-file",
+							[
+								$this->getDocRoot("public") . "/" . $name,
+								$this->getDocRoot() . "/" . $name,
+							],
+							$result,
+						)
+					) {
+						throw new \Exception(
+							"Error moving file at: " .
+								$this->getDocRoot("public") .
+								"/" .
+								$name .
+								$result->text,
+						);
+					}
+				}
+			}
+		}
+		if (
+			!$this->appcontext->runUser(
+				"v-delete-fs-directory",
+				[$this->getDocRoot("public")],
+				$result,
+			)
+		) {
+			throw new \Exception(
+				"Error deleting folder at: " . $this->getDocRoot("public") . $result->text,
+			);
+		}
+
+		// Not using 'public'; enable protection rewrite rules and update paths
+		$result = $this->updateFile(
+			$this->getDocRoot(".htaccess"),
+			"# RewriteRule ",
+			"RewriteRule ",
+		);
+		$result = $this->updateFile(
+			$this->getDocRoot("index.php"),
+			'$site = require \'../site.php\';',
+			'$site = require \'./site.php\';',
+		);
+		$result = $this->updateFile(
+			$this->getDocRoot("site.php"),
+			"'public' => __DIR__.'/public',",
+			"'public' => __DIR__,",
+		);
+
+		// POST install
+		$this->appcontext->run(
+			"v-list-web-domain",
+			[$this->appcontext->user(), $this->domain, "json"],
+			$status,
+		);
+		$sslEnabled = $status->json[$this->domain]["SSL"] == "no" ? 0 : 1;
+		$webDomain = ($sslEnabled ? "https://" : "http://") . $this->domain;
+		$webPort = $sslEnabled ? "443" : "80";
+
+		$mysql_host = "localhost";
+		$mysql_database = addcslashes(
+			$this->appcontext->user() . "_" . $options["database_name"],
+			"\\'",
+		);
+		$mysql_username = addcslashes(
+			$this->appcontext->user() . "_" . $options["database_user"],
+			"\\'",
+		);
+		$mysql_password = addcslashes($options["database_password"], "\\'");
+		$table_prefix = addcslashes(Util::generate_string(5, false) . "_", "\\'");
+		$subfolder = $options["install_directory"];
+		if (substr($subfolder, 0, 1) != "/") {
+			$subfolder = "/" . $subfolder;
+		}
+
+		$cmd =
+			"/usr/bin/curl --location --post301 --insecure --resolve " .
+			$this->domain .
+			":$webPort:" .
+			$this->appcontext->getWebDomainIp($this->domain) .
+			" " .
+			escapeshellarg($webDomain . $subfolder . "/index.php") .
+			" -d " .
+			escapeshellarg(
+				"forumTitle=" .
+					rawurlencode($options["forum_title"]) .
+					"&mysqlHost=" .
+					rawurlencode($mysql_host) .
+					"&mysqlDatabase=" .
+					rawurlencode($mysql_database) .
+					"&mysqlUsername=" .
+					rawurlencode($mysql_username) .
+					"&mysqlPassword=" .
+					rawurlencode($mysql_password) .
+					"&tablePrefix=" .
+					rawurlencode($table_prefix) .
+					"&adminUsername=" .
+					rawurlencode($options["admin_username"]) .
+					"&adminEmail=" .
+					rawurlencode($options["admin_email"]) .
+					"&adminPassword=" .
+					rawurlencode($options["admin_password"]) .
+					"&adminPasswordConfirmation=" .
+					rawurlencode($options["admin_password"]),
+			);
+		exec($cmd, $output, $return_var);
+
+		// Report any errors
+		if ($return_var > 0) {
+			throw new \Exception(implode(PHP_EOL, $output));
+		}
+		return $result->code === 0 && $return_var === 0;
+	}
+}

二進制
web/src/app/WebApp/Installers/Flarum/fl-thumb.png


+ 3 - 3
web/src/app/WebApp/Installers/MediaWiki/MediaWikiSetup.php

@@ -10,7 +10,7 @@ class MediaWikiSetup extends BaseSetup {
 		"name" => "MediaWiki",
 		"name" => "MediaWiki",
 		"group" => "cms",
 		"group" => "cms",
 		"enabled" => true,
 		"enabled" => true,
-		"version" => "1.39.1",
+		"version" => "1.39.2",
 		"thumbnail" => "MediaWiki-2020-logo.svg", //Max size is 300px by 300px
 		"thumbnail" => "MediaWiki-2020-logo.svg", //Max size is 300px by 300px
 	];
 	];
 
 
@@ -26,7 +26,7 @@ class MediaWikiSetup extends BaseSetup {
 		"database" => true,
 		"database" => true,
 		"resources" => [
 		"resources" => [
 			"archive" => [
 			"archive" => [
-				"src" => "https://releases.wikimedia.org/mediawiki/1.39/mediawiki-1.39.1.zip",
+				"src" => "https://releases.wikimedia.org/mediawiki/1.39/mediawiki-1.39.2.zip",
 			],
 			],
 		],
 		],
 		"server" => [
 		"server" => [
@@ -60,7 +60,7 @@ class MediaWikiSetup extends BaseSetup {
 
 
 		$this->appcontext->runUser(
 		$this->appcontext->runUser(
 			"v-copy-fs-directory",
 			"v-copy-fs-directory",
-			[$this->getDocRoot($this->extractsubdir . "/mediawiki-1.39.1/."), $this->getDocRoot()],
+			[$this->getDocRoot($this->extractsubdir . "/mediawiki-1.39.2/."), $this->getDocRoot()],
 			$result,
 			$result,
 		);
 		);
 
 

+ 3 - 4
web/src/app/WebApp/Installers/Prestashop/PrestashopSetup.php

@@ -9,7 +9,7 @@ class PrestashopSetup extends BaseSetup {
 		"name" => "Prestashop",
 		"name" => "Prestashop",
 		"group" => "ecommerce",
 		"group" => "ecommerce",
 		"enabled" => true,
 		"enabled" => true,
-		"version" => "1.7.8.7",
+		"version" => "8.0.1",
 		"thumbnail" => "prestashop-thumb.png",
 		"thumbnail" => "prestashop-thumb.png",
 	];
 	];
 
 
@@ -27,7 +27,7 @@ class PrestashopSetup extends BaseSetup {
 		"resources" => [
 		"resources" => [
 			"archive" => [
 			"archive" => [
 				"src" =>
 				"src" =>
-					"https://github.com/PrestaShop/PrestaShop/releases/download/1.7.8.7/prestashop_1.7.8.7.zip",
+					"https://github.com/PrestaShop/PrestaShop/releases/download/8.0.1/prestashop_8.0.1.zip",
 			],
 			],
 		],
 		],
 		"server" => [
 		"server" => [
@@ -35,7 +35,7 @@ class PrestashopSetup extends BaseSetup {
 				"template" => "prestashop",
 				"template" => "prestashop",
 			],
 			],
 			"php" => [
 			"php" => [
-				"supported" => ["7.3", "7.4"],
+				"supported" => ["8.0", "8.1"],
 			],
 			],
 		],
 		],
 	];
 	];
@@ -43,7 +43,6 @@ class PrestashopSetup extends BaseSetup {
 	public function install(array $options = null): bool {
 	public function install(array $options = null): bool {
 		parent::install($options);
 		parent::install($options);
 		parent::setup($options);
 		parent::setup($options);
-
 		$this->appcontext->archiveExtract(
 		$this->appcontext->archiveExtract(
 			$this->getDocRoot($this->extractsubdir . "/prestashop.zip"),
 			$this->getDocRoot($this->extractsubdir . "/prestashop.zip"),
 			$this->getDocRoot(),
 			$this->getDocRoot(),

+ 1 - 1
web/templates/includes/js.php

@@ -1,11 +1,11 @@
 <script defer src="/js/main.js?<?= JS_LATEST_UPDATE ?>"></script>
 <script defer src="/js/main.js?<?= JS_LATEST_UPDATE ?>"></script>
-<script defer src="/js/vendor/alpine-3.10.5.min.js?<?= JS_LATEST_UPDATE ?>"></script>
 <script defer src="/js/vendor/jquery-3.6.3.min.js?<?= JS_LATEST_UPDATE ?>"></script>
 <script defer src="/js/vendor/jquery-3.6.3.min.js?<?= JS_LATEST_UPDATE ?>"></script>
 <script defer src="/js/vendor/jquery-ui.min.js?<?= JS_LATEST_UPDATE ?>"></script>
 <script defer src="/js/vendor/jquery-ui.min.js?<?= JS_LATEST_UPDATE ?>"></script>
 <script defer src="/js/vendor/chart.min.js?<?= JS_LATEST_UPDATE ?>"></script>
 <script defer src="/js/vendor/chart.min.js?<?= JS_LATEST_UPDATE ?>"></script>
 <script defer src="/js/shortcuts.js?<?= JS_LATEST_UPDATE ?>"></script>
 <script defer src="/js/shortcuts.js?<?= JS_LATEST_UPDATE ?>"></script>
 <script defer src="/js/events.js?<?= JS_LATEST_UPDATE ?>"></script>
 <script defer src="/js/events.js?<?= JS_LATEST_UPDATE ?>"></script>
 <script defer src="/js/init.js?<?= JS_LATEST_UPDATE ?>"></script>
 <script defer src="/js/init.js?<?= JS_LATEST_UPDATE ?>"></script>
+<script defer src="/js/vendor/alpine-3.10.5.min.js?<?= JS_LATEST_UPDATE ?>"></script>
 <script>
 <script>
 	// TODO: REMOVE
 	// TODO: REMOVE
 	const App = {
 	const App = {

+ 1 - 1
web/templates/pages/edit_server.php

@@ -788,7 +788,7 @@
 								</option>
 								</option>
 							</select>
 							</select>
 						</div>
 						</div>
-						<div x-cloak x-show="backupType == "ftp' || backupType == "sftp'">
+						<div x-cloak x-show="backupType == 'ftp' || backupType == 'sftp' || backupType == ''">
 							<div class="u-mb10">
 							<div class="u-mb10">
 								<label for="v_backup_host" class="form-label">
 								<label for="v_backup_host" class="form-label">
 									<?= _("Host") ?>
 									<?= _("Host") ?>

+ 10 - 8
web/templates/pages/list_packages.php

@@ -40,11 +40,11 @@
 			<div class="clearfix l-unit__stat-col--left super-compact">
 			<div class="clearfix l-unit__stat-col--left super-compact">
 				<input type="checkbox" class="js-toggle-all" title="<?= _("Select all") ?>">
 				<input type="checkbox" class="js-toggle-all" title="<?= _("Select all") ?>">
 			</div>
 			</div>
-			<div class="clearfix l-unit__stat-col--left wide-2"><b><?= _("Package") ?></b></div>
+			<div class="clearfix l-unit__stat-col--left wide"><b><?= _("Package") ?></b></div>
 			<div class="clearfix l-unit__stat-col--left compact-3 u-text-right"><b>&nbsp;</b></div>
 			<div class="clearfix l-unit__stat-col--left compact-3 u-text-right"><b>&nbsp;</b></div>
 			<div class="clearfix l-unit__stat-col--left u-text-center compact"><b><i class="fas fa-terminal" title="<?= _("Shell") ?>"></i></b></div>
 			<div class="clearfix l-unit__stat-col--left u-text-center compact"><b><i class="fas fa-terminal" title="<?= _("Shell") ?>"></i></b></div>
-			<div class="clearfix l-unit__stat-col--left u-text-center compact"><b><i class="fas fa-hard-drive" title="<?= _("Quota") ?>"></i></b></div>
-			<div class="clearfix l-unit__stat-col--left u-text-center compact"><b><i class="fas fa-right-left" title="<?= _("Bandwidth") ?>"></i></b></div>
+			<div class="clearfix l-unit__stat-col--left u-text-center compact-2"><b><i class="fas fa-hard-drive" title="<?= _("Quota") ?>"></i></b></div>
+			<div class="clearfix l-unit__stat-col--left u-text-center compact-2"><b><i class="fas fa-right-left" title="<?= _("Bandwidth") ?>"></i></b></div>
 			<div class="clearfix l-unit__stat-col--left u-text-center super-compact"><b><i class="fas fa-earth-americas" title="<?= _("Web Domains") ?>"></i></b></div>
 			<div class="clearfix l-unit__stat-col--left u-text-center super-compact"><b><i class="fas fa-earth-americas" title="<?= _("Web Domains") ?>"></i></b></div>
 			<div class="clearfix l-unit__stat-col--left u-text-center super-compact"><b><i class="fas fa-link" title="<?= _("Web Aliases") ?>"></i></b></div>
 			<div class="clearfix l-unit__stat-col--left u-text-center super-compact"><b><i class="fas fa-link" title="<?= _("Web Aliases") ?>"></i></b></div>
 			<div class="clearfix l-unit__stat-col--left u-text-center super-compact"><b><i class="fas fa-book-atlas" title="<?= _("DNS Domains") ?>"></i></b></div>
 			<div class="clearfix l-unit__stat-col--left u-text-center super-compact"><b><i class="fas fa-book-atlas" title="<?= _("DNS Domains") ?>"></i></b></div>
@@ -70,9 +70,11 @@
 					<input id="check<?=$i?>" class="ch-toggle" type="checkbox" title="<?= _("Select") ?>" name="package[]" value="<?=$key?>">
 					<input id="check<?=$i?>" class="ch-toggle" type="checkbox" title="<?= _("Select") ?>" name="package[]" value="<?=$key?>">
 				</div>
 				</div>
 				<?php if ($key == 'system'){ ?>
 				<?php if ($key == 'system'){ ?>
-					<div class="clearfix l-unit__stat-col--left wide-2 truncate"><b><?=$key?></b></div>
-				<?php } else {?>
-					<div class="clearfix l-unit__stat-col--left wide-2 truncate"><b><a href="/edit/package/?package=<?=$key?>&token=<?=$_SESSION['token']?>" title="<?= _("Editing Package") ?>: <?=$key?>"><?=$key?></a></b></div>
+					<div class="clearfix l-unit__stat-col--left wide truncate"><b><?=$key?></b></div>
+				<?php } else { ?>
+					<div class="clearfix l-unit__stat-col--left wide truncate">
+						<b><a href="/edit/package/?package=<?=$key?>&token=<?=$_SESSION['token']?>" title="<?= _("Editing Package") ?>: <?=$key?>"><?=$key?></a></b>
+					</div>
 				<?php } ?>
 				<?php } ?>
 				<!-- START QUICK ACTION TOOLBAR AREA -->
 				<!-- START QUICK ACTION TOOLBAR AREA -->
 				<div class="clearfix l-unit__stat-col--left u-text-right compact-3">
 				<div class="clearfix l-unit__stat-col--left u-text-right compact-3">
@@ -108,7 +110,7 @@
 						<i class="fas fa-circle-check icon-green icon-large"></i>
 						<i class="fas fa-circle-check icon-green icon-large"></i>
 					<?php } ?>
 					<?php } ?>
 				</div>
 				</div>
-				<div class="clearfix l-unit__stat-col--left u-text-center compact">
+				<div class="clearfix l-unit__stat-col--left u-text-center compact-2">
 					<span title="<?= _("Quota") ?>: <?= humanize_usage_size($data[$key]["DISK_QUOTA"]) ?> <?= humanize_usage_measure($data[$key]["DISK_QUOTA"]) ?>">
 					<span title="<?= _("Quota") ?>: <?= humanize_usage_size($data[$key]["DISK_QUOTA"]) ?> <?= humanize_usage_measure($data[$key]["DISK_QUOTA"]) ?>">
 						<?php if (preg_match("/[a-z]/i", $data[$key]["DISK_QUOTA"])): ?>
 						<?php if (preg_match("/[a-z]/i", $data[$key]["DISK_QUOTA"])): ?>
 							<b>&infin;</b>
 							<b>&infin;</b>
@@ -117,7 +119,7 @@
 						<?php endif; ?>
 						<?php endif; ?>
 					</span>
 					</span>
 				</div>
 				</div>
-				<div class="clearfix l-unit__stat-col--left u-text-center compact">
+				<div class="clearfix l-unit__stat-col--left u-text-center compact-2">
 					<span title="<?= _("Bandwidth") ?>: <?= humanize_usage_size($data[$key]["BANDWIDTH"]) ?> <?= humanize_usage_measure($data[$key]["BANDWIDTH"]) ?>">
 					<span title="<?= _("Bandwidth") ?>: <?= humanize_usage_size($data[$key]["BANDWIDTH"]) ?> <?= humanize_usage_measure($data[$key]["BANDWIDTH"]) ?>">
 						<?php if ($data[$key]["BANDWIDTH"] == "unlimited") { ?>
 						<?php if ($data[$key]["BANDWIDTH"] == "unlimited") { ?>
 							<b>&infin;</b>
 							<b>&infin;</b>

部分文件因文件數量過多而無法顯示