(fix): send SSE done event before tracking to prevent installer hang

This commit is contained in:
Jake Barnby
2026-03-31 14:44:20 +13:00
parent ea032c2804
commit 4e71a54fae
2 changed files with 45 additions and 20 deletions
@@ -321,6 +321,28 @@ class Install extends Action
}
};
$responseSent = false;
$onComplete = function () use ($wantsStream, $swooleResponse, $response, $installId, $state, &$responseSent) {
if ($responseSent) {
return;
}
$responseSent = true;
$state->updateGlobalLock($installId, Server::STATUS_COMPLETED);
if ($wantsStream) {
$this->writeSseEvent($swooleResponse, 'done', ['installId' => $installId, 'success' => true]);
usleep(self::SSE_KEEPALIVE_DELAY_MICROSECONDS);
$swooleResponse->write(": keepalive\n\n");
usleep(self::SSE_KEEPALIVE_DELAY_MICROSECONDS);
$swooleResponse->end();
} else {
$response->json([
'success' => true,
'installId' => $installId,
'message' => 'Installation completed successfully',
]);
}
};
$installer->performInstallation(
$httpPort ?: $config->getDefaultHttpPort(),
$httpsPort ?: $config->getDefaultHttpsPort(),
@@ -331,23 +353,11 @@ class Install extends Action
$progress,
$retryStep,
$config->isUpgrade(),
$account
$account,
$onComplete,
);
if ($wantsStream) {
$this->writeSseEvent($swooleResponse, 'done', ['installId' => $installId, 'success' => true]);
usleep(self::SSE_KEEPALIVE_DELAY_MICROSECONDS);
$swooleResponse->write(": keepalive\n\n");
usleep(self::SSE_KEEPALIVE_DELAY_MICROSECONDS);
$swooleResponse->end();
} else {
$response->json([
'success' => true,
'installId' => $installId,
'message' => 'Installation completed successfully',
]);
}
$state->updateGlobalLock($installId, Server::STATUS_COMPLETED);
$onComplete();
} catch (\Throwable $e) {
$this->handleInstallationError($e, $installId, $wantsStream, $response, $swooleResponse, $state);
}
+20 -5
View File
@@ -510,7 +510,8 @@ class Install extends Action
?callable $progress = null,
?string $resumeFromStep = null,
bool $isUpgrade = false,
array $account = []
array $account = [],
?callable $onComplete = null,
): void {
$isLocalInstall = $this->isLocalInstall();
$this->applyLocalPaths($isLocalInstall, false);
@@ -633,8 +634,20 @@ class Install extends Action
$this->createInitialAdminAccount($account, $progress, $apiUrl, $domain);
}
// Track installs
$this->trackSelfHostedInstall($input, $isUpgrade, $version, $account);
// Signal completion before tracking so the SSE stream
// finishes and the frontend can redirect immediately.
// Tracking is best-effort and must never block the user.
if ($onComplete) {
try {
$onComplete();
} catch (\Throwable) {
}
}
try {
$this->trackSelfHostedInstall($input, $isUpgrade, $version, $account);
} catch (\Throwable) {
}
if ($isCLI) {
Console::success('Appwrite installed successfully');
@@ -753,7 +766,7 @@ class Install extends Action
$name = $account['name'] ?? 'Admin';
$email = $account['email'] ?? 'admin@selfhosted.local';
$hostIp = gethostbyname($domain);
$hostIp = @gethostbyname($domain);
$payload = [
'action' => $type,
@@ -767,7 +780,7 @@ class Install extends Action
'email' => $email,
'domain' => $domain,
'database' => $database,
'hostIp' => $hostIp !== $domain ? $hostIp : null,
'hostIp' => ($hostIp !== false && $hostIp !== $domain) ? $hostIp : null,
'os' => php_uname('s') . ' ' . php_uname('r'),
'arch' => php_uname('m'),
'cpus' => ((int) trim((string) \shell_exec('nproc'))) ?: null,
@@ -778,6 +791,8 @@ class Install extends Action
try {
$client = new Client();
$client
->setConnectTimeout(5000)
->setTimeout(5000)
->addHeader('Content-Type', 'application/json')
->fetch(self::GROWTH_API_URL . '/analytics', Client::METHOD_POST, $payload);
} catch (\Throwable) {