Quellcode durchsuchen

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

asmcc vor 3 Jahren
Ursprung
Commit
38ff852b46

+ 3 - 3
bin/v-add-remote-dns-domain

@@ -55,7 +55,7 @@ if [ -z "$str" ]; then
 	fi
 	exit
 fi
-if [ "$DNS_CLUSTER_SYSTEM" = "zone" ]; then
+if [ "$DNS_CLUSTER_SYSTEM" = "hestia-zone" ]; then
 	str=$(echo "$str" | sed "s/SLAVE='no'/SLAVE='yes'/g")
 	str=$(echo "$str" | sed "s/SLAVE=''/SLAVE='yes'/g")
 	ip=$($BIN/v-list-sys-ips plain | cut -f1 | head -n1)
@@ -73,7 +73,7 @@ for cluster in $(grep "SUSPENDED='no'" $HESTIA/conf/dns-cluster.conf); do
 	# Parsing domain parameters
 	parse_object_kv_list "$str"
 
-	if [ "$DNS_CLUSTER_SYSTEM" = "zone" ]; then
+	if [ "$DNS_CLUSTER_SYSTEM" = "hestia-zone" ]; then
 		# Syncing domain data
 		cluster_cmd v-insert-dns-domain $DNS_USER "$str" $HOSTNAME $flush 'no'
 		check_result $? "$HOST connection failed" "$E_CONNECT"
@@ -101,7 +101,7 @@ for cluster in $(grep "SUSPENDED='no'" $HESTIA/conf/dns-cluster.conf); do
 	fi
 done
 
-if [ "$DNS_CLUSTER_SYSTEM" = "zone" ]; then
+if [ "$DNS_CLUSTER_SYSTEM" = "hestia-zone" ]; then
 	rndc notify $domain > /dev/null 2>&1
 fi
 #----------------------------------------------------------#

+ 2 - 2
bin/v-add-remote-dns-record

@@ -54,9 +54,9 @@ if [ -z "$str" ]; then
 	fi
 	exit
 fi
-# $DNS_CLUSTER_SYSTEM = "zone" doesn't need to be uopdated
+# $DNS_CLUSTER_SYSTEM = "hestia-zone" doesn't need to be uopdated
 
-if [ "$DNS_CLUSTER_SYSTEM" != "zone" ]; then
+if [ "$DNS_CLUSTER_SYSTEM" != "hestia-zone" ]; then
 	IFS=$'\n'
 	for cluster in $(grep "SUSPENDED='no'" $HESTIA/conf/dns-cluster.conf); do
 

+ 1 - 1
bin/v-change-remote-dns-domain-exp

@@ -52,7 +52,7 @@ for cluster in $(grep "SUSPENDED='no'" $HESTIA/conf/dns-cluster.conf); do
 
 	# Syncing domain
 	str=$(grep "DOMAIN='$domain'" $USER_DATA/dns.conf)
-	if [ "$DNS_CLUSTER_SYSTEM" = "zone" ]; then
+	if [ "$DNS_CLUSTER_SYSTEM" = "hestia-zone" ]; then
 		str=$(echo "$str" | sed "s/SLAVE='no'/SLAVE='yes'/g")
 		ip=$($BIN/v-list-sys-ips plain | cut -f1)
 		str=$(echo "$str" | sed "s/MASTER='*'/MASTER='$ip'/g")

+ 1 - 1
bin/v-change-remote-dns-domain-soa

@@ -52,7 +52,7 @@ for cluster in $(grep "SUSPENDED='no'" $HESTIA/conf/dns-cluster.conf); do
 	# Parsing remote host parameters
 	parse_object_kv_list "$cluster"
 
-	if [ "$DNS_CLUSTER_SYSTEM" = "zone" ]; then
+	if [ "$DNS_CLUSTER_SYSTEM" = "hestia-zone" ]; then
 		str=$(echo "$str" | sed "s/SLAVE='no'/SLAVE='yes'/g")
 		ip=$($BIN/v-list-sys-ips plain | cut -f1)
 		str=$(echo "$str" | sed "s/MASTER='*'/MASTER='$ip'/g")

+ 1 - 1
bin/v-change-remote-dns-domain-ttl

@@ -54,7 +54,7 @@ for cluster in $(grep "SUSPENDED='no'" $HESTIA/conf/dns-cluster.conf); do
 
 	# Syncing TTL
 	str=$(grep "DOMAIN='$domain'" $USER_DATA/dns.conf)
