From febe332050d5a94072e6b7e69399556fedec277d Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Mon, 18 May 2026 10:56:29 +0530 Subject: [PATCH 1/7] Add Unity SDK getting started docs --- docs/sdks/unity/GETTING_STARTED.md | 113 +++++++++++++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 docs/sdks/unity/GETTING_STARTED.md diff --git a/docs/sdks/unity/GETTING_STARTED.md b/docs/sdks/unity/GETTING_STARTED.md new file mode 100644 index 0000000000..ae932eebc2 --- /dev/null +++ b/docs/sdks/unity/GETTING_STARTED.md @@ -0,0 +1,113 @@ +## Getting Started + +Before you begin, create an Appwrite project and add a Unity platform in your Appwrite Console. + +This SDK requires the following Unity packages and libraries: + +- [**UniTask**](https://github.com/Cysharp/UniTask): For async/await support in Unity. +- [**NativeWebSocket**](https://github.com/endel/NativeWebSocket): For WebSocket realtime subscriptions. +- **System.Text.Json**: For JSON serialization, provided as a DLL in the project. + +After installing the SDK, open **Appwrite → Setup Assistant** in Unity and install the required dependencies. + +### Configure the SDK + +Create an Appwrite configuration using the **QuickStart** window in the **Appwrite Setup Assistant**, or through **Appwrite → Create Configuration**. + +### Using AppwriteManager + +```csharp +[SerializeField] private AppwriteConfig config; +private AppwriteManager _manager; + +private async UniTask ExampleWithManager() +{ + _manager = AppwriteManager.Instance ?? new GameObject("AppwriteManager").AddComponent(); + _manager.SetConfig(config); + + var success = await _manager.Initialize(); + if (!success) + { + Debug.LogError("Failed to initialize AppwriteManager"); + return; + } + + var client = _manager.Client; + var pingResult = await client.Ping(); + Debug.Log($"Ping result: {pingResult}"); + + var account = _manager.GetService(); + var databases = _manager.GetService(); + + var realtime = _manager.Realtime; + var subscription = realtime.Subscribe( + new[] { "databases.*.collections.*.documents" }, + response => Debug.Log($"Realtime event: {response.Events[0]}") + ); +} +``` + +### Using Client directly + +```csharp +[SerializeField] private AppwriteConfig config; + +private async UniTask ExampleWithDirectClient() +{ + var client = new Client() + .SetEndpoint(config.Endpoint) + .SetProject(config.ProjectId); + + if (!string.IsNullOrEmpty(config.DevKey)) + { + client.SetDevKey(config.DevKey); + } + + if (!string.IsNullOrEmpty(config.RealtimeEndpoint)) + { + client.SetEndPointRealtime(config.RealtimeEndpoint); + } + + var pingResult = await client.Ping(); + Debug.Log($"Direct client ping: {pingResult}"); + + var account = new Account(client); + var databases = new Databases(client); +} +``` + +### Error handling + +```csharp +try +{ + var result = await client.Ping(); +} +catch (AppwriteException ex) +{ + Debug.LogError($"Appwrite Error: {ex.Message}"); + Debug.LogError($"Status Code: {ex.Code}"); + Debug.LogError($"Response: {ex.Response}"); +} +``` + +## Preparing Models for Databases API + +When working with the Databases API in Unity, models should be prepared for serialization using the System.Text.Json library. By default, System.Text.Json converts property names from PascalCase to camelCase when serializing to JSON. If your Appwrite collection attributes are not in camelCase, this can cause errors due to mismatches between serialized property names and actual attribute names in your collection. + +To avoid this, add the `JsonPropertyName` attribute to each property in your model class to match the attribute name in Appwrite: + +```csharp +using System.Text.Json.Serialization; + +public class TestModel +{ + [JsonPropertyName("name")] + public string Name { get; set; } + + [JsonPropertyName("release_date")] + public System.DateTime ReleaseDate { get; set; } +} +``` + +The `JsonPropertyName` attribute ensures your data object is serialized with the correct attribute names for Appwrite databases. From 5b3af53bddbf3ddf85d0f5db6b89cd3a194c7fa1 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Mon, 18 May 2026 11:04:07 +0530 Subject: [PATCH 2/7] Address Unity SDK docs review --- docs/sdks/unity/GETTING_STARTED.md | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/docs/sdks/unity/GETTING_STARTED.md b/docs/sdks/unity/GETTING_STARTED.md index ae932eebc2..174e932366 100644 --- a/docs/sdks/unity/GETTING_STARTED.md +++ b/docs/sdks/unity/GETTING_STARTED.md @@ -36,13 +36,17 @@ private async UniTask ExampleWithManager() var pingResult = await client.Ping(); Debug.Log($"Ping result: {pingResult}"); - var account = _manager.GetService(); - var databases = _manager.GetService(); - var realtime = _manager.Realtime; var subscription = realtime.Subscribe( new[] { "databases.*.collections.*.documents" }, - response => Debug.Log($"Realtime event: {response.Events[0]}") + response => + { + var eventName = response.Events != null && response.Events.Length > 0 + ? response.Events[0] + : "unknown"; + + Debug.Log($"Realtime event: {eventName}"); + } ); } ``` @@ -70,9 +74,6 @@ private async UniTask ExampleWithDirectClient() var pingResult = await client.Ping(); Debug.Log($"Direct client ping: {pingResult}"); - - var account = new Account(client); - var databases = new Databases(client); } ``` @@ -93,7 +94,7 @@ catch (AppwriteException ex) ## Preparing Models for Databases API -When working with the Databases API in Unity, models should be prepared for serialization using the System.Text.Json library. By default, System.Text.Json converts property names from PascalCase to camelCase when serializing to JSON. If your Appwrite collection attributes are not in camelCase, this can cause errors due to mismatches between serialized property names and actual attribute names in your collection. +When working with the Databases API in Unity, models should be prepared for serialization using the System.Text.Json library. System.Text.Json uses CLR property names by default unless a naming policy is configured. If your project or SDK configuration serializes property names differently from your Appwrite collection attributes, this can cause errors due to mismatches between serialized property names and actual attribute names in your collection. To avoid this, add the `JsonPropertyName` attribute to each property in your model class to match the attribute name in Appwrite: @@ -111,3 +112,10 @@ public class TestModel ``` The `JsonPropertyName` attribute ensures your data object is serialized with the correct attribute names for Appwrite databases. + +### Learn more +You can use the following resources to learn more and get help: + +- 🚀 [Getting Started Tutorial](https://appwrite.io/docs/getting-started-for-client) +- 📜 [Appwrite Docs](https://appwrite.io/docs) +- 💬 [Discord Community](https://appwrite.io/discord) From ab208a01b6d89c9ee10257e836a0bccad18bcf58 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Mon, 18 May 2026 11:22:24 +0530 Subject: [PATCH 3/7] Update Unity docs for client factories --- docs/sdks/unity/GETTING_STARTED.md | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/docs/sdks/unity/GETTING_STARTED.md b/docs/sdks/unity/GETTING_STARTED.md index 174e932366..7e1f37879c 100644 --- a/docs/sdks/unity/GETTING_STARTED.md +++ b/docs/sdks/unity/GETTING_STARTED.md @@ -25,7 +25,7 @@ private async UniTask ExampleWithManager() _manager = AppwriteManager.Instance ?? new GameObject("AppwriteManager").AddComponent(); _manager.SetConfig(config); - var success = await _manager.Initialize(); + var success = await _manager.Initialize(needRealtime: true); if (!success) { Debug.LogError("Failed to initialize AppwriteManager"); @@ -48,35 +48,29 @@ private async UniTask ExampleWithManager() Debug.Log($"Realtime event: {eventName}"); } ); + + // Keep a reference to close the subscription when your MonoBehaviour is destroyed. + // subscription.Close(); } ``` ### Using Client directly ```csharp -[SerializeField] private AppwriteConfig config; - private async UniTask ExampleWithDirectClient() { - var client = new Client() - .SetEndpoint(config.Endpoint) - .SetProject(config.ProjectId); - - if (!string.IsNullOrEmpty(config.DevKey)) - { - client.SetDevKey(config.DevKey); - } - - if (!string.IsNullOrEmpty(config.RealtimeEndpoint)) - { - client.SetEndPointRealtime(config.RealtimeEndpoint); - } + var client = Client.From( + projectId: "", + endpoint: "https://.cloud.appwrite.io/v1", + endpointRealtime: "wss://.cloud.appwrite.io/v1"); var pingResult = await client.Ping(); Debug.Log($"Direct client ping: {pingResult}"); } ``` +You can also create authenticated clients with `Client.FromSession`, `Client.FromDevKey`, or `Client.FromImpersonation` when those authentication flows are needed. + ### Error handling ```csharp @@ -119,3 +113,4 @@ You can use the following resources to learn more and get help: - 🚀 [Getting Started Tutorial](https://appwrite.io/docs/getting-started-for-client) - 📜 [Appwrite Docs](https://appwrite.io/docs) - 💬 [Discord Community](https://appwrite.io/discord) +- 🧰 [Appwrite SDK Generator](https://github.com/appwrite/sdk-generator) From 254606c4f9fbb6762a1ab207913264df0eb0fabe Mon Sep 17 00:00:00 2001 From: fogelito Date: Mon, 18 May 2026 10:00:24 +0300 Subject: [PATCH 4/7] Add orderDesc --- src/Appwrite/Platform/Workers/Deletes.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Appwrite/Platform/Workers/Deletes.php b/src/Appwrite/Platform/Workers/Deletes.php index a58fc48098..b9efd3ac3b 100644 --- a/src/Appwrite/Platform/Workers/Deletes.php +++ b/src/Appwrite/Platform/Workers/Deletes.php @@ -1021,6 +1021,7 @@ class Deletes extends Action Query::equal('resourceInternalId', [$resourceInternalId]), Query::equal('resourceType', [$resourceType]), Query::orderDesc('$createdAt'), + Query::orderDesc(), Query::offset($executionsRetentionCount), ]); From 03e62dde94b13abceb1867bac90d3c6833b10105 Mon Sep 17 00:00:00 2001 From: Atharva Deosthale Date: Mon, 18 May 2026 15:16:59 +0530 Subject: [PATCH 5/7] feat(sites): allow bun and deno as build runtimes for JS frameworks Merge BUN and DENO template runtimes into the runtimes list for every NODE-based Sites framework (analog, angular, nextjs, react, nuxt, vue, sveltekit, astro, tanstack-start, remix, lynx, react-native, vite, other). The Console's build-runtime dropdown reads framework.runtimes, so this is what surfaces bun-1.3 and deno-2.6 in the UI now that openruntimes/open-runtimes#485 has shipped SSR support for both runtimes. The server-side WhiteList validator on POST /v1/sites already accepts any runtime in the global runtimes config, so CLI and REST clients have been able to pick bun/deno all along; this change only flips the UI gate. Flutter stays flutter-only. --- app/config/frameworks.php | 84 ++++++++++++++++++++++++++++++++------- 1 file changed, 70 insertions(+), 14 deletions(-) diff --git a/app/config/frameworks.php b/app/config/frameworks.php index 6078c53c63..342657017f 100644 --- a/app/config/frameworks.php +++ b/app/config/frameworks.php @@ -14,7 +14,11 @@ return [ 'name' => 'Analog', 'screenshotSleep' => 3000, 'buildRuntime' => 'node-22', - 'runtimes' => $templateRuntimes['NODE'], + 'runtimes' => array_merge( + $templateRuntimes['NODE'], + $templateRuntimes['BUN'], + $templateRuntimes['DENO'] + ), 'bundleCommand' => 'bash /usr/local/server/helpers/analog/bundle.sh', 'envCommand' => 'source /usr/local/server/helpers/analog/env.sh', 'adapters' => [ @@ -40,7 +44,11 @@ return [ 'name' => 'Angular', 'screenshotSleep' => 3000, 'buildRuntime' => 'node-22', - 'runtimes' => $templateRuntimes['NODE'], + 'runtimes' => array_merge( + $templateRuntimes['NODE'], + $templateRuntimes['BUN'], + $templateRuntimes['DENO'] + ), 'bundleCommand' => 'bash /usr/local/server/helpers/angular/bundle.sh', 'envCommand' => 'source /usr/local/server/helpers/angular/env.sh', 'adapters' => [ @@ -66,7 +74,11 @@ return [ 'name' => 'Next.js', 'screenshotSleep' => 3000, 'buildRuntime' => 'node-22', - 'runtimes' => $templateRuntimes['NODE'], + 'runtimes' => array_merge( + $templateRuntimes['NODE'], + $templateRuntimes['BUN'], + $templateRuntimes['DENO'] + ), 'bundleCommand' => 'bash /usr/local/server/helpers/next-js/bundle.sh', 'envCommand' => 'source /usr/local/server/helpers/next-js/env.sh', 'adapters' => [ @@ -91,7 +103,11 @@ return [ 'name' => 'React', 'screenshotSleep' => 3000, 'buildRuntime' => 'node-22', - 'runtimes' => $templateRuntimes['NODE'], + 'runtimes' => array_merge( + $templateRuntimes['NODE'], + $templateRuntimes['BUN'], + $templateRuntimes['DENO'] + ), 'adapters' => [ 'static' => [ 'key' => 'static', @@ -108,7 +124,11 @@ return [ 'name' => 'Nuxt', 'screenshotSleep' => 3000, 'buildRuntime' => 'node-22', - 'runtimes' => $templateRuntimes['NODE'], + 'runtimes' => array_merge( + $templateRuntimes['NODE'], + $templateRuntimes['BUN'], + $templateRuntimes['DENO'] + ), 'bundleCommand' => 'bash /usr/local/server/helpers/nuxt/bundle.sh', 'envCommand' => 'source /usr/local/server/helpers/nuxt/env.sh', 'adapters' => [ @@ -133,7 +153,11 @@ return [ 'name' => 'Vue.js', 'screenshotSleep' => 5000, 'buildRuntime' => 'node-22', - 'runtimes' => $templateRuntimes['NODE'], + 'runtimes' => array_merge( + $templateRuntimes['NODE'], + $templateRuntimes['BUN'], + $templateRuntimes['DENO'] + ), 'adapters' => [ 'static' => [ 'key' => 'static', @@ -150,7 +174,11 @@ return [ 'name' => 'SvelteKit', 'screenshotSleep' => 3000, 'buildRuntime' => 'node-22', - 'runtimes' => $templateRuntimes['NODE'], + 'runtimes' => array_merge( + $templateRuntimes['NODE'], + $templateRuntimes['BUN'], + $templateRuntimes['DENO'] + ), 'bundleCommand' => 'bash /usr/local/server/helpers/sveltekit/bundle.sh', 'envCommand' => 'source /usr/local/server/helpers/sveltekit/env.sh', 'adapters' => [ @@ -175,7 +203,11 @@ return [ 'name' => 'Astro', 'screenshotSleep' => 3000, 'buildRuntime' => 'node-22', - 'runtimes' => $templateRuntimes['NODE'], + 'runtimes' => array_merge( + $templateRuntimes['NODE'], + $templateRuntimes['BUN'], + $templateRuntimes['DENO'] + ), 'bundleCommand' => 'bash /usr/local/server/helpers/astro/bundle.sh', 'envCommand' => 'source /usr/local/server/helpers/astro/env.sh', 'adapters' => [ @@ -200,7 +232,11 @@ return [ 'name' => 'TanStack Start', 'screenshotSleep' => 3000, 'buildRuntime' => 'node-22', - 'runtimes' => $templateRuntimes['NODE'], + 'runtimes' => array_merge( + $templateRuntimes['NODE'], + $templateRuntimes['BUN'], + $templateRuntimes['DENO'] + ), 'bundleCommand' => 'bash /usr/local/server/helpers/tanstack-start/bundle.sh', 'envCommand' => 'source /usr/local/server/helpers/tanstack-start/env.sh', 'adapters' => [ @@ -225,7 +261,11 @@ return [ 'name' => 'Remix', 'screenshotSleep' => 3000, 'buildRuntime' => 'node-22', - 'runtimes' => $templateRuntimes['NODE'], + 'runtimes' => array_merge( + $templateRuntimes['NODE'], + $templateRuntimes['BUN'], + $templateRuntimes['DENO'] + ), 'bundleCommand' => 'bash /usr/local/server/helpers/remix/bundle.sh', 'envCommand' => 'source /usr/local/server/helpers/remix/env.sh', 'adapters' => [ @@ -250,7 +290,11 @@ return [ 'name' => 'Lynx', 'screenshotSleep' => 5000, 'buildRuntime' => 'node-22', - 'runtimes' => $templateRuntimes['NODE'], + 'runtimes' => array_merge( + $templateRuntimes['NODE'], + $templateRuntimes['BUN'], + $templateRuntimes['DENO'] + ), 'adapters' => [ 'static' => [ 'key' => 'static', @@ -284,7 +328,11 @@ return [ 'name' => 'React Native', 'screenshotSleep' => 3000, 'buildRuntime' => 'node-22', - 'runtimes' => $templateRuntimes['NODE'], + 'runtimes' => array_merge( + $templateRuntimes['NODE'], + $templateRuntimes['BUN'], + $templateRuntimes['DENO'] + ), 'adapters' => [ 'static' => [ 'key' => 'static', @@ -301,7 +349,11 @@ return [ 'name' => 'Vite', 'screenshotSleep' => 3000, 'buildRuntime' => 'node-22', - 'runtimes' => $templateRuntimes['NODE'], + 'runtimes' => array_merge( + $templateRuntimes['NODE'], + $templateRuntimes['BUN'], + $templateRuntimes['DENO'] + ), 'adapters' => [ 'static' => [ 'key' => 'static', @@ -317,7 +369,11 @@ return [ 'name' => 'Other', 'screenshotSleep' => 3000, 'buildRuntime' => 'node-22', - 'runtimes' => $templateRuntimes['NODE'], + 'runtimes' => array_merge( + $templateRuntimes['NODE'], + $templateRuntimes['BUN'], + $templateRuntimes['DENO'] + ), 'adapters' => [ 'static' => [ 'key' => 'static', From b756a382523bbfc5b5a3edbdbad285664639f1b7 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Mon, 18 May 2026 12:39:23 +0530 Subject: [PATCH 6/7] Consolidate shared DI resources --- app/cli.php | 63 +------------ app/init/resources.php | 172 ++++++++++++++++++------------------ app/init/worker/message.php | 6 -- 3 files changed, 89 insertions(+), 152 deletions(-) diff --git a/app/cli.php b/app/cli.php index 9ad223a3ff..496a79eab9 100644 --- a/app/cli.php +++ b/app/cli.php @@ -2,18 +2,10 @@ require_once __DIR__ . '/init.php'; -use Appwrite\Event\Event; -use Appwrite\Event\Publisher\Certificate as CertificatePublisher; -use Appwrite\Event\Publisher\Database as DatabasePublisher; -use Appwrite\Event\Publisher\Delete as DeletePublisher; -use Appwrite\Event\Publisher\Func as FunctionPublisher; -use Appwrite\Event\Publisher\StatsResources as StatsResourcesPublisher; -use Appwrite\Event\Publisher\Usage as UsagePublisher; use Appwrite\Platform\Appwrite; use Appwrite\Runtimes\Runtimes; use Appwrite\Usage\Context as UsageContext; use Appwrite\Utopia\Database\Documents\User; -use Executor\Executor; use Swoole\Runtime; use Swoole\Timer; use Utopia\Cache\Adapter\Pool as CachePool; @@ -27,17 +19,12 @@ use Utopia\Database\Adapter\Pool as DatabasePool; use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Validator\Authorization; -use Utopia\DI\Container; use Utopia\DSN\DSN; use Utopia\Logger\Log; use Utopia\Platform\Service; use Utopia\Pools\Group; -use Utopia\Queue\Broker\Pool as BrokerPool; -use Utopia\Queue\Publisher; -use Utopia\Queue\Queue; use Utopia\Registry\Registry; use Utopia\System\System; -use Utopia\Telemetry\Adapter\None as NoTelemetry; use function Swoole\Coroutine\run; @@ -48,6 +35,7 @@ Config::setParam('runtimes', (new Runtimes('v5'))->getAll(supported: false)); require_once __DIR__ . '/controllers/general.php'; global $register; +global $container; $platform = new Appwrite(); $args = $_SERVER['argv'] ?? []; @@ -59,7 +47,6 @@ if (! isset($args[0])) { } $taskName = $args[0]; -$container = new Container(); $cli = new CLI(new Generic(), $_SERVER['argv'] ?? [], $container); $platform->setCli($cli); @@ -132,10 +119,6 @@ $container->set('dbForPlatform', function ($pools, $cache, $authorization) { return $dbForPlatform; }, ['pools', 'cache', 'authorization']); -$container->set('console', function () { - return new Document(Config::getParam('console')); -}, []); - $container->set( 'isResourceBlocked', fn () => fn (Document $project, string $resourceType, ?string $resourceId) => false, @@ -252,48 +235,10 @@ $container->set('getLogsDB', function (Group $pools, Cache $cache, Authorization return $database; }; }, ['pools', 'cache', 'authorization']); -$container->set('publisher', function (Group $pools) { - return new BrokerPool(publisher: $pools->get('publisher')); -}, ['pools']); -$container->set('publisherDatabases', function (BrokerPool $publisher) { - return $publisher; -}, ['publisher']); -$container->set('publisherFunctions', function (BrokerPool $publisher) { - return $publisher; -}, ['publisher']); -$container->set('publisherMigrations', function (BrokerPool $publisher) { - return $publisher; -}, ['publisher']); -$container->set('publisherMessaging', function (BrokerPool $publisher) { - return $publisher; -}, ['publisher']); + $container->set('usage', function () { return new UsageContext(); }, []); -$container->set('publisherForUsage', fn (Publisher $publisher) => new UsagePublisher( - $publisher, - new Queue(System::getEnv('_APP_STATS_USAGE_QUEUE_NAME', Event::STATS_USAGE_QUEUE_NAME)) -), ['publisher']); -$container->set('publisherForCertificates', fn (Publisher $publisher) => new CertificatePublisher( - $publisher, - new Queue(System::getEnv('_APP_CERTIFICATES_QUEUE_NAME', Event::CERTIFICATES_QUEUE_NAME)) -), ['publisher']); -$container->set('publisherForStatsResources', fn (Publisher $publisher) => new StatsResourcesPublisher( - $publisher, - new Queue(System::getEnv('_APP_STATS_RESOURCES_QUEUE_NAME', Event::STATS_RESOURCES_QUEUE_NAME)) -), ['publisher']); -$container->set('publisherForFunctions', fn (Publisher $publisher) => new FunctionPublisher( - $publisher, - new Queue(System::getEnv('_APP_FUNCTIONS_QUEUE_NAME', Event::FUNCTIONS_QUEUE_NAME), 'utopia-queue', Event::FUNCTIONS_QUEUE_TTL) -), ['publisher']); -$container->set('publisherForDatabase', fn (Publisher $publisherDatabases) => new DatabasePublisher( - $publisherDatabases, - new Queue(System::getEnv('_APP_DATABASE_QUEUE_NAME', Event::DATABASE_QUEUE_NAME)) -), ['publisherDatabases']); -$container->set('publisherForDeletes', fn (Publisher $publisher) => new DeletePublisher( - $publisher, - new Queue(System::getEnv('_APP_DELETE_QUEUE_NAME', Event::DELETE_QUEUE_NAME)) -), ['publisher']); $container->set('logError', function (Registry $register) { return function (Throwable $error, string $namespace, string $action) use ($register) { Console::error('[Error] Timestamp: ' . date('c', time())); @@ -346,14 +291,10 @@ $container->set('logError', function (Registry $register) { }; }, ['register']); -$container->set('executor', fn () => new Executor(), []); - $container->set('bus', function (Registry $register) use ($container) { return $register->get('bus')->setResolver(fn (string $name) => $container->get($name)); }, ['register']); -$container->set('telemetry', fn () => new NoTelemetry(), []); - $exitCode = 0; $cli diff --git a/app/init/resources.php b/app/init/resources.php index d48a60c06c..7b7e13482c 100644 --- a/app/init/resources.php +++ b/app/init/resources.php @@ -53,6 +53,93 @@ global $register; global $container; $container = new Container(); +$container->set('console', fn () => new Document(Config::getParam('console')), []); + +$container->set('executor', fn () => new Executor(), []); + +$container->set('telemetry', fn () => new NoTelemetry(), []); + +$container->set('publisher', fn (Group $pools) => new BrokerPool(publisher: $pools->get('publisher')), ['pools']); + +$container->set('publisherDatabases', fn (Publisher $publisher) => $publisher, ['publisher']); + +$container->set('publisherFunctions', fn (Publisher $publisher) => $publisher, ['publisher']); + +$container->set('publisherMigrations', fn (Publisher $publisher) => $publisher, ['publisher']); + +$container->set('publisherMails', fn (Publisher $publisher) => $publisher, ['publisher']); + +$container->set('publisherDeletes', fn (Publisher $publisher) => $publisher, ['publisher']); + +$container->set('publisherMessaging', fn (Publisher $publisher) => $publisher, ['publisher']); + +$container->set('publisherWebhooks', fn (Publisher $publisher) => $publisher, ['publisher']); + +$container->set('publisherForAudits', fn (Publisher $publisher) => new AuditPublisher( + $publisher, + new Queue(System::getEnv('_APP_AUDITS_QUEUE_NAME', Event::AUDITS_QUEUE_NAME)) +), ['publisher']); + +$container->set('publisherForCertificates', fn (Publisher $publisher) => new CertificatePublisher( + $publisher, + new Queue(System::getEnv('_APP_CERTIFICATES_QUEUE_NAME', Event::CERTIFICATES_QUEUE_NAME)) +), ['publisher']); + +$container->set('publisherForScreenshots', fn (Publisher $publisher) => new ScreenshotPublisher( + $publisher, + new Queue(System::getEnv('_APP_SCREENSHOTS_QUEUE_NAME', Event::SCREENSHOTS_QUEUE_NAME)) +), ['publisher']); + +$container->set('publisherForUsage', fn (Publisher $publisher) => new UsagePublisher( + $publisher, + new Queue(System::getEnv('_APP_STATS_USAGE_QUEUE_NAME', Event::STATS_USAGE_QUEUE_NAME)) +), ['publisher']); + +$container->set('publisherForExecutions', fn (Publisher $publisher) => new ExecutionPublisher( + $publisher, + new Queue(System::getEnv('_APP_EXECUTIONS_QUEUE_NAME', Event::EXECUTIONS_QUEUE_NAME)) +), ['publisher']); + +$container->set('publisherForFunctions', fn (Publisher $publisher) => new FunctionPublisher( + $publisher, + new Queue(System::getEnv('_APP_FUNCTIONS_QUEUE_NAME', Event::FUNCTIONS_QUEUE_NAME), 'utopia-queue', Event::FUNCTIONS_QUEUE_TTL) +), ['publisher']); + +$container->set('publisherForMigrations', fn (Publisher $publisher) => new MigrationPublisher( + $publisher, + new Queue(System::getEnv('_APP_MIGRATIONS_QUEUE_NAME', Event::MIGRATIONS_QUEUE_NAME)) +), ['publisher']); + +$container->set('publisherForStatsResources', fn (Publisher $publisher) => new StatsResourcesPublisher( + $publisher, + new Queue(System::getEnv('_APP_STATS_RESOURCES_QUEUE_NAME', Event::STATS_RESOURCES_QUEUE_NAME)) +), ['publisher']); + +$container->set('publisherForBuilds', fn (Publisher $publisher) => new BuildPublisher( + $publisher, + new Queue(System::getEnv('_APP_BUILDS_QUEUE_NAME', Event::BUILDS_QUEUE_NAME)) +), ['publisher']); + +$container->set('publisherForDatabase', fn (Publisher $publisherDatabases) => new DatabasePublisher( + $publisherDatabases, + new Queue(System::getEnv('_APP_DATABASE_QUEUE_NAME', Event::DATABASE_QUEUE_NAME)) +), ['publisherDatabases']); + +$container->set('publisherForDeletes', fn (Publisher $publisher) => new DeletePublisher( + $publisher, + new Queue(System::getEnv('_APP_DELETE_QUEUE_NAME', Event::DELETE_QUEUE_NAME)) +), ['publisher']); + +$container->set('publisherForMails', fn (Publisher $publisher) => new MailPublisher( + $publisher, + new Queue(System::getEnv('_APP_MAILS_QUEUE_NAME', Event::MAILS_QUEUE_NAME)) +), ['publisher']); + +$container->set('publisherForMessaging', fn (Publisher $publisher) => new MessagingPublisher( + $publisher, + new Queue(System::getEnv('_APP_MESSAGING_QUEUE_NAME', Event::MESSAGING_QUEUE_NAME)) +), ['publisher']); + $container->set('logger', function ($register) { return $register->get('logger'); }, ['register']); @@ -67,83 +154,6 @@ $container->set('localeCodes', function () { return array_map(fn ($locale) => $locale['code'], Config::getParam('locale-codes', [])); }); -// Queues - shared infrastructure (stateless pool wrappers) -$container->set('publisher', function (Group $pools) { - return new BrokerPool(publisher: $pools->get('publisher')); -}, ['pools']); -$container->set('publisherDatabases', function (Publisher $publisher) { - return $publisher; -}, ['publisher']); -$container->set('publisherFunctions', function (Publisher $publisher) { - return $publisher; -}, ['publisher']); -$container->set('publisherMigrations', function (Publisher $publisher) { - return $publisher; -}, ['publisher']); -$container->set('publisherMails', function (Publisher $publisher) { - return $publisher; -}, ['publisher']); -$container->set('publisherDeletes', function (Publisher $publisher) { - return $publisher; -}, ['publisher']); -$container->set('publisherMessaging', function (Publisher $publisher) { - return $publisher; -}, ['publisher']); -$container->set('publisherWebhooks', function (Publisher $publisher) { - return $publisher; -}, ['publisher']); -$container->set('publisherForAudits', fn (Publisher $publisher) => new AuditPublisher( - $publisher, - new Queue(System::getEnv('_APP_AUDITS_QUEUE_NAME', Event::AUDITS_QUEUE_NAME)) -), ['publisher']); -$container->set('publisherForCertificates', fn (Publisher $publisher) => new CertificatePublisher( - $publisher, - new Queue(System::getEnv('_APP_CERTIFICATES_QUEUE_NAME', Event::CERTIFICATES_QUEUE_NAME)) -), ['publisher']); -$container->set('publisherForScreenshots', fn (Publisher $publisher) => new ScreenshotPublisher( - $publisher, - new Queue(System::getEnv('_APP_SCREENSHOTS_QUEUE_NAME', Event::SCREENSHOTS_QUEUE_NAME)) -), ['publisher']); -$container->set('publisherForUsage', fn (Publisher $publisher) => new UsagePublisher( - $publisher, - new Queue(System::getEnv('_APP_STATS_USAGE_QUEUE_NAME', Event::STATS_USAGE_QUEUE_NAME)) -), ['publisher']); -$container->set('publisherForExecutions', fn (Publisher $publisher) => new ExecutionPublisher( - $publisher, - new Queue(System::getEnv('_APP_EXECUTIONS_QUEUE_NAME', Event::EXECUTIONS_QUEUE_NAME)) -), ['publisher']); -$container->set('publisherForFunctions', fn (Publisher $publisher) => new FunctionPublisher( - $publisher, - new Queue(System::getEnv('_APP_FUNCTIONS_QUEUE_NAME', Event::FUNCTIONS_QUEUE_NAME), 'utopia-queue', Event::FUNCTIONS_QUEUE_TTL) -), ['publisher']); -$container->set('publisherForMigrations', fn (Publisher $publisher) => new MigrationPublisher( - $publisher, - new Queue(System::getEnv('_APP_MIGRATIONS_QUEUE_NAME', Event::MIGRATIONS_QUEUE_NAME)) -), ['publisher']); -$container->set('publisherForStatsResources', fn (Publisher $publisher) => new StatsResourcesPublisher( - $publisher, - new Queue(System::getEnv('_APP_STATS_RESOURCES_QUEUE_NAME', Event::STATS_RESOURCES_QUEUE_NAME)) -), ['publisher']); -$container->set('publisherForBuilds', fn (Publisher $publisher) => new BuildPublisher( - $publisher, - new Queue(System::getEnv('_APP_BUILDS_QUEUE_NAME', Event::BUILDS_QUEUE_NAME)) -), ['publisher']); -$container->set('publisherForDatabase', fn (Publisher $publisherDatabases) => new DatabasePublisher( - $publisherDatabases, - new Queue(System::getEnv('_APP_DATABASE_QUEUE_NAME', Event::DATABASE_QUEUE_NAME)) -), ['publisherDatabases']); -$container->set('publisherForDeletes', fn (Publisher $publisher) => new DeletePublisher( - $publisher, - new Queue(System::getEnv('_APP_DELETE_QUEUE_NAME', Event::DELETE_QUEUE_NAME)) -), ['publisher']); -$container->set('publisherForMails', fn (Publisher $publisher) => new MailPublisher( - $publisher, - new Queue(System::getEnv('_APP_MAILS_QUEUE_NAME', Event::MAILS_QUEUE_NAME)) -), ['publisher']); -$container->set('publisherForMessaging', fn (Publisher $publisher) => new MessagingPublisher( - $publisher, - new Queue(System::getEnv('_APP_MESSAGING_QUEUE_NAME', Event::MESSAGING_QUEUE_NAME)) -), ['publisher']); /** * Platform configuration @@ -152,10 +162,6 @@ $container->set('platform', function () { return Config::getParam('platform', []); }, []); -$container->set('console', function () { - return new Document(Config::getParam('console')); -}, []); - $container->set('authorization', function () { return new Authorization(); }, []); @@ -214,8 +220,6 @@ $container->set('getLogsDB', function (Group $pools, Cache $cache, Authorization }; }, ['pools', 'cache', 'authorization']); -$container->set('telemetry', fn () => new NoTelemetry()); - $container->set('cache', function (Group $pools, Telemetry $telemetry) { $list = Config::getParam('pools-cache', []); $adapters = []; @@ -416,5 +420,3 @@ $container->set( 'isResourceBlocked', fn () => fn (Document $project, string $resourceType, ?string $resourceId) => false ); - -$container->set('executor', fn () => new Executor()); diff --git a/app/init/worker/message.php b/app/init/worker/message.php index 3585421a28..5cabfc7859 100644 --- a/app/init/worker/message.php +++ b/app/init/worker/message.php @@ -1,7 +1,6 @@ set('publisherForFunctions', fn (Publisher $publisher) => new FunctionPublisher( - $publisher, - new Queue(System::getEnv('_APP_FUNCTIONS_QUEUE_NAME', Event::FUNCTIONS_QUEUE_NAME), 'utopia-queue', Event::FUNCTIONS_QUEUE_TTL) - ), ['publisher']); $container->set('queueForRealtime', function () { return new Realtime(); }, []); From 2cd422f4c2ca619d9b45a5dca1e374a6300579ca Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Mon, 18 May 2026 13:58:24 +0100 Subject: [PATCH 7/7] fix(health): use injected cache resource for healthcheck Inject the live cache resource and ping it directly instead of rebuilding pool adapters from pools-cache. The container already constructs the right adapter (pool, multiplexing, none), so the healthcheck now exercises the actual configured cache. Co-Authored-By: Claude Opus 4.7 --- .../Modules/Health/Http/Health/Cache/Get.php | 47 +++++++------------ 1 file changed, 16 insertions(+), 31 deletions(-) diff --git a/src/Appwrite/Platform/Modules/Health/Http/Health/Cache/Get.php b/src/Appwrite/Platform/Modules/Health/Http/Health/Cache/Get.php index bf7c3c4889..8d717eb9ab 100644 --- a/src/Appwrite/Platform/Modules/Health/Http/Health/Cache/Get.php +++ b/src/Appwrite/Platform/Modules/Health/Http/Health/Cache/Get.php @@ -8,12 +8,10 @@ use Appwrite\SDK\ContentType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response; -use Utopia\Cache\Adapter\Pool as CachePool; -use Utopia\Config\Config; +use Utopia\Cache\Cache; use Utopia\Database\Document; use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; -use Utopia\Pools\Group; class Get extends Action { @@ -47,45 +45,32 @@ class Get extends Action contentType: ContentType::JSON )) ->inject('response') - ->inject('pools') + ->inject('cache') ->callback($this->action(...)); } - public function action(Response $response, Group $pools): void + public function action(Response $response, Cache $cache): void { $output = []; - $failures = []; - $configs = [ - 'Cache' => Config::getParam('pools-cache'), - ]; + $checkStart = \microtime(true); - foreach ($configs as $key => $config) { - foreach ($config as $cache) { - try { - $adapter = new CachePool($pools->get($cache)); - - $checkStart = \microtime(true); - - if ($adapter->ping()) { - $output[] = new Document([ - 'name' => $key . " ($cache)", - 'status' => 'pass', - 'ping' => \round((\microtime(true) - $checkStart) * 1000), - ]); - } else { - $failures[] = $cache; - } - } catch (\Throwable) { - $failures[] = $cache; - } - } + try { + $ok = $cache->ping(); + } catch (\Throwable) { + $ok = false; } - if (!empty($failures)) { - throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Cache failure on: ' . \implode(', ', $failures)); + if (!$ok) { + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Cache failure on: cache'); } + $output[] = new Document([ + 'name' => 'Cache', + 'status' => 'pass', + 'ping' => \round((\microtime(true) - $checkStart) * 1000), + ]); + $response->dynamic(new Document([ 'statuses' => $output, 'total' => \count($output),