diff --git a/app/views/install/compose.phtml b/app/views/install/compose.phtml
index 7664155718..1439d80124 100644
--- a/app/views/install/compose.phtml
+++ b/app/views/install/compose.phtml
@@ -13,7 +13,7 @@ $organization = $this->getParam('organization', '');
$image = $this->getParam('image', '');
$enableAssistant = $this->getParam('enableAssistant', false);
$dbService = $this->getParam('database', 'mongodb');
-$allowedDbServices = ['mariadb', 'mongodb'];
+$allowedDbServices = ['mariadb', 'mongodb', 'postgresql'];
if (!\in_array($dbService, $allowedDbServices, true)) {
$dbService = 'mongodb';
}
diff --git a/app/views/install/installer.phtml b/app/views/install/installer.phtml
index 9d5a3a0d39..5aef2efa77 100644
--- a/app/views/install/installer.phtml
+++ b/app/views/install/installer.phtml
@@ -13,21 +13,6 @@ $defaultDatabase = $vars['_APP_DB_ADAPTER']['default'] ?? 'mongodb';
$lockedDatabase = $isUpgrade && empty($lockedDatabase) ? $defaultDatabase : $lockedDatabase;
$isLocalInstall = $isLocalInstall ?? false;
-$step = isset($_GET['step']) ? (int) $_GET['step'] : 1;
-$step = max(1, min(5, $step));
-if ($isUpgrade && ($step === 2 || $step === 3)) {
- $step = 4;
-}
-
-$partialFile = __DIR__ . "/installer/templates/steps/step-{$step}.phtml";
-if (!is_file($partialFile)) {
- $partialFile = __DIR__ . "/installer/templates/steps/step-1.phtml";
-}
-
-if (isset($_GET['partial'])) {
- include $partialFile;
- exit;
-}
$cardStep = min(4, $step);
$stepFile = __DIR__ . "/installer/templates/steps/step-{$cardStep}.phtml";
diff --git a/src/Appwrite/Platform/Installer/Http/Installer/View.php b/src/Appwrite/Platform/Installer/Http/Installer/View.php
index 7d7ecfbed6..170b298a96 100644
--- a/src/Appwrite/Platform/Installer/Http/Installer/View.php
+++ b/src/Appwrite/Platform/Installer/Http/Installer/View.php
@@ -34,6 +34,7 @@ class View extends Action
$response->addHeader('Content-Security-Policy', implode('; ', Server::INSTALLER_CSP));
+ $params = $request->getParams();
$vars = $config->getVars();
$defaultHttpPort = $config->getDefaultHttpPort();
$defaultHttpsPort = $config->getDefaultHttpsPort();
@@ -46,6 +47,25 @@ class View extends Action
$defaultEmailCertificates = 'walterobrien@example.com';
}
+ $step = isset($params['step']) ? (int) $params['step'] : 1;
+ $step = max(1, min(5, $step));
+ if ($isUpgrade && ($step === 2 || $step === 3)) {
+ $step = 4;
+ }
+
+ $partialFile = $paths['views'] . "/installer/templates/steps/step-{$step}.phtml";
+ if (!is_file($partialFile)) {
+ $partialFile = $paths['views'] . '/installer/templates/steps/step-1.phtml';
+ }
+
+ if (isset($params['partial'])) {
+ ob_start();
+ include $partialFile;
+ $html = ob_get_clean();
+ $response->html($html);
+ return;
+ }
+
ob_start();
include $paths['views'] . '/installer.phtml';
$html = ob_get_clean();
diff --git a/src/Appwrite/Platform/Installer/Server.php b/src/Appwrite/Platform/Installer/Server.php
index cbda61114c..6627afac4f 100644
--- a/src/Appwrite/Platform/Installer/Server.php
+++ b/src/Appwrite/Platform/Installer/Server.php
@@ -207,11 +207,6 @@ class Server
$app->run($request, $response);
});
- // Handle Ctrl+C gracefully
- \Swoole\Process::signal(2, function () use ($server) {
- $server->shutdown();
- });
-
$server->start();
}
diff --git a/src/Appwrite/Platform/Tasks/Install.php b/src/Appwrite/Platform/Tasks/Install.php
index ed0d7cbe4a..76f527a3ef 100644
--- a/src/Appwrite/Platform/Tasks/Install.php
+++ b/src/Appwrite/Platform/Tasks/Install.php
@@ -14,6 +14,7 @@ use Utopia\Fetch\Client;
use Utopia\Platform\Action;
use Utopia\Validator\Boolean;
use Utopia\Validator\Text;
+use Utopia\Validator\WhiteList;
class Install extends Action
{
@@ -51,7 +52,7 @@ class Install extends Action
->param('image', 'appwrite', new Text(0), 'Main appwrite docker image', true)
->param('interactive', 'Y', new Text(1), 'Run an interactive session', true)
->param('no-start', false, new Boolean(true), 'Run an interactive session', true)
- ->param('database', 'mongodb', new Text(0), 'Database to use (mongodb|mariadb|postgres)', true)
+ ->param('database', 'mongodb', new WhiteList(['mongodb', 'mariadb', 'postgresql']), 'Database to use (mongodb|mariadb|postgresql)', true)
->callback($this->action(...));
}
@@ -308,9 +309,12 @@ class Install extends Action
]);
// Start Swoole-based installer server in background
+ // Redirect stdout/stderr to a log file so exec() returns immediately
+ // (otherwise the backgrounded process holds the pipe open and exec() hangs)
$serverScript = \escapeshellarg(dirname(__DIR__) . '/Installer/Server.php');
+ $logFile = \sys_get_temp_dir() . '/appwrite-installer-server.log';
$output = [];
- \exec("php {$serverScript} 2>&1 & echo $!", $output);
+ \exec("php {$serverScript} > " . \escapeshellarg($logFile) . " 2>&1 & echo \$!", $output);
$pid = isset($output[0]) ? (int) $output[0] : 0;
\register_shutdown_function(function () use ($pid) {
@@ -318,9 +322,14 @@ class Install extends Action
@\posix_kill($pid, SIGTERM);
}
});
- \sleep(3);
+ \sleep(1);
if (!$this->waitForWebServer($port)) {
+ $log = @\file_get_contents($logFile);
+ if ($log !== false && $log !== '') {
+ Console::error('Installer server log:');
+ Console::error($log);
+ }
Console::warning('Web installer did not respond in time. Please refresh the browser.');
return;
}