-	if [ "$DNS_CLUSTER_SYSTEM" = "zone" ]; then
+	if [ "$DNS_CLUSTER_SYSTEM" = "hestia-zone" ]; then
 		str=$(echo "$str" | sed "s/SLAVE='no'/SLAVE='yes'/g")
 		ip=$($BIN/v-list-sys-ips plain | cut -f1)
 		str=$(echo "$str" | sed "s/MASTER='*'/MASTER='$ip'/g")

+ 2 - 1
bin/v-restart-service

@@ -19,6 +19,7 @@ restart=$2
 source /etc/hestiacp/hestia.conf
 # shellcheck source=/usr/local/hestia/func/main.sh
 source $HESTIA/func/main.sh
+source_conf "$HESTIA/conf/hestia.conf"
 
 #----------------------------------------------------------#
 #                    Verifications                         #
@@ -77,7 +78,7 @@ for service in $service_list; do
 		"$service" = "proftpd" -o \
 		"$service" = "ssh" -o \
 		"$service" = "fail2ban" ]; then
-		systemctl reload-or-restart "$service" > /dev/null 2>&1
+		systemctl reload-or-restart "$service" >> $log 2>&1
 	else
 		systemctl reset-failed "$service" >> $log 2>&1
 		systemctl restart "$service" >> $log 2>&1

+ 3 - 3
bin/v-sync-dns-cluster

@@ -68,7 +68,7 @@ for cluster in $hosts; do
 				unset $SLAVE
 				parse_object_kv_list "$str"
 				if [ "$SLAVE" != "yes" ]; then
-					if [ "$DNS_CLUSTER_SYSTEM" != "zone" ]; then
+					if [ "$DNS_CLUSTER_SYSTEM" != "hestia-zone" ]; then
 						# Syncing domain index
 
 						cluster_cmd v-insert-dns-domain "$DNS_USER" "$str" "$HOSTNAME" ' ' "no"
@@ -82,7 +82,7 @@ for cluster in $hosts; do
 						cluster_cmd v-insert-dns-records "$DNS_USER" "$DOMAIN" "$tmp_file" 'no'
 						check_result $? "$HOST connection failed" "$E_CONNECT"
 					fi
-					if [ "$DNS_CLUSTER_SYSTEM" = "zone" ]; then
+					if [ "$DNS_CLUSTER_SYSTEM" = "hestia-zone" ]; then
 						str=$(echo "$str" | sed "s/SLAVE='no'/SLAVE='yes'/g")
 						str=$(echo "$str" | sed "s/SLAVE=''/SLAVE='yes'/g")
 
@@ -100,7 +100,7 @@ for cluster in $hosts; do
 			done
 		fi
 	done
-	if [ "$DNS_CLUSTER_SYSTEM" != "zone" ]; then
+	if [ "$DNS_CLUSTER_SYSTEM" != "hestia-zone" ]; then
 		# Rebuilding dns zones
 		cluster_cmd v-rebuild-dns-domains "$DNS_USER"
 		check_result $? "$TYPE connection to $HOST failed" "$E_CONNECT"

+ 2 - 2
docs/docs/server-administration/dns.md

@@ -48,7 +48,7 @@ There is no limitation on how to chain DNS servers.
 ### DNS Cluster with the Hestia API (Master -> Slave)
 
 1. Create a new user on the Hestia server that will act as a “Slave”.
-2. In `/usr/local/hestia/conf/hestia.conf`, change `DNS_CLUSTER_SYSTEM='hestia'` to `DNS_CLUSTER_SYSTEM='zone'`.
+2. In `/usr/local/hestia/conf/hestia.conf`, change `DNS_CLUSTER_SYSTEM='hestia'` to `DNS_CLUSTER_SYSTEM='hestia-zone'`.
 3. On the master server, open `/etc/bind/named.options`, do the following changes, then restart bind9 with `systemctl restart bind9`.
 
    ```bash
@@ -87,7 +87,7 @@ There is no limitation on how to chain DNS servers.
 
 ### Converting an existing DNS cluster to Master -> Slave
 
-1. In `/usr/local/hestia/conf/hestia.conf`, change `DNS_CLUSTER_SYSTEM='hestia'` to `DNS_CLUSTER_SYSTEM='zone'`.
+1. In `/usr/local/hestia/conf/hestia.conf`, change `DNS_CLUSTER_SYSTEM='hestia'` to `DNS_CLUSTER_SYSTEM='hestia-zone'`.
 2. On the master server, open `/etc/bind/named.options`, do the following changes, then restart bind9 with `systemctl restart bind9`.
 
    ```bash

