From 114016bbca44331b1c8946ee4a97edf52ff35b08 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Wed, 18 Mar 2026 17:50:47 +1300 Subject: [PATCH 1/6] (fix): preserve session cookie across installer redirect --- app/views/install/installer/js/modules/progress.js | 8 ++++++-- .../Platform/Installer/Http/Installer/Complete.php | 10 ++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/app/views/install/installer/js/modules/progress.js b/app/views/install/installer/js/modules/progress.js index a6e784ada9..7f7b23e3fc 100644 --- a/app/views/install/installer/js/modules/progress.js +++ b/app/views/install/installer/js/modules/progress.js @@ -27,6 +27,7 @@ let activeInstall = null; let unloadGuard = null; + let sseSessionDetails = null; const csrfToken = document.querySelector('meta[name="appwrite-installer-csrf"]')?.getAttribute('content') || ''; const withCsrfHeader = (headers = {}) => { @@ -524,6 +525,9 @@ inProgress: payload.message || payload.step, done: payload.message || payload.step }; + if (step.id === STEP_IDS.ACCOUNT_SETUP && payload.details?.sessionSecret) { + sseSessionDetails = payload.details; + } progressState.set(step.id, { status: payload.status || STATUS.IN_PROGRESS, message: payload.message, @@ -599,7 +603,7 @@ }); if (!allDone) return; const accountState = progressState.get(STEP_IDS.ACCOUNT_SETUP); - const sessionDetails = accountState?.details; + const sessionDetails = sseSessionDetails || accountState?.details; finalizeInstall(); notifyInstallComplete(activeInstall?.installId, sessionDetails).finally(() => { setTimeout(() => redirectToApp(), TIMINGS?.redirectDelay ?? 0); @@ -740,7 +744,7 @@ } const accountState = progressState.get(STEP_IDS.ACCOUNT_SETUP); - const sessionDetails = accountState?.details; + const sessionDetails = sseSessionDetails || accountState?.details; finalizeInstall(); notifyInstallComplete(activeInstall?.installId, sessionDetails).finally(() => { setTimeout(() => redirectToApp(), TIMINGS?.redirectDelay ?? 0); diff --git a/src/Appwrite/Platform/Installer/Http/Installer/Complete.php b/src/Appwrite/Platform/Installer/Http/Installer/Complete.php index da8985396f..92a00651fe 100644 --- a/src/Appwrite/Platform/Installer/Http/Installer/Complete.php +++ b/src/Appwrite/Platform/Installer/Http/Installer/Complete.php @@ -48,6 +48,16 @@ class Complete extends Action @touch(Server::INSTALLER_COMPLETE_FILE); + if (!$sessionSecret && $installId !== '') { + $data = $state->readProgressFile($installId); + $details = $data['details'][Server::STEP_ACCOUNT_SETUP] ?? []; + if (!empty($details['sessionSecret'])) { + $sessionSecret = $details['sessionSecret']; + $sessionId = $sessionId ?: ($details['sessionId'] ?? ''); + $sessionExpire = $sessionExpire ?: ($details['sessionExpire'] ?? ''); + } + } + if ($sessionSecret) { $isHttps = $request->getProtocol() === 'https'; $sameSite = $isHttps ? Response::COOKIE_SAMESITE_NONE : Response::COOKIE_SAMESITE_LAX; From a3bb7af634ba3c5e5b863f7f5b4956135bb030a8 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Wed, 18 Mar 2026 18:44:13 +1300 Subject: [PATCH 2/6] (chore): improve wait time --- composer.lock | 32 ++++++++++++------------- src/Appwrite/Platform/Tasks/Install.php | 6 ++--- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/composer.lock b/composer.lock index 314265f3e0..c2c3f86209 100644 --- a/composer.lock +++ b/composer.lock @@ -4603,16 +4603,16 @@ }, { "name": "utopia-php/mongo", - "version": "1.0.1", + "version": "1.0.2", "source": { "type": "git", "url": "https://github.com/utopia-php/mongo.git", - "reference": "83dbcde768d5fb40241f5ca8aa5ed8ca140a7469" + "reference": "677a21c53f7a1316c528b4b45b3fce886cee7223" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/mongo/zipball/83dbcde768d5fb40241f5ca8aa5ed8ca140a7469", - "reference": "83dbcde768d5fb40241f5ca8aa5ed8ca140a7469", + "url": "https://api.github.com/repos/utopia-php/mongo/zipball/677a21c53f7a1316c528b4b45b3fce886cee7223", + "reference": "677a21c53f7a1316c528b4b45b3fce886cee7223", "shasum": "" }, "require": { @@ -4658,9 +4658,9 @@ ], "support": { "issues": "https://github.com/utopia-php/mongo/issues", - "source": "https://github.com/utopia-php/mongo/tree/1.0.1" + "source": "https://github.com/utopia-php/mongo/tree/1.0.2" }, - "time": "2026-03-13T07:29:24+00:00" + "time": "2026-03-18T02:45:50+00:00" }, { "name": "utopia-php/platform", @@ -5468,16 +5468,16 @@ "packages-dev": [ { "name": "appwrite/sdk-generator", - "version": "1.11.6", + "version": "1.11.9", "source": { "type": "git", "url": "https://github.com/appwrite/sdk-generator.git", - "reference": "f80e302d000cdc2f98b4bb5ff2fc3bd0bdff7b38" + "reference": "2f0f6ec54736ba7efdff188a9451b56f1665f25a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/f80e302d000cdc2f98b4bb5ff2fc3bd0bdff7b38", - "reference": "f80e302d000cdc2f98b4bb5ff2fc3bd0bdff7b38", + "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/2f0f6ec54736ba7efdff188a9451b56f1665f25a", + "reference": "2f0f6ec54736ba7efdff188a9451b56f1665f25a", "shasum": "" }, "require": { @@ -5513,9 +5513,9 @@ "description": "Appwrite PHP library for generating API SDKs for multiple programming languages and platforms", "support": { "issues": "https://github.com/appwrite/sdk-generator/issues", - "source": "https://github.com/appwrite/sdk-generator/tree/1.11.6" + "source": "https://github.com/appwrite/sdk-generator/tree/1.11.9" }, - "time": "2026-03-09T07:12:51+00:00" + "time": "2026-03-17T08:16:49+00:00" }, { "name": "brianium/paratest", @@ -6224,11 +6224,11 @@ }, { "name": "phpstan/phpstan", - "version": "2.1.40", + "version": "2.1.42", "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/9b2c7aeb83a75d8680ea5e7c9b7fca88052b766b", - "reference": "9b2c7aeb83a75d8680ea5e7c9b7fca88052b766b", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/1279e1ce86ba768f0780c9d889852b4e02ff40d0", + "reference": "1279e1ce86ba768f0780c9d889852b4e02ff40d0", "shasum": "" }, "require": { @@ -6273,7 +6273,7 @@ "type": "github" } ], - "time": "2026-02-23T15:04:35+00:00" + "time": "2026-03-17T14:58:32+00:00" }, { "name": "phpunit/php-code-coverage", diff --git a/src/Appwrite/Platform/Tasks/Install.php b/src/Appwrite/Platform/Tasks/Install.php index 820bca46d5..47fa70e11d 100644 --- a/src/Appwrite/Platform/Tasks/Install.php +++ b/src/Appwrite/Platform/Tasks/Install.php @@ -23,7 +23,7 @@ class Install extends Action private const int WEB_SERVER_CHECK_DELAY_SECONDS = 1; private const int HEALTH_CHECK_ATTEMPTS = 30; - private const int HEALTH_CHECK_DELAY_SECONDS = 3; + private const int HEALTH_CHECK_DELAY_SECONDS = 1; private const string PATTERN_ENV_VAR_NAME = '/^[A-Z0-9_]+$/'; private const string PATTERN_DB_PASSWORD_VAR = '/^_APP_DB_.*_PASS$/'; @@ -734,8 +734,8 @@ class Install extends Action { $client = new Client(); $client - ->setTimeout(5000) - ->setConnectTimeout(5000) + ->setTimeout(2000) + ->setConnectTimeout(2000) ->addHeader('Host', $domain); $healthPath = '/v1/health/version'; From e1c327235a1bcfacd564611435822c8e42d22697 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Thu, 19 Mar 2026 15:03:58 +1300 Subject: [PATCH 3/6] (feat): add dark mode support to installer --- app/views/install/installer/css/styles.css | 43 +++++++++++++++++++ .../install/installer/icons/appwrite-logo.svg | 18 ++++---- .../installer/icons/indicator-inactive.svg | 2 +- 3 files changed, 53 insertions(+), 10 deletions(-) diff --git a/app/views/install/installer/css/styles.css b/app/views/install/installer/css/styles.css index 4601cfcb67..b1d8fe5089 100644 --- a/app/views/install/installer/css/styles.css +++ b/app/views/install/installer/css/styles.css @@ -11,9 +11,13 @@ --neutral-300: rgba(173, 173, 176, 1); --neutral-400: rgba(151, 151, 155, 1); --neutral-500: rgba(129, 129, 134, 1); + --neutral-600: rgba(108, 108, 113, 1); --neutral-700: rgba(86, 86, 92, 1); + --neutral-750: rgba(65, 65, 70, 1); --neutral-800: rgba(45, 45, 49, 1); + --neutral-850: rgba(29, 29, 33, 1); --neutral-900: rgba(25, 25, 28, 1); + --neutral-250: rgba(195, 195, 198, 1); /* Warning colors */ --web-orange-200: rgba(255, 213, 194, 1); @@ -172,6 +176,44 @@ --fgColor-neutral-weak: var(--fgcolor-neutral-weak); --fgColor-accent: var(--fgcolor-accent); --fgColor-on-accent: var(--fgcolor-on-accent); + + color-scheme: light dark; +} + +@media (prefers-color-scheme: dark) { + :root { + --bgcolor-neutral-default: var(--neutral-900); + --bgcolor-neutral-primary: var(--neutral-850); + --bgcolor-neutral-secondary: var(--neutral-800); + --bgcolor-neutral-tertiary: var(--neutral-800); + --bgcolor-neutral-invert-weaker: var(--neutral-400); + --bgcolor-neutral-invert-weak: var(--neutral-300); + --bgcolor-success-weak: rgba(16, 185, 129, 0.12); + --bgcolor-warning-weaker: rgba(254, 124, 67, 0.08); + --bgcolor-warning-weak: rgba(254, 124, 67, 0.12); + --bgcolor-error-weaker: rgba(255, 69, 58, 0.08); + + --fgcolor-neutral-primary: var(--neutral-50); + --fgcolor-neutral-secondary: var(--neutral-250); + --fgcolor-neutral-tertiary: var(--neutral-500); + --fgcolor-neutral-weak: var(--neutral-600); + --fgcolor-on-accent: var(--neutral-0); + --fgcolor-on-invert: var(--neutral-900); + --fgcolor-on-success-weak: rgba(52, 211, 153, 1); + --fgcolor-warning: var(--web-orange-500); + --fgcolor-on-warning-weak: var(--web-orange-500); + --fgcolor-error: var(--web-red-500); + --fgcolor-on-error: var(--neutral-0); + + --border-neutral: var(--neutral-800); + --border-neutral-strong: var(--neutral-750); + --border-neutral-stronger: var(--neutral-600); + --border-focus: var(--neutral-600); + + --overlay-neutral-hover: rgba(255, 255, 255, 0.04); + --overlay-neutral-pressed: rgba(255, 255, 255, 0.08); + --overlay-scrim: rgba(0, 0, 0, 0.48); + } } .installer-toast-stack { @@ -1354,6 +1396,7 @@ body { align-items: center; justify-content: center; line-height: 0; + color: var(--fgcolor-neutral-weak); } .step-indicator svg { diff --git a/app/views/install/installer/icons/appwrite-logo.svg b/app/views/install/installer/icons/appwrite-logo.svg index f0f2fa5af0..af9378f8fc 100644 --- a/app/views/install/installer/icons/appwrite-logo.svg +++ b/app/views/install/installer/icons/appwrite-logo.svg @@ -1,13 +1,13 @@ diff --git a/app/views/install/installer/icons/indicator-inactive.svg b/app/views/install/installer/icons/indicator-inactive.svg index 78b5ef3ac0..550101da9b 100644 --- a/app/views/install/installer/icons/indicator-inactive.svg +++ b/app/views/install/installer/icons/indicator-inactive.svg @@ -1,3 +1,3 @@ From d27aad6e674783cdf21145945ffa7aab937084f7 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Thu, 19 Mar 2026 22:10:23 +1300 Subject: [PATCH 4/6] (feat): add enabledDatabases config to hide unsupported databases in installer --- app/views/install/installer.phtml | 2 + .../install/installer/js/modules/context.js | 6 ++ app/views/install/installer/js/steps.js | 18 ++++- .../installer/templates/steps/step-1.phtml | 10 +++ .../Installer/Http/Installer/Install.php | 4 ++ .../Installer/Http/Installer/View.php | 1 + .../Platform/Installer/Runtime/Config.php | 30 +++++++++ src/Appwrite/Platform/Tasks/Install.php | 11 ++++ .../Modules/Installer/Runtime/ConfigTest.php | 66 +++++++++++++++++++ 9 files changed, 147 insertions(+), 1 deletion(-) diff --git a/app/views/install/installer.phtml b/app/views/install/installer.phtml index 3cf2b77aa9..05bc1b80ed 100644 --- a/app/views/install/installer.phtml +++ b/app/views/install/installer.phtml @@ -9,6 +9,7 @@ $defaultAppDomain = ($defaultAppDomain === 'traefik') ? 'localhost' : $defaultAp $defaultEmailCertificates = $defaultEmailCertificates ?? ''; $defaultDatabase = $vars['_APP_DB_ADAPTER']['default'] ?? 'mongodb'; $lockedDatabase = $isUpgrade && empty($lockedDatabase) ? $defaultDatabase : $lockedDatabase; +$enabledDatabases = $enabledDatabases ?? ['mongodb', 'mariadb', 'postgresql']; $isLocalInstall = $isLocalInstall ?? false; @@ -64,6 +65,7 @@ $installerVersion = @filemtime(__DIR__ . '/installer/js/installer.js') ?: time() data-default-secret-key="" data-default-assistant-openai-key="" data-default-database="" + data-enabled-databases="" data-dev-mode="true" diff --git a/app/views/install/installer/js/modules/context.js b/app/views/install/installer/js/modules/context.js index bc08a8c8df..c531ecddce 100644 --- a/app/views/install/installer/js/modules/context.js +++ b/app/views/install/installer/js/modules/context.js @@ -2,6 +2,11 @@ const getBodyDataset = () => document.body?.dataset ?? {}; const isUpgradeMode = () => getBodyDataset().upgrade === 'true'; const getLockedDatabase = () => getBodyDataset().lockedDatabase || ''; + const getEnabledDatabases = () => { + const raw = getBodyDataset().enabledDatabases; + if (!raw) return ['mongodb', 'mariadb', 'postgresql']; + try { return JSON.parse(raw); } catch (e) { return ['mongodb', 'mariadb', 'postgresql']; } + }; const STEP_IDS = Object.freeze({ CONFIG_FILES: 'config-files', @@ -95,6 +100,7 @@ getBodyDataset, isUpgradeMode, getLockedDatabase, + getEnabledDatabases, STEP_IDS, STATUS, SSE_EVENTS, diff --git a/app/views/install/installer/js/steps.js b/app/views/install/installer/js/steps.js index d8b0621761..2a71d075cc 100644 --- a/app/views/install/installer/js/steps.js +++ b/app/views/install/installer/js/steps.js @@ -9,7 +9,8 @@ const { INSTALLATION_STEPS, clampStep, - isUpgradeMode + isUpgradeMode, + getEnabledDatabases } = Context; const { @@ -79,6 +80,19 @@ } }; + const applyEnabledDatabases = (root) => { + const enabled = getEnabledDatabases?.() || []; + const radios = root.querySelectorAll('input[name="database"]'); + radios.forEach((radio) => { + if (!enabled.includes(radio.value)) { + const card = radio.closest('.selector-card'); + if (card) { + card.remove(); + } + } + }); + }; + const bindDatabaseSelection = (root) => { const radios = root.querySelectorAll('input[name="database"]'); radios.forEach((radio) => { @@ -139,6 +153,8 @@ return; } + applyEnabledDatabases(root); + const lockedDatabase = getLockedDatabase?.() || ''; if (lockedDatabase) { lockDatabaseSelection(root, lockedDatabase); diff --git a/app/views/install/installer/templates/steps/step-1.phtml b/app/views/install/installer/templates/steps/step-1.phtml index ae7674c3bf..8f4a726158 100644 --- a/app/views/install/installer/templates/steps/step-1.phtml +++ b/app/views/install/installer/templates/steps/step-1.phtml @@ -7,8 +7,12 @@ $defaultHttpsPort = $defaultHttpsPort ?? '443'; $defaultEmailCertificates = $defaultEmailCertificates ?? ''; $defaultAssistantOpenAIKey = $defaultAssistantOpenAIKey ?? ''; $defaultDatabase = $defaultDatabase ?? 'mongodb'; +$enabledDatabases = $enabledDatabases ?? ['mongodb', 'mariadb', 'postgresql']; $selectedDatabase = $lockedDatabase ?: $defaultDatabase; $isDatabaseLocked = !empty($lockedDatabase); +$mongoEnabled = in_array('mongodb', $enabledDatabases, true); +$mariaEnabled = in_array('mariadb', $enabledDatabases, true); +$postgresEnabled = in_array('postgresql', $enabledDatabases, true); $mongoDisabled = $isDatabaseLocked && $selectedDatabase !== 'mongodb'; $mariaDisabled = $isDatabaseLocked && $selectedDatabase !== 'mariadb'; $postgresDisabled = $isDatabaseLocked && $selectedDatabase !== 'postgresql'; @@ -43,6 +47,7 @@ $assistantOpenAIKeyValue = htmlspecialchars((string) $defaultAssistantOpenAIKey,
+