+ 3 - 3
func/upgrade.sh

@@ -600,7 +600,7 @@ upgrade_filemanager() {
 		else
 			fm_version="1.0.0"
 		fi
-		if [ "$fm_version" != "$fm_v" ]; then
+		if version_ge "$fm_version" "$fm_v"; then
 			echo "[ ! ] Upgrading File Manager to version $fm_v..."
 			# Reinstall the File Manager
 			$BIN/v-delete-sys-filemanager quiet yes
@@ -628,7 +628,7 @@ upgrade_roundcube() {
 			echo "      To upgrade to the latest version of Roundcube directly from upstream, from please run the command migrate_roundcube.sh located in: /usr/local/hestia/install/upgrade/manual/"
 		else
 			rc_version=$(cat /var/lib/roundcube/index.php | grep -o -E '[0-9].[0-9].[0-9]+' | head -1)
-			if [ "$rc_version" != "$rc_v" ]; then
+			if version_ge "$rc_version" "$rc_v"; then
 				echo "[ ! ] Upgrading Roundcube to version $rc_v..."
 				$BIN/v-add-sys-roundcube
 			else
@@ -641,7 +641,7 @@ upgrade_roundcube() {
 upgrade_rainloop() {
 	if [ -n "$(echo "$WEBMAIL_SYSTEM" | grep -w 'rainloop')" ]; then
 		rl_version=$(cat /var/lib/rainloop/data/VERSION)
-		if [ "$rl_version" != "$rl_v" ]; then
+		if version_ge "$rl_version" "$rl_v"; then
 			echo "[ ! ] Upgrading Rainloop to version $rl_v..."
 			$BIN/v-add-sys-rainloop
 		else

+ 1 - 1
install/common/dovecot/conf.d/10-auth.conf

@@ -1,5 +1,5 @@
 disable_plaintext_auth = no
-auth_username_format = %u
+auth_username_format = %Lu
 auth_verbose = yes
 auth_mechanisms = plain login
 !include auth-passwdfile.conf.ext

+ 1 - 13
install/deb/pma/apache.conf

@@ -5,18 +5,6 @@ Alias /%pma_alias% /usr/share/phpmyadmin
 <Directory /usr/share/phpmyadmin>
 	Options FollowSymLinks
 	DirectoryIndex index.php
-
-	<IfModule mod_php5.c>
-		AddType application/x-httpd-php .php
-
-		php_flag magic_quotes_gpc Off
-		php_flag track_vars On
-		php_flag register_globals Off
-		php_admin_flag allow_url_fopen Off
-		php_value include_path .
-		php_admin_value upload_tmp_dir /var/lib/phpmyadmin/tmp
-		php_admin_value open_basedir /usr/share/phpmyadmin/:/etc/phpmyadmin/:/var/lib/phpmyadmin/:/usr/share/php/php-gettext:/usr/share/javascript/
-	</IfModule>
     <IfModule mpm_event_module>
         # Use www.conf instead
         <FilesMatch \.php$>
@@ -49,4 +37,4 @@ Alias /%pma_alias% /usr/share/phpmyadmin
 <Directory /usr/share/phpmyadmin/locale>
     Order Deny,Allow
     Deny from All
-</Directory>
+</Directory>

+ 2 - 3
install/hst-install-ubuntu.sh

@@ -52,7 +52,7 @@ software="apache2 apache2.2-common apache2-suexec-custom apache2-utils
     php$fpm_v-opcache php$fpm_v-pspell php$fpm_v-readline php$fpm_v-xml
     postgresql postgresql-contrib proftpd-basic quota rrdtool spamassassin sudo hestia=${HESTIA_INSTALL_VER}
     hestia-nginx hestia-php vim-common vsftpd whois unzip zip acl sysstat setpriv rsyslog
-    ipset libonig5 libzip5 openssh-server lsb-release zstd jq"
+    ipset libonig5 libzip4 openssh-server lsb-release zstd jq"
 
 installer_dependencies="apt-transport-https curl dirmngr gnupg wget software-properties-common ca-certificates"
 
@@ -942,14 +942,13 @@ if [ -d "$withdebs" ]; then
 fi
 if [ "$release" = '18.04' ]; then
 	software=$(echo "$software" | sed -e "s/libonig5/libonig4/")
-	software=$(echo "$software" | sed -e "s/libzip5/libzip4/")
 fi
 if [ "$release" = '20.04' ]; then
 	software=$(echo "$software" | sed -e "s/setpriv/util-linux/")
+	software=$(echo "$software" | sed -e "s/libzip4/libzip5/")
 fi
 if [ "$release" = '22.04' ]; then
 	software=$(echo "$software" | sed -e "s/setpriv/util-linux/")
-	software=$(echo "$software" | sed -e "s/libzip5/libzip4/")
 fi
 
 #----------------------------------------------------------#

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

@@ -49,6 +49,11 @@ if [ -z "$(grep -e 'condition =  ${lookup{$local_part@$domain}lsearch{/etc/exim4
 	done
 fi
 
+# Allow Email@domain.com for login
+if [ -f "/etc/dovecot/conf.d/10-auth.conf" ]; then
+	sed -i "s/auth_username_format = %u/auth_username_format = %Lu/g" /etc/dovecot/conf.d/10-auth.conf
+fi
+
 # rename /var/run/xx to /run/
 for file in /etc/dovecot/dovecot.conf /etc/clamav/clamd.conf /etc/exim/exim.conf.template /etc/logrotate.d/apache2 /etc/logrotate.d/nginx /etc/mysql/my.cnf /etc/nginx/nginx.conf; do
 	if [ -f "$file" ]; then

+ 1 - 1
src/deb/hestia/copyright

@@ -3,7 +3,7 @@ Upstream-Name: hestia
 Source: https://www.hestiacp.com
 
 Files: *
-Copyright: 2018-2022, Hestia Control Panel <info@hestiacp.com>
+Copyright: 2018-2023, Hestia Control Panel <info@hestiacp.com>
 License: GPL-3.0+
 Remarks: Hestia Control Panel is a fork from VestaCP, special thanks to vestacp.com and Serghey Rodin
 

+ 1 - 1
src/deb/nginx/copyright

@@ -3,7 +3,7 @@ Upstream-Name: hestia
 Source: https://www.hestiacp.com
 
 Files: *
-Copyright: 2018-2022, Hestia Control Panel <info@hestiacp.com>
+Copyright: 2018-2023, Hestia Control Panel <info@hestiacp.com>
 License: GPL-3.0+
 Remarks: Hestia is a fork from VestaCP, special thanks to vestacp.com and Serghey Rodin
 

+ 3 - 2
src/deb/php/control

@@ -1,12 +1,13 @@
 Source: hestia-php
 Package: hestia-php
 Priority: optional
-Version: 8.2.0
+Version: 8.2.1
 Section: admin
 Maintainer: HestaCP <info@hestiacp.com>
 Homepage: https://www.hestiacp.com
 Architecture: amd64
-Depends: hestia, libzip5 | libzip4, unzip, libonig5 | libonig4 | libonig2
+Depends: hestia, libzip4, unzip, libonig5 | libonig4 | libonig2
+Conflct: libzip5
 Description: hestia php-fpm
  hestia is an open source hosting control panel.
  hestia has a clean and focused interface without the clutter.

+ 1 - 1
src/deb/php/copyright

@@ -3,7 +3,7 @@ Upstream-Name: hestia
 Source: https://www.hestiacp.com
 
 Files: *
-Copyright: 2018-2022, Hestia Control Panel <info@hestiacp.com>
+Copyright: 2018-2023, Hestia Control Panel <info@hestiacp.com>
 License: GPL-3.0+
 Remarks: Hestia is a fork from VestaCP, special thanks to vestacp.com and Serghey Rodin
 

+ 9 - 1
src/hst_autocompile.sh

@@ -258,7 +258,7 @@ if [ "$dontinstalldeps" != 'true' ]; then
 		dnf install -y -q $SOFTWARE
 	else
 		# Set package dependencies for compiling
-		SOFTWARE='build-essential libxml2-dev libz-dev libzip-dev libgmp-dev libcurl4-gnutls-dev unzip openssl libssl-dev pkg-config libsqlite3-dev libonig-dev rpm'
+		SOFTWARE='build-essential libxml2-dev libz-dev libzip-dev libgmp-dev libcurl4-gnutls-dev unzip openssl libssl-dev pkg-config libsqlite3-dev libonig-dev rpm lsb-release'
 
 		echo "Updating system APT repositories..."
 		apt-get -qq update > /dev/null 2>&1
@@ -555,6 +555,14 @@ if [ "$PHP_B" = true ]; then
 		if [ "$BUILD_ARCH" != "amd64" ]; then
 			sed -i "s/amd64/${BUILD_ARCH}/g" "$BUILD_DIR_HESTIAPHP/DEBIAN/control"
 		fi
+
+		os=$(lsb_release -is)
+		release=$(lsb_release -rs)
+		if [[ "$os" = "Ubuntu" ]] && [[ "$release" = "20.04" ]]; then
+			sed -i "/Conflicts: libzip5/d" "$BUILD_DIR_HESTIAPHP/DEBIAN/control"
+			sed -i "s/libzip4/libzip5/g" "$BUILD_DIR_HESTIAPHP/DEBIAN/control"
+		fi
+
 		get_branch_file 'src/deb/php/copyright' "$BUILD_DIR_HESTIAPHP/DEBIAN/copyright"
 		get_branch_file 'src/deb/php/postinst' "$BUILD_DIR_HESTIAPHP/DEBIAN/postinst"
 		chmod +x $BUILD_DIR_HESTIAPHP/DEBIAN/postinst

+ 12 - 13
web/api/index.php

@@ -40,7 +40,11 @@ function api_error($exit_code, $message, bool $add_log = false, $user = "system"
 	$http_code = $exit_code >= 100 ? $exit_code : exit_code_to_http_code($exit_code);
 	header("Hestia-Exit-Code: $exit_code");
 	http_response_code($http_code);
-	echo !preg_match("/^Error:/", $message) ? "Error: $message" : $message;
+	if ($hst_return == "code") {
+		echo $exit_code;
+	} else {
+		echo !preg_match("/^Error:/", $message) ? "Error: $message" : $message;
+	}
 	exit();
 }
 
@@ -58,15 +62,14 @@ function api_legacy(array $request_data) {
 
 	if ($settings["config"]["API"] != "yes") {
 		echo "Error: API has been disabled";
-		exit();
+		api_error(E_DISABLED, "Error: API Disabled");
 	}
 
 	if ($settings["config"]["API_ALLOWED_IP"] != "allow-all") {
 		$ip_list = explode(",", $settings["config"]["API_ALLOWED_IP"]);
 		$ip_list[] = "";
 		if (!in_array(get_real_user_ip(), $ip_list)) {
-			echo "Error: IP is not allowed to connect with API";
-			exit();
+			api_error(E_FORBIDDEN, "Error: IP is not allowed to connect with API");
 		}
 	}
 
@@ -74,13 +77,11 @@ function api_legacy(array $request_data) {
 	// Authentication
 	if (empty($request_data["hash"])) {
 		if ($request_data["user"] != "admin") {
-			echo "Error: authentication failed";
-			exit();
+			api_error(E_FORBIDDEN, "Error: authentication failed");
 		}
 		$password = $request_data["password"];
 		if (!isset($password)) {
-			echo "Error: missing authentication";
-			exit();
+			api_error(E_PASSWORD, "Error: authentication failed");
 		}
 		$v_ip = quoteshellarg(get_real_user_ip());
 		unset($output);
@@ -134,8 +135,7 @@ function api_legacy(array $request_data) {
 
 		// Check API answer
 		if ($return_var > 0) {
-			echo "Error: authentication failed";
-			exit();
+			api_error(E_PASSWORD, "Error: authentication failed");
 		}
 	} else {
 		$key = "/usr/local/hestia/data/keys/" . basename($request_data["hash"]);
@@ -148,8 +148,7 @@ function api_legacy(array $request_data) {
 		unset($output);
 		// Check API answer
 		if ($return_var > 0) {
-			echo "Error: authentication failed";
-			exit();
+			api_error(E_PASSWORD, "Error: authentication failed");
 		}
 	}
 
@@ -285,7 +284,7 @@ function api_connection(array $request_data) {
 
 	# Check if API access is enabled for nonadmin users
 	if ($key_user != "admin" && $api_status < 2) {
-		api_error(E_DISABLED, "API has been disabled");
+		api_error(E_API_DISABLED, "API has been disabled");
 	}
 
 	// Checks if the value entered in the "user" argument matches the user of the key

+ 0 - 26
web/js/pages/edit_server.js

@@ -1,26 +0,0 @@
-$('#backup_type').change(function () {
-	if (this.value == 'b2') {
-		$('#backup_bucket').show();
-		$('#backup_sftp').hide();
-		$('#backup_rclone').hide();
-	} else if (this.value == 'rclone') {
-		$('#backup_bucket').hide();
-		$('#backup_sftp').hide();
-		$('#backup_rclone').show();
-	} else {
-		$('#backup_bucket').hide();
-		$('#backup_sftp').show();
-		$('#backup_rclone').hide();
-	}
-});
-
-$('#api, #api-system').change(function () {
-	var api = $('#api').val();
-	var apiSystem = $('#api-system').val();
-
-	if (api === 'yes' || apiSystem > 0) {
-		$('#security_ip').show();
-	} else {
-		$('#security_ip').hide();
-	}
-});

+ 0 - 14
web/js/pages/list_ssl.js

@@ -1,14 +0,0 @@
-// eslint-disable-next-line @typescript-eslint/no-unused-vars
-function saveTextToBlob(file, element) {
-	const downloadLink = document.createElement('a');
-	downloadLink.style.display = 'none';
-	downloadLink.textContent = 'Download File';
-	downloadLink.download = file;
-	downloadLink.href = window.URL.createObjectURL(
-		new Blob([document.getElementById(element).value], { type: 'text/plain' })
-	);
-
-	const child = document.body.appendChild(downloadLink);
-	downloadLink.click();
-	document.body.removeChild(child);
-}

+ 0 - 4
web/js/pages/setup_webapp.js

@@ -1,4 +0,0 @@
-// eslint-disable-next-line @typescript-eslint/no-unused-vars
-function applyRandomStringToTarget(target, min_length = 16) {
-	document.querySelector(`#${target}`).value = randomString(min_length);
-}

Datei-Diff unterdrückt, da er zu groß ist
+ 556 - 196
web/templates/pages/edit_server.php


+ 63 - 17
web/templates/pages/list_ssl.php

@@ -2,48 +2,94 @@
 <div class="toolbar">
 	<div class="toolbar-inner">
 		<div class="toolbar-right">
-
 		</div>
 	</div>
 </div>
 <!-- End toolbar -->
 
+<!-- Begin form -->
 <div class="container animate__animated animate__fadeIn">
-
 	<form id="vstobjects" name="v_generate_csr" method="post">
 		<input type="hidden" name="token" value="<?= $_SESSION["token"] ?>">
 
 		<div class="form-container">
 			<h1 class="form-title"><?= _("Generating CSR") ?></h1>
-			<?php show_alert_message($_SESSION); ?>
-			<div class="u-mb20">
+			<?php show_alert_message($_SESSION);?>
+			<div
+				x-data="{
+					text: '<?= base64_encode($v_crt) ?>',
+					blob() {
+						return window.URL.createObjectURL(new Blob([atob(this.text)], { type: 'text/plain' }))
+					}
+				}"
+				class="u-mb20"
+			>
 				<label for="v_crt" class="form-label">
 					<?= _("SSL Certificate") ?>
-					<a href="javascript:saveTextToBlob('<?php echo htmlentities($v_domain); ?>.crt', 'v_crt');"><i class="fas fa-download"></i></a>
+					<a
+						x-bind:href="blob()"
+						download="<?= htmlentities($v_domain) ?>.crt"
+					><i class="fas fa-download"></i></a>
 				</label>
-				<textarea class="form-control u-min-height100" name="v_crt" id="v_crt"><?= $v_crt ?></textarea>
+				<textarea
+					x-model="atob(text)"
+					class="form-control u-min-height100"
+					name="v_crt"
+					id="v_crt"
+				></textarea>
 			</div>
-			<div class="u-mb20">
+			<div
+				x-data="{
+					text: '<?= base64_encode($v_key) ?>',
+					blob() {
+						return window.URL.createObjectURL(new Blob([atob(this.text)], { type: 'text/plain' }))
+					}
+				}"
+				class="u-mb20"
+			>
 				<label for="v_key" class="form-label">
-					<?= _("SSL Key") ?>
-					<a href="javascript:saveTextToBlob('<?php echo htmlentities($v_domain); ?>.key', 'v_key');"><i class="fas fa-download"></i></a>
+					<?=_("SSL Key");?>
+					<a
+						x-bind:href="blob()"
+						download="<?= htmlentities($v_domain) ?>.key"
+					><i class="fas fa-download"></i></a>
 				</label>
-				<textarea class="form-control u-min-height100" name="v_key" id="v_key"><?= $v_key ?></textarea>
+				<textarea
+					x-model="atob(text)"
+					class="form-control u-min-height100"
+					name="v_key"
+					id="v_key"
+				></textarea>
 			</div>
-			<div class="u-mb20">
+			<div
+				x-data="{
+					text: '<?= base64_encode($v_csr) ?>',
+					blob() {
+						return window.URL.createObjectURL(new Blob([atob(this.text)], { type: 'text/plain' }))
+					}
+				}"
+				class="u-mb20"
+			>
 				<label for="v_csr" class="form-label">
-					<?= _("SSL CSR") ?>
-					<a href="javascript:saveTextToBlob('<?php echo htmlentities($v_domain); ?>.csr', 'v_crt');"><i class="fas fa-download"></i></a>
+					<?=_("SSL CSR");?>
+					<a
+						x-bind:href="blob()"
+						download="<?= htmlentities($v_domain) ?>.csr"
+					><i class="fas fa-download"></i></a>
 				</label>
-				<textarea class="form-control u-min-height100" name="v_csr" id="v_csr"><?= $v_csr ?></textarea>
+				<textarea
+					x-model="atob(text)"
+					class="form-control u-min-height100"
+					name="v_csr"
+					id="v_csr"
+				></textarea>
 			</div>
 			<div>
 				<button type="button" class="button button-secondary" onclick="<?= $back ?>">
-					<?= _("Back") ?>
+					<?=_("Back");?>
 				</button>
 			</div>
 		</div>
-
 	</form>
-
 </div>
+<!-- End form -->

+ 95 - 52
web/templates/pages/setup_webapp.php

@@ -2,22 +2,29 @@
 <div class="toolbar">
 	<div class="toolbar-inner">
 		<div class="toolbar-buttons">
-			<a class="button button-secondary" id="btn-back" href="/add/webapp/?domain=<?= htmlentities($v_domain) ?>">
-				<i class="fas fa-arrow-left icon-blue"></i><?= _("Back") ?>
+			<a
+				class="button button-secondary"
+				id="btn-back"
+				href="/add/webapp/?domain=<?= htmlentities($v_domain) ?>"
+			>
+				<i class="fas fa-arrow-left icon-blue"></i>
+				<?= _("Back") ?>
 			</a>
 		</div>
 		<div class="toolbar-buttons">
-			<button type="submit" class="button" form="vstobjects">
-				<i class="fas fa-floppy-disk icon-purple"></i><?= _("Save") ?>
+			<button class="button" type="submit" form="vstobjects">
+				<i class="fas fa-floppy-disk icon-purple"></i>
+				<?= _("Save") ?>
 			</button>
 		</div>
 	</div>
 </div>
 <!-- End toolbar -->
 
+<!-- Begin form -->
 <div class="container animate__animated animate__fadeIn">
 
-	<?php if (!empty($WebappInstaller->getOptions())): ?>
+	<?php if (!empty($WebappInstaller->getOptions())) { ?>
 		<form id="vstobjects" method="POST" name="v_setup_webapp">
 			<input type="hidden" name="token" value="<?= $_SESSION["token"] ?>">
 			<input type="hidden" name="ok" value="true">
@@ -25,7 +32,7 @@
 			<div class="form-container">
 				<h1 class="form-title"><?= sprintf(_("Install %s"), $WebappInstaller->info()["name"]) ?></h1>
 				<?php show_alert_message($_SESSION); ?>
-				<?php if (!$WebappInstaller->isDomainRootClean()): ?>
+				<?php if (!$WebappInstaller->isDomainRootClean()) { ?>
 					<div class="alert alert-info" role="alert">
 						<i class="fas fa-info"></i>
 						<div>
@@ -34,61 +41,97 @@
 							<p><?php echo sprintf(_("Please make sure ~/web/%s/public_html is empty!"), $v_domain); ?></p>
 						</div>
 					</div>
-				<?php endif; ?>
+				<?php } ?>
 				<div class="u-mt20">
-					<?php foreach ($WebappInstaller->getOptions() as $form_name => $form_control): ?>
-						<?php
-							$f_name = $WebappInstaller->formNs() . '_' . $form_name;
-							$f_type = $form_control;
-							$f_value = '';
-							if (isset($form_control['label'])) {
-								$f_label = htmlentities($form_control['label']);
-							} else {
-								$f_label = ucwords(str_replace(['.','_'], ' ', $form_name));
-							}
-							$f_placeholder = '';
-							if (is_array($form_control)) {
-								$f_type = (!empty($form_control['type']))?$form_control['type']:'text';
-								$f_value = (!empty($form_control['value']))?$form_control['value']:'';
-								$f_placeholder = (!empty($form_control['placeholder']))?$form_control['placeholder']:'';
-							}
-
-							$f_value = htmlentities($f_value);
-							$f_label = htmlentities($f_label);
-							$f_name = htmlentities($f_name);
-							$f_placeholder = htmlentities($f_placeholder);
-						?>
-						<div class="u-mb10">
-							<?php if ($f_type != "boolean"): ?>
-								<label for="<?= $f_name ?>" class="form-label">
-									<?= $f_label ?>
-									<?php if ($f_type === "password"): ?> / <a href="javascript:applyRandomStringToTarget('<?= $f_name ?>');" class="form-link"><?= _("generate") ?></a> <?php endif; ?>
+					<?php
+					foreach ($WebappInstaller->getOptions() as $form_name => $form_control) {
+						$field_name = $WebappInstaller->formNs() . "_" . $form_name;
+						$field_type = $form_control;
+						$field_value = "";
+						$field_label =
+							isset($form_control["label"])
+								? htmlentities($form_control["label"])
+								: ucwords(str_replace([".","_"], " ", $form_name));
+						$field_placeholder = "";
+						if (is_array($form_control)) {
+							$field_type = !empty($form_control["type"]) ? $form_control["type"] : "text";
+							$field_value = !empty($form_control["value"]) ? $form_control["value"] : "";
+							$field_placeholder = !empty($form_control["placeholder"]) ? $form_control["placeholder"] : "";
+						}
+						$field_value = htmlentities($field_value);
+						$field_label = htmlentities($field_label);
+						$field_name = htmlentities($field_name);
+						$field_placeholder = htmlentities($field_placeholder);
+					?>
+						<div
+							x-data="{
+								value: '<?= !empty($field_value) ? $field_value : "" ?>'
+							}"
+							class="u-mb10"
+						>
+							<?php if ($field_type != "boolean"): ?>
+								<label for="<?= $field_name ?>" class="form-label">
+									<?= $field_label ?>
+									<?php if ($field_type == "password") { ?>
+										/
+										<button
+											x-on:click="value = randomString()"
+											class="form-link"
+											type="button"
+										>
+											<?= _("generate") ?>
+									</button>
+									<?php } ?>
 								</label>
 							<?php endif; ?>
-							<?php if (in_array($f_type, ['select']) && count($form_control['options']) ):?>
-								<select class="form-select" name="<?=$f_name?>" id="<?=$f_name?>">
-									<?php foreach ($form_control['options'] as $key => $option){
-										if(is_numeric($key)){
-											$key = $option;
-										}
+
+							<?php if ($field_type == 'select' && count($form_control['options'])) { ?>
+								<select class="form-select" name="<?= $field_name ?>" id="<?= $field_name ?>">
+									<?php
+									foreach ($form_control['options'] as $key => $option) {
+										$key = !is_numeric($key) ? $key : $option;
+										$selected = !empty($form_control['value'] && $key == $form_control['value']) ? 'selected' : '';
 									?>
-										<?php $selected = (!empty($form_control['value']) && $key == $form_control['value'])?'selected':''?>
-										<option value="<?=$key?>" <?=$selected?>><?=htmlentities($option)?></option>
-									<?php }; ?>
+										<option
+											value="<?= $key ?>"
+											<?= $selected ?>
+										>
+											<?= htmlentities($option) ?>
+										</option>
+									<?php } ?>
 								</select>
-							<?php elseif (in_array($f_type, ["boolean"])): ?>
+							<?php
+							} elseif ($field_type == "boolean") {
+								$checked = !empty($field_value) ? "checked" : "";
+							?>
 								<div class="form-check">
-									<?php $checked = !empty($f_value) ? "checked" : ""; ?>
-									<input class="form-check-input" type="checkbox" name="<?= $f_name ?>" id="<?= $f_name ?>" <?= $checked ?> value="true">
-									<label for="<?= $f_name ?>"><?= $f_label ?></label>
+									<input
+										class="form-check-input"
+										type="checkbox"
+										name="<?= $field_name ?>"
+										id="<?= $field_name ?>"
+										value="true"
+										<?= $checked ?>
+									>
+									<label for="<?= $field_name ?>">
+										<?= $field_label ?>
+									</label>
 								</div>
-							<?php else: ?>
-								<input type="text" class="form-control" name="<?= $f_name ?>" id="<?= $f_name ?>" placeholder="<?= $f_placeholder ?>" value="<?= $f_value ?>">
-							<?php endif; ?>
+							<?php } else { ?>
+								<input
+									x-model="value"
+									type="text"
+									class="form-control"
+									name="<?= $field_name ?>"
+									id="<?= $field_name ?>"
+									placeholder="<?= $field_placeholder ?>"
+								>
+							<?php } ?>
 						</div>
-					<?php endforeach; ?>
+					<?php } ?>
 				</div>
 			</div>
 		</form>
-	<?php endif; ?>
+	<?php } ?>
 </div>
+<!-- End form -->

Einige Dateien werden nicht angezeigt, da zu viele Dateien in diesem Diff geändert wurden.