From 6e09bcf6c64f6e46554aedd832a143e2170dcc81 Mon Sep 17 00:00:00 2001 From: Steven Nguyen <1477010+stnguyen90@users.noreply.github.com> Date: Wed, 27 Nov 2024 18:10:28 +0000 Subject: [PATCH 001/143] Bump appwrite version to 1.6.1-RC1 --- README-CN.md | 6 +++--- README.md | 6 +++--- app/init.php | 2 +- src/Appwrite/Migration/Migration.php | 1 + 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/README-CN.md b/README-CN.md index 92a9bf9806..e43e9ac0d0 100644 --- a/README-CN.md +++ b/README-CN.md @@ -67,7 +67,7 @@ docker run -it --rm \ --volume /var/run/docker.sock:/var/run/docker.sock \ --volume "$(pwd)"/appwrite:/usr/src/code/appwrite:rw \ --entrypoint="install" \ - appwrite/appwrite:1.6.1 + appwrite/appwrite:1.6.1-RC1 ``` ### Windows @@ -79,7 +79,7 @@ docker run -it --rm ^ --volume //var/run/docker.sock:/var/run/docker.sock ^ --volume "%cd%"/appwrite:/usr/src/code/appwrite:rw ^ --entrypoint="install" ^ - appwrite/appwrite:1.6.1 + appwrite/appwrite:1.6.1-RC1 ``` #### PowerShell @@ -89,7 +89,7 @@ docker run -it --rm ` --volume /var/run/docker.sock:/var/run/docker.sock ` --volume ${pwd}/appwrite:/usr/src/code/appwrite:rw ` --entrypoint="install" ` - appwrite/appwrite:1.6.1 + appwrite/appwrite:1.6.1-RC1 ``` 运行后,可以在浏览器上访问 http://localhost 找到 Appwrite 控制台。在非 Linux 的本机主机上完成安装后,服务器可能需要几分钟才能启动。 diff --git a/README.md b/README.md index ab57e65c4c..1d121e5407 100644 --- a/README.md +++ b/README.md @@ -75,7 +75,7 @@ docker run -it --rm \ --volume /var/run/docker.sock:/var/run/docker.sock \ --volume "$(pwd)"/appwrite:/usr/src/code/appwrite:rw \ --entrypoint="install" \ - appwrite/appwrite:1.6.1 + appwrite/appwrite:1.6.1-RC1 ``` ### Windows @@ -87,7 +87,7 @@ docker run -it --rm ^ --volume //var/run/docker.sock:/var/run/docker.sock ^ --volume "%cd%"/appwrite:/usr/src/code/appwrite:rw ^ --entrypoint="install" ^ - appwrite/appwrite:1.6.1 + appwrite/appwrite:1.6.1-RC1 ``` #### PowerShell @@ -97,7 +97,7 @@ docker run -it --rm ` --volume /var/run/docker.sock:/var/run/docker.sock ` --volume ${pwd}/appwrite:/usr/src/code/appwrite:rw ` --entrypoint="install" ` - appwrite/appwrite:1.6.1 + appwrite/appwrite:1.6.1-RC1 ``` Once the Docker installation is complete, go to http://localhost to access the Appwrite console from your browser. Please note that on non-Linux native hosts, the server might take a few minutes to start after completing the installation. diff --git a/app/init.php b/app/init.php index 30ece74553..3cccabe78d 100644 --- a/app/init.php +++ b/app/init.php @@ -123,7 +123,7 @@ const APP_USER_ACCESS = 24 * 60 * 60; // 24 hours const APP_PROJECT_ACCESS = 24 * 60 * 60; // 24 hours const APP_CACHE_UPDATE = 24 * 60 * 60; // 24 hours const APP_CACHE_BUSTER = 4318; -const APP_VERSION_STABLE = '1.6.1'; +const APP_VERSION_STABLE = '1.6.1-RC1'; const APP_DATABASE_ATTRIBUTE_EMAIL = 'email'; const APP_DATABASE_ATTRIBUTE_ENUM = 'enum'; const APP_DATABASE_ATTRIBUTE_IP = 'ip'; diff --git a/src/Appwrite/Migration/Migration.php b/src/Appwrite/Migration/Migration.php index 19f69b1a4f..71b5706ed7 100644 --- a/src/Appwrite/Migration/Migration.php +++ b/src/Appwrite/Migration/Migration.php @@ -92,6 +92,7 @@ abstract class Migration '1.5.11' => 'V20', '1.6.0' => 'V21', '1.6.1' => 'V21', + '1.6.1-RC1' => 'V21', ]; /** From cce6222184c861aedd0966c9d380686623ba60d0 Mon Sep 17 00:00:00 2001 From: Gurjeet Singh Virdee <73753957+gurjeetsinghvirdee@users.noreply.github.com> Date: Fri, 20 Dec 2024 22:24:39 +0530 Subject: [PATCH 002/143] docs: update CLI commands in documentation --- docs/sdks/cli/GETTING_STARTED.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/sdks/cli/GETTING_STARTED.md b/docs/sdks/cli/GETTING_STARTED.md index 564fb4d5f9..a7c3174516 100644 --- a/docs/sdks/cli/GETTING_STARTED.md +++ b/docs/sdks/cli/GETTING_STARTED.md @@ -59,7 +59,7 @@ My Awesome Function You can now deploy this function using ```sh -$ appwrite deploy function +$ appwrite push function ? Which functions would you like to deploy? My Awesome Function (61d1a4c81dfcd95bc834) ℹ Info Deploying function My Awesome Function ( 61d1a4c81dfcd95bc834 ) @@ -73,7 +73,7 @@ Your function has now been deployed on your Appwrite server! As soon as the buil Similarly, you can deploy all your collections to your Appwrite server using ```sh -appwrite deploy collections +appwrite push collections ``` > ### Note @@ -98,7 +98,7 @@ $ appwrite users list To create a document you can use the following command ```sh -$ appwrite database createDocument --collectionId --documentId 'unique()' --data '{ "Name": "Iron Man" }' --permissions 'read("any")' 'read("team:abc")' +$ appwrite databases create-document --database-id= --collection-id= --document-id="unique()" --data '{"name": "Walter O Brein"}' ``` ### Some Gotchas @@ -140,4 +140,4 @@ The Appwrite CLI can also work in a CI environment. The initialisation of the CL ```sh appwrite client --endpoint http://localhost/v1 --projectId --key -``` \ No newline at end of file +``` From c451074278cf86c40ad69f4a4dc4c126995ad5a3 Mon Sep 17 00:00:00 2001 From: Gurjeet Singh Virdee <73753957+gurjeetsinghvirdee@users.noreply.github.com> Date: Mon, 6 Jan 2025 01:29:06 +0530 Subject: [PATCH 003/143] restored the permissions term --- docs/sdks/cli/GETTING_STARTED.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sdks/cli/GETTING_STARTED.md b/docs/sdks/cli/GETTING_STARTED.md index a7c3174516..fbd7dca96b 100644 --- a/docs/sdks/cli/GETTING_STARTED.md +++ b/docs/sdks/cli/GETTING_STARTED.md @@ -98,7 +98,7 @@ $ appwrite users list To create a document you can use the following command ```sh -$ appwrite databases create-document --database-id= --collection-id= --document-id="unique()" --data '{"name": "Walter O Brein"}' +$ appwrite databases create-document --database-id= --collection-id= --document-id="unique()" --data '{"name": "Walter O Brein"} --permissions 'read("any")' 'read("team:abc")' ``` ### Some Gotchas From a39341ada8d4494fc0be3789ad286b9a9a50673f Mon Sep 17 00:00:00 2001 From: Gurjeet Singh Virdee <73753957+gurjeetsinghvirdee@users.noreply.github.com> Date: Wed, 8 Jan 2025 14:48:54 +0530 Subject: [PATCH 004/143] fix: add missing single quote --- docs/sdks/cli/GETTING_STARTED.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sdks/cli/GETTING_STARTED.md b/docs/sdks/cli/GETTING_STARTED.md index fbd7dca96b..3f3c0ca56c 100644 --- a/docs/sdks/cli/GETTING_STARTED.md +++ b/docs/sdks/cli/GETTING_STARTED.md @@ -98,7 +98,7 @@ $ appwrite users list To create a document you can use the following command ```sh -$ appwrite databases create-document --database-id= --collection-id= --document-id="unique()" --data '{"name": "Walter O Brein"} --permissions 'read("any")' 'read("team:abc")' +$ appwrite databases create-document --database-id= --collection-id= --document-id="unique()" --data '{"name": "Walter O Brein"}' --permissions 'read("any")' 'read("team:abc")' ``` ### Some Gotchas From 8508e6009bf062038845daeeb68fb4a23436811a Mon Sep 17 00:00:00 2001 From: Gurjeet Singh Virdee <73753957+gurjeetsinghvirdee@users.noreply.github.com> Date: Fri, 24 Jan 2025 19:13:56 +0530 Subject: [PATCH 005/143] refactor: cleaned up syntax by removing unnecessary '=' --- docs/sdks/cli/GETTING_STARTED.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sdks/cli/GETTING_STARTED.md b/docs/sdks/cli/GETTING_STARTED.md index 3f3c0ca56c..1cadb1bbda 100644 --- a/docs/sdks/cli/GETTING_STARTED.md +++ b/docs/sdks/cli/GETTING_STARTED.md @@ -98,7 +98,7 @@ $ appwrite users list To create a document you can use the following command ```sh -$ appwrite databases create-document --database-id= --collection-id= --document-id="unique()" --data '{"name": "Walter O Brein"}' --permissions 'read("any")' 'read("team:abc")' +$ appwrite databases create-document --database-id --collection-id --document-id "unique()" --data '{"name": "Walter O Brein"}' --permissions 'read("any")' 'read("team:abc")' ``` ### Some Gotchas From 2de4e7166230be73007cf2fa7daddf527f31e7c3 Mon Sep 17 00:00:00 2001 From: Laura Du Ry <123562732+LauraDuRy@users.noreply.github.com> Date: Wed, 29 Jan 2025 09:58:04 +0100 Subject: [PATCH 006/143] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f72133baa6..847c67e965 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -> Appwrite Init has concluded! You can check out all the latest announcements [on our Init website](https://appwrite.io/init) :rocket: +> [Get started with Appwrite](https://cloud.appwrite.io/)

From 507c8bd3ec0a11780a84a9050a9458806d5f8157 Mon Sep 17 00:00:00 2001 From: Laura Du Ry <123562732+LauraDuRy@users.noreply.github.com> Date: Wed, 29 Jan 2025 10:02:40 +0100 Subject: [PATCH 007/143] Update README.md --- README.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/README.md b/README.md index 847c67e965..2b5d1bcc6c 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -> [Get started with Appwrite](https://cloud.appwrite.io/) +> [Get started with Appwrite](https://apwr.dev/appcloud)

@@ -24,8 +24,6 @@ English | [简体中文](README-CN.md) -[**Announcing Appwrite Cloud Public Beta! Sign up today!**](https://cloud.appwrite.io) - Appwrite is an end-to-end backend server for Web, Mobile, Native, or Backend apps packaged as a set of Docker microservices. Appwrite abstracts the complexity and repetitiveness required to build a modern backend API from scratch and allows you to build secure apps faster. Using Appwrite, you can easily integrate your app with user authentication and multiple sign-in methods, a database for storing and querying users and team data, storage and file management, image manipulation, Cloud Functions, and [more services](https://appwrite.io/docs). From 891c1c4093ae762a003e68c50343e106f8565cab Mon Sep 17 00:00:00 2001 From: Laura Du Ry <123562732+LauraDuRy@users.noreply.github.com> Date: Wed, 29 Jan 2025 10:04:15 +0100 Subject: [PATCH 008/143] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2b5d1bcc6c..9bb1aa5c58 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ English | [简体中文](README-CN.md) Appwrite is an end-to-end backend server for Web, Mobile, Native, or Backend apps packaged as a set of Docker microservices. Appwrite abstracts the complexity and repetitiveness required to build a modern backend API from scratch and allows you to build secure apps faster. -Using Appwrite, you can easily integrate your app with user authentication and multiple sign-in methods, a database for storing and querying users and team data, storage and file management, image manipulation, Cloud Functions, and [more services](https://appwrite.io/docs). +Using Appwrite, you can easily integrate your app with user authentication and multiple sign-in methods, a database for storing and querying users and team data, storage and file management, image manipulation, Cloud Functions, messaging, and [more services](https://appwrite.io/docs).


From df017bccb29bf09e9f645bd5004547ee51da307f Mon Sep 17 00:00:00 2001 From: Steven Nguyen <1477010+stnguyen90@users.noreply.github.com> Date: Fri, 31 Jan 2025 22:00:32 +0000 Subject: [PATCH 009/143] chore: bump appwrite version to 1.6.1 --- README-CN.md | 6 +++--- README.md | 8 ++++---- app/init.php | 2 +- src/Appwrite/Migration/Migration.php | 1 - 4 files changed, 8 insertions(+), 9 deletions(-) diff --git a/README-CN.md b/README-CN.md index e43e9ac0d0..92a9bf9806 100644 --- a/README-CN.md +++ b/README-CN.md @@ -67,7 +67,7 @@ docker run -it --rm \ --volume /var/run/docker.sock:/var/run/docker.sock \ --volume "$(pwd)"/appwrite:/usr/src/code/appwrite:rw \ --entrypoint="install" \ - appwrite/appwrite:1.6.1-RC1 + appwrite/appwrite:1.6.1 ``` ### Windows @@ -79,7 +79,7 @@ docker run -it --rm ^ --volume //var/run/docker.sock:/var/run/docker.sock ^ --volume "%cd%"/appwrite:/usr/src/code/appwrite:rw ^ --entrypoint="install" ^ - appwrite/appwrite:1.6.1-RC1 + appwrite/appwrite:1.6.1 ``` #### PowerShell @@ -89,7 +89,7 @@ docker run -it --rm ` --volume /var/run/docker.sock:/var/run/docker.sock ` --volume ${pwd}/appwrite:/usr/src/code/appwrite:rw ` --entrypoint="install" ` - appwrite/appwrite:1.6.1-RC1 + appwrite/appwrite:1.6.1 ``` 运行后,可以在浏览器上访问 http://localhost 找到 Appwrite 控制台。在非 Linux 的本机主机上完成安装后,服务器可能需要几分钟才能启动。 diff --git a/README.md b/README.md index 1d121e5407..a433da3c84 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ Table of Contents: - [Upgrade from an Older Version](#upgrade-from-an-older-version) - [One-Click Setups](#one-click-setups) - [Getting Started](#getting-started) - - [Services](#services) + - [Products](#products) - [SDKs](#sdks) - [Client](#client) - [Server](#server) @@ -75,7 +75,7 @@ docker run -it --rm \ --volume /var/run/docker.sock:/var/run/docker.sock \ --volume "$(pwd)"/appwrite:/usr/src/code/appwrite:rw \ --entrypoint="install" \ - appwrite/appwrite:1.6.1-RC1 + appwrite/appwrite:1.6.1 ``` ### Windows @@ -87,7 +87,7 @@ docker run -it --rm ^ --volume //var/run/docker.sock:/var/run/docker.sock ^ --volume "%cd%"/appwrite:/usr/src/code/appwrite:rw ^ --entrypoint="install" ^ - appwrite/appwrite:1.6.1-RC1 + appwrite/appwrite:1.6.1 ``` #### PowerShell @@ -97,7 +97,7 @@ docker run -it --rm ` --volume /var/run/docker.sock:/var/run/docker.sock ` --volume ${pwd}/appwrite:/usr/src/code/appwrite:rw ` --entrypoint="install" ` - appwrite/appwrite:1.6.1-RC1 + appwrite/appwrite:1.6.1 ``` Once the Docker installation is complete, go to http://localhost to access the Appwrite console from your browser. Please note that on non-Linux native hosts, the server might take a few minutes to start after completing the installation. diff --git a/app/init.php b/app/init.php index 3cccabe78d..30ece74553 100644 --- a/app/init.php +++ b/app/init.php @@ -123,7 +123,7 @@ const APP_USER_ACCESS = 24 * 60 * 60; // 24 hours const APP_PROJECT_ACCESS = 24 * 60 * 60; // 24 hours const APP_CACHE_UPDATE = 24 * 60 * 60; // 24 hours const APP_CACHE_BUSTER = 4318; -const APP_VERSION_STABLE = '1.6.1-RC1'; +const APP_VERSION_STABLE = '1.6.1'; const APP_DATABASE_ATTRIBUTE_EMAIL = 'email'; const APP_DATABASE_ATTRIBUTE_ENUM = 'enum'; const APP_DATABASE_ATTRIBUTE_IP = 'ip'; diff --git a/src/Appwrite/Migration/Migration.php b/src/Appwrite/Migration/Migration.php index 71b5706ed7..19f69b1a4f 100644 --- a/src/Appwrite/Migration/Migration.php +++ b/src/Appwrite/Migration/Migration.php @@ -92,7 +92,6 @@ abstract class Migration '1.5.11' => 'V20', '1.6.0' => 'V21', '1.6.1' => 'V21', - '1.6.1-RC1' => 'V21', ]; /** From 0547dbc4543072f847c8a52a15ee688848e71031 Mon Sep 17 00:00:00 2001 From: Steven Nguyen <1477010+stnguyen90@users.noreply.github.com> Date: Mon, 3 Feb 2025 22:34:08 +0000 Subject: [PATCH 010/143] fix: use github var instead of secret for DOCKERHUB_USERNAME DOCKERHUB_USERNAME was moved to be a variable since it doesn't need to be secret. --- .github/workflows/publish.yml | 2 +- .github/workflows/release.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index f914e662d3..0ed82dd853 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -26,7 +26,7 @@ jobs: - name: Login to Docker Hub uses: docker/login-action@v2 with: - username: ${{ secrets.DOCKERHUB_USERNAME }} + username: ${{ vars.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Extract metadata (tags, labels) for Docker diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4475a49809..712d30dac0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -28,7 +28,7 @@ jobs: - name: Login to Docker Hub uses: docker/login-action@v2 with: - username: ${{ secrets.DOCKERHUB_USERNAME }} + username: ${{ vars.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Extract metadata (tags, labels) for Docker From 375f6432142f462fe8a052c1ea7138dea4755fed Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Tue, 11 Feb 2025 03:29:32 +0000 Subject: [PATCH 011/143] refactor: triggering realtime events with queueForRealtime --- app/controllers/api/functions.php | 52 ++++++--------- app/worker.php | 5 ++ src/Appwrite/Event/Event.php | 23 +++++++ src/Appwrite/Event/Realtime.php | 2 +- src/Appwrite/Platform/Workers/Databases.php | 74 +++++++++------------ 5 files changed, 80 insertions(+), 76 deletions(-) diff --git a/app/controllers/api/functions.php b/app/controllers/api/functions.php index 14255ef7a4..0f9f5211f4 100644 --- a/app/controllers/api/functions.php +++ b/app/controllers/api/functions.php @@ -6,13 +6,13 @@ use Appwrite\Event\Build; use Appwrite\Event\Delete; use Appwrite\Event\Event; use Appwrite\Event\Func; +use Appwrite\Event\Realtime; use Appwrite\Event\StatsUsage; use Appwrite\Event\Validator\FunctionEvent; use Appwrite\Extend\Exception; use Appwrite\Extend\Exception as AppwriteException; use Appwrite\Functions\Validator\Headers; use Appwrite\Functions\Validator\RuntimeSpecification; -use Appwrite\Messaging\Adapter\Realtime; use Appwrite\Platform\Tasks\ScheduleExecutions; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; @@ -194,9 +194,10 @@ App::post('/v1/functions') ->inject('user') ->inject('queueForEvents') ->inject('queueForBuilds') + ->inject('queueForRealtime') ->inject('dbForPlatform') ->inject('gitHub') - ->action(function (string $functionId, string $name, string $runtime, array $execute, array $events, string $schedule, int $timeout, bool $enabled, bool $logging, string $entrypoint, string $commands, array $scopes, string $installationId, string $providerRepositoryId, string $providerBranch, bool $providerSilentMode, string $providerRootDirectory, string $templateRepository, string $templateOwner, string $templateRootDirectory, string $templateVersion, string $specification, Request $request, Response $response, Database $dbForProject, callable $timelimit, Document $project, Document $user, Event $queueForEvents, Build $queueForBuilds, Database $dbForPlatform, GitHub $github) use ($redeployVcs) { + ->action(function (string $functionId, string $name, string $runtime, array $execute, array $events, string $schedule, int $timeout, bool $enabled, bool $logging, string $entrypoint, string $commands, array $scopes, string $installationId, string $providerRepositoryId, string $providerBranch, bool $providerSilentMode, string $providerRootDirectory, string $templateRepository, string $templateOwner, string $templateRootDirectory, string $templateVersion, string $specification, Request $request, Response $response, Database $dbForProject, callable $timelimit, Document $project, Document $user, Event $queueForEvents, Build $queueForBuilds, Realtime $queueForRealtime, Database $dbForPlatform, GitHub $github) use ($redeployVcs) { $functionId = ($functionId == 'unique()') ? ID::unique() : $functionId; // Temporary abuse check @@ -386,18 +387,18 @@ App::post('/v1/functions') ])) ); - /** Trigger Webhook */ $ruleModel = new Rule(); $ruleCreate = $queueForEvents - ->setClass(Event::WEBHOOK_CLASS_NAME) - ->setQueue(Event::WEBHOOK_QUEUE_NAME); + ->setProject($project) + ->setEvent('rules.[ruleId].create') + ->setParam('ruleId', $rule->getId()) + ->setPayload($rule->getArrayCopy(array_keys($ruleModel->getRules()))); + /** Trigger Webhook */ $ruleCreate - ->setProject($project) - ->setEvent('rules.[ruleId].create') - ->setParam('ruleId', $rule->getId()) - ->setPayload($rule->getArrayCopy(array_keys($ruleModel->getRules()))) + ->setClass(Event::WEBHOOK_CLASS_NAME) + ->setQueue(Event::WEBHOOK_QUEUE_NAME) ->trigger(); /** Trigger Functions */ @@ -406,31 +407,16 @@ App::post('/v1/functions') ->setQueue(Event::FUNCTIONS_QUEUE_NAME) ->trigger(); - /** Trigger realtime event */ - $allEvents = Event::generateEvents('rules.[ruleId].create', [ - 'ruleId' => $rule->getId(), - ]); + /** Trigger Realtime Events */ + $queueForRealtime + ->from($ruleCreate) + ->setProjectId('console') + ->trigger(); - $target = Realtime::fromPayload( - // Pass first, most verbose event pattern - event: $allEvents[0], - payload: $rule, - project: $project - ); - Realtime::send( - projectId: 'console', - payload: $rule->getArrayCopy(), - events: $allEvents, - channels: $target['channels'], - roles: $target['roles'] - ); - Realtime::send( - projectId: $project->getId(), - payload: $rule->getArrayCopy(), - events: $allEvents, - channels: $target['channels'], - roles: $target['roles'] - ); + $queueForRealtime + ->from($ruleCreate) + ->setProjectId($project->getId()) + ->trigger(); } $queueForEvents->setParam('functionId', $function->getId()); diff --git a/app/worker.php b/app/worker.php index 605474e9f1..ad6bf475f9 100644 --- a/app/worker.php +++ b/app/worker.php @@ -13,6 +13,7 @@ use Appwrite\Event\Func; use Appwrite\Event\Mail; use Appwrite\Event\Messaging; use Appwrite\Event\Migration; +use Appwrite\Event\Realtime; use Appwrite\Event\StatsUsage; use Appwrite\Event\StatsUsageDump; /** remove */ @@ -317,6 +318,10 @@ Server::setResource('queueForFunctions', function (Publisher $publisher) { return new Func($publisher); }, ['publisher']); +Server::setResource('queueForRealtime', function () { + return new Realtime(); +}, []); + Server::setResource('queueForCertificates', function (Publisher $publisher) { return new Certificate($publisher); }, ['publisher']); diff --git a/src/Appwrite/Event/Event.php b/src/Appwrite/Event/Event.php index 0edffdf4dc..8085a836a8 100644 --- a/src/Appwrite/Event/Event.php +++ b/src/Appwrite/Event/Event.php @@ -66,6 +66,7 @@ class Event protected ?Document $project = null; protected ?Document $user = null; protected ?string $userId = null; + protected ?string $projectId = null; protected bool $paused = false; /** @@ -151,6 +152,18 @@ class Event return $this; } + /** + * Set projectId for this event. + * + * @param string $projectId + * @return self + */ + public function setProjectId(string $projectId): self + { + $this->projectId = $projectId; + return $this; + } + /** * Get project for this event. * @@ -161,6 +174,16 @@ class Event return $this->project; } + /** + * Get projectId for this event. + * + * @return ?string + */ + public function getProjectId(): ?string + { + return $this->projectId; + } + /** * Set user for this event. * diff --git a/src/Appwrite/Event/Realtime.php b/src/Appwrite/Event/Realtime.php index f4f00b59d4..8c302bbabf 100644 --- a/src/Appwrite/Event/Realtime.php +++ b/src/Appwrite/Event/Realtime.php @@ -54,7 +54,7 @@ class Realtime extends Event ); RealtimeAdapter::send( - projectId: $target['projectId'] ?? $this->getProject()->getId(), + projectId: $this->getProjectId() ?? $target['projectId'] ?? $this->getProject()->getId(), payload: $this->getRealtimePayload(), events: $allEvents, channels: $target['channels'], diff --git a/src/Appwrite/Platform/Workers/Databases.php b/src/Appwrite/Platform/Workers/Databases.php index 441b09b4cc..50a3fa52f3 100644 --- a/src/Appwrite/Platform/Workers/Databases.php +++ b/src/Appwrite/Platform/Workers/Databases.php @@ -3,7 +3,7 @@ namespace Appwrite\Platform\Workers; use Appwrite\Event\Event; -use Appwrite\Messaging\Adapter\Realtime; +use Appwrite\Event\Realtime; use Exception; use Utopia\CLI\Console; use Utopia\Database\Database; @@ -38,7 +38,8 @@ class Databases extends Action ->inject('dbForPlatform') ->inject('dbForProject') ->inject('log') - ->callback(fn (Message $message, Document $project, Database $dbForPlatform, Database $dbForProject, Log $log) => $this->action($message, $project, $dbForPlatform, $dbForProject, $log)); + ->inject('queueForRealtime') + ->callback(fn (Message $message, Document $project, Database $dbForPlatform, Database $dbForProject, Log $log, Realtime $queueForRealtime) => $this->action($message, $project, $dbForPlatform, $dbForProject, $log, $queueForRealtime)); } /** @@ -47,10 +48,11 @@ class Databases extends Action * @param Database $dbForPlatform * @param Database $dbForProject * @param Log $log + * @param Realtime $queueForRealtime * @return void * @throws \Exception */ - public function action(Message $message, Document $project, Database $dbForPlatform, Database $dbForProject, Log $log): void + public function action(Message $message, Document $project, Database $dbForPlatform, Database $dbForProject, Log $log, Realtime $queueForRealtime): void { $payload = $message->getPayload() ?? []; @@ -75,10 +77,10 @@ class Databases extends Action match (\strval($type)) { DATABASE_TYPE_DELETE_DATABASE => $this->deleteDatabase($database, $project, $dbForProject), DATABASE_TYPE_DELETE_COLLECTION => $this->deleteCollection($database, $collection, $project, $dbForProject), - DATABASE_TYPE_CREATE_ATTRIBUTE => $this->createAttribute($database, $collection, $document, $project, $dbForPlatform, $dbForProject), - DATABASE_TYPE_DELETE_ATTRIBUTE => $this->deleteAttribute($database, $collection, $document, $project, $dbForPlatform, $dbForProject), - DATABASE_TYPE_CREATE_INDEX => $this->createIndex($database, $collection, $document, $project, $dbForPlatform, $dbForProject), - DATABASE_TYPE_DELETE_INDEX => $this->deleteIndex($database, $collection, $document, $project, $dbForPlatform, $dbForProject), + DATABASE_TYPE_CREATE_ATTRIBUTE => $this->createAttribute($database, $collection, $document, $project, $dbForPlatform, $dbForProject, $queueForRealtime), + DATABASE_TYPE_DELETE_ATTRIBUTE => $this->deleteAttribute($database, $collection, $document, $project, $dbForPlatform, $dbForProject, $queueForRealtime), + DATABASE_TYPE_CREATE_INDEX => $this->createIndex($database, $collection, $document, $project, $dbForPlatform, $dbForProject, $queueForRealtime), + DATABASE_TYPE_DELETE_INDEX => $this->deleteIndex($database, $collection, $document, $project, $dbForPlatform, $dbForProject, $queueForRealtime), default => throw new \Exception('No database operation for type: ' . \strval($type)), }; } @@ -90,13 +92,14 @@ class Databases extends Action * @param Document $project * @param Database $dbForPlatform * @param Database $dbForProject + * @param Realtime $queueForRealtime * @return void * @throws Authorization * @throws Conflict * @throws \Exception * @throws \Throwable */ - private function createAttribute(Document $database, Document $collection, Document $attribute, Document $project, Database $dbForPlatform, Database $dbForProject): void + private function createAttribute(Document $database, Document $collection, Document $attribute, Document $project, Database $dbForPlatform, Database $dbForProject, Realtime $queueForRealtime): void { if ($collection->isEmpty()) { throw new Exception('Missing collection'); @@ -106,12 +109,6 @@ class Databases extends Action } $projectId = $project->getId(); - - $events = Event::generateEvents('databases.[databaseId].collections.[collectionId].attributes.[attributeId].update', [ - 'databaseId' => $database->getId(), - 'collectionId' => $collection->getId(), - 'attributeId' => $attribute->getId() - ]); /** * TODO @christyjacob4 verify if this is still the case * Fetch attribute from the database, since with Resque float values are loosing informations. @@ -200,7 +197,7 @@ class Databases extends Action throw $e; } finally { - $this->trigger($database, $collection, $attribute, $project, $projectId, $events); + $this->trigger($database, $collection, $attribute, $project, $queueForRealtime); if (! $relatedCollection->isEmpty()) { $dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $relatedCollection->getId()); @@ -217,13 +214,14 @@ class Databases extends Action * @param Document $project * @param Database $dbForPlatform * @param Database $dbForProject + * @param Realtime $queueForRealtime * @return void * @throws Authorization * @throws Conflict * @throws \Exception * @throws \Throwable **/ - private function deleteAttribute(Document $database, Document $collection, Document $attribute, Document $project, Database $dbForPlatform, Database $dbForProject): void + private function deleteAttribute(Document $database, Document $collection, Document $attribute, Document $project, Database $dbForPlatform, Database $dbForProject, Realtime $queueForRealtime): void { if ($collection->isEmpty()) { throw new Exception('Missing collection'); @@ -312,7 +310,7 @@ class Databases extends Action throw $e; } finally { - $this->trigger($database, $collection, $attribute, $project, $projectId, $events); + $this->trigger($database, $collection, $attribute, $project, $queueForRealtime); } // The underlying database removes/rebuilds indexes when attribute is removed @@ -358,7 +356,7 @@ class Databases extends Action } if ($exists) { // Delete the duplicate if created, else update in db - $this->deleteIndex($database, $collection, $index, $project, $dbForPlatform, $dbForProject); + $this->deleteIndex($database, $collection, $index, $project, $dbForPlatform, $dbForProject, $queueForRealtime); } else { $dbForProject->updateDocument('indexes', $index->getId(), $index); } @@ -381,6 +379,7 @@ class Databases extends Action * @param Document $project * @param Database $dbForPlatform * @param Database $dbForProject + * @param Realtime $queueForRealtime * @return void * @throws Authorization * @throws Conflict @@ -388,7 +387,7 @@ class Databases extends Action * @throws DatabaseException * @throws \Throwable */ - private function createIndex(Document $database, Document $collection, Document $index, Document $project, Database $dbForPlatform, Database $dbForProject): void + private function createIndex(Document $database, Document $collection, Document $index, Document $project, Database $dbForPlatform, Database $dbForProject, Realtime $queueForRealtime): void { if ($collection->isEmpty()) { throw new Exception('Missing collection'); @@ -430,7 +429,7 @@ class Databases extends Action throw $e; } finally { - $this->trigger($database, $collection, $index, $project, $projectId, $events); + $this->trigger($database, $collection, $index, $project, $queueForRealtime); $dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $collectionId); } } @@ -442,6 +441,7 @@ class Databases extends Action * @param Document $project * @param Database $dbForPlatform * @param Database $dbForProject + * @param Realtime $queueForRealtime * @return void * @throws Authorization * @throws Conflict @@ -449,7 +449,7 @@ class Databases extends Action * @throws DatabaseException * @throws \Throwable */ - private function deleteIndex(Document $database, Document $collection, Document $index, Document $project, Database $dbForPlatform, Database $dbForProject): void + private function deleteIndex(Document $database, Document $collection, Document $index, Document $project, Database $dbForPlatform, Database $dbForProject, Realtime $queueForRealtime): void { if ($collection->isEmpty()) { throw new Exception('Missing collection'); @@ -490,7 +490,7 @@ class Databases extends Action throw $e; } finally { - $this->trigger($database, $collection, $index, $project, $projectId, $events); + $this->trigger($database, $collection, $index, $project, $queueForRealtime); $dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $collection->getId()); } } @@ -617,26 +617,16 @@ class Databases extends Action Document $collection, Document $attribute, Document $project, - string $projectId, - array $events + Realtime $queueForRealtime ): void { - $target = Realtime::fromPayload( - // Pass first, most verbose event pattern - event: $events[0], - payload: $attribute, - project: $project, - ); - Realtime::send( - projectId: 'console', - payload: $attribute->getArrayCopy(), - events: $events, - channels: $target['channels'], - roles: $target['roles'], - options: [ - 'projectId' => $projectId, - 'databaseId' => $database->getId(), - 'collectionId' => $collection->getId() - ] - ); + $queueForRealtime + ->setProject($project) + ->setProjectId('console') + ->setEvent('databases.[databaseId].collections.[collectionId].attributes.[attributeId].update') + ->setParam('databaseId', $database->getId()) + ->setParam('collectionId', $collection->getId()) + ->setParam('attributeId', $attribute->getId()) + ->setPayload($attribute->getArrayCopy()) + ->trigger(); } } From 2c4c42de054ad6388f53562c29621b913a58864d Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Tue, 11 Feb 2025 03:53:18 +0000 Subject: [PATCH 012/143] docs: fix database worker --- src/Appwrite/Platform/Workers/Databases.php | 40 ++++++++------------- 1 file changed, 14 insertions(+), 26 deletions(-) diff --git a/src/Appwrite/Platform/Workers/Databases.php b/src/Appwrite/Platform/Workers/Databases.php index 50a3fa52f3..6db88a618f 100644 --- a/src/Appwrite/Platform/Workers/Databases.php +++ b/src/Appwrite/Platform/Workers/Databases.php @@ -2,7 +2,6 @@ namespace Appwrite\Platform\Workers; -use Appwrite\Event\Event; use Appwrite\Event\Realtime; use Exception; use Utopia\CLI\Console; @@ -57,7 +56,7 @@ class Databases extends Action $payload = $message->getPayload() ?? []; if (empty($payload)) { - throw new \Exception('Missing payload'); + throw new Exception('Missing payload'); } $type = $payload['type']; @@ -81,7 +80,7 @@ class Databases extends Action DATABASE_TYPE_DELETE_ATTRIBUTE => $this->deleteAttribute($database, $collection, $document, $project, $dbForPlatform, $dbForProject, $queueForRealtime), DATABASE_TYPE_CREATE_INDEX => $this->createIndex($database, $collection, $document, $project, $dbForPlatform, $dbForProject, $queueForRealtime), DATABASE_TYPE_DELETE_INDEX => $this->deleteIndex($database, $collection, $document, $project, $dbForPlatform, $dbForProject, $queueForRealtime), - default => throw new \Exception('No database operation for type: ' . \strval($type)), + default => throw new Exception('No database operation for type: ' . \strval($type)), }; } @@ -166,7 +165,7 @@ class Databases extends Action break; default: if (!$dbForProject->createAttribute('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $key, $type, $size, $required, $default, $signed, $array, $format, $formatOptions, $filters)) { - throw new \Exception('Failed to create Attribute'); + throw new Exception('Failed to create Attribute'); } } @@ -231,15 +230,8 @@ class Databases extends Action } $projectId = $project->getId(); - - $events = Event::generateEvents('databases.[databaseId].collections.[collectionId].attributes.[attributeId].delete', [ - 'databaseId' => $database->getId(), - 'collectionId' => $collection->getId(), - 'attributeId' => $attribute->getId() - ]); $collectionId = $collection->getId(); $key = $attribute->getAttribute('key', ''); - $status = $attribute->getAttribute('status', ''); $type = $attribute->getAttribute('type', ''); $project = $dbForPlatform->getDocument('projects', $projectId); $options = $attribute->getAttribute('options', []); @@ -397,12 +389,6 @@ class Databases extends Action } $projectId = $project->getId(); - - $events = Event::generateEvents('databases.[databaseId].collections.[collectionId].indexes.[indexId].update', [ - 'databaseId' => $database->getId(), - 'collectionId' => $collection->getId(), - 'indexId' => $index->getId() - ]); $collectionId = $collection->getId(); $key = $index->getAttribute('key', ''); $type = $index->getAttribute('type', ''); @@ -459,12 +445,6 @@ class Databases extends Action } $projectId = $project->getId(); - - $events = Event::generateEvents('databases.[databaseId].collections.[collectionId].indexes.[indexId].delete', [ - 'databaseId' => $database->getId(), - 'collectionId' => $collection->getId(), - 'indexId' => $index->getId() - ]); $key = $index->getAttribute('key'); $status = $index->getAttribute('status', ''); $project = $dbForPlatform->getDocument('projects', $projectId); @@ -568,14 +548,14 @@ class Databases extends Action /** - * @param string $collection collectionID + * @param string $collectionId * @param array $queries * @param Database $database * @param callable|null $callback * @return void * @throws Exception */ - protected function deleteByGroup(string $collection, array $queries, Database $database, callable $callback = null): void + protected function deleteByGroup(string $collectionId, array $queries, Database $database, callable $callback = null): void { $count = 0; $chunk = 0; @@ -587,7 +567,7 @@ class Databases extends Action while ($sum === $limit) { $chunk++; - $results = $database->find($collection, \array_merge([Query::limit($limit)], $queries)); + $results = $database->find($collectionId, \array_merge([Query::limit($limit)], $queries)); $sum = count($results); @@ -612,6 +592,14 @@ class Databases extends Action Console::info("Deleted {$count} document by group in " . ($executionEnd - $executionStart) . " seconds"); } + /** + * @param Document $database + * @param Document $collection + * @param Document $attribute + * @param Document $project + * @param Realtime $queueForRealtime + * @return void + */ protected function trigger( Document $database, Document $collection, From 47fbb777ed2893e8230316d0386febed76e407a4 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Tue, 11 Feb 2025 04:03:02 +0000 Subject: [PATCH 013/143] chore: refactor migrations realtime queue --- src/Appwrite/Platform/Workers/Migrations.php | 66 +++++++++----------- 1 file changed, 28 insertions(+), 38 deletions(-) diff --git a/src/Appwrite/Platform/Workers/Migrations.php b/src/Appwrite/Platform/Workers/Migrations.php index cd567f6fa3..50d6002d28 100644 --- a/src/Appwrite/Platform/Workers/Migrations.php +++ b/src/Appwrite/Platform/Workers/Migrations.php @@ -3,8 +3,7 @@ namespace Appwrite\Platform\Workers; use Ahc\Jwt\JWT; -use Appwrite\Event\Event; -use Appwrite\Messaging\Adapter\Realtime; +use Appwrite\Event\Realtime; use Exception; use Utopia\CLI\Console; use Utopia\Config\Config; @@ -54,13 +53,14 @@ class Migrations extends Action ->inject('dbForProject') ->inject('dbForPlatform') ->inject('logError') - ->callback(fn (Message $message, Document $project, Database $dbForProject, Database $dbForPlatform, callable $logError) => $this->action($message, $project, $dbForProject, $dbForPlatform, $logError)); + ->inject('queueForRealtime') + ->callback(fn (Message $message, Document $project, Database $dbForProject, Database $dbForPlatform, callable $logError, Realtime $queueForRealtime) => $this->action($message, $project, $dbForProject, $dbForPlatform, $logError, $queueForRealtime)); } /** * @throws Exception */ - public function action(Message $message, Document $project, Database $dbForProject, Database $dbForPlatform, callable $logError): void + public function action(Message $message, Document $project, Database $dbForProject, Database $dbForPlatform, callable $logError, Realtime $queueForRealtime): void { $payload = $message->getPayload() ?? []; @@ -87,7 +87,7 @@ class Migrations extends Action return; } - $this->processMigration($migration); + $this->processMigration($migration, $queueForRealtime); } /** @@ -155,34 +155,24 @@ class Migrations extends Action * @throws \Utopia\Database\Exception * @throws Exception */ - protected function updateMigrationDocument(Document $migration, Document $project): Document + protected function updateMigrationDocument(Document $migration, Document $project, Realtime $queueForRealtime): Document { - /** Trigger Realtime */ - $allEvents = Event::generateEvents('migrations.[migrationId].update', [ - 'migrationId' => $migration->getId(), - ]); + /** Trigger Realtime Events */ + $queueForRealtime + ->setProject($project) + ->setProjectId('console') + ->setEvent('migrations.[migrationId].update') + ->setParam('migrationId', $migration->getId()) + ->setPayload($migration->getArrayCopy()) + ->trigger(); - $target = Realtime::fromPayload( - event: $allEvents[0], - payload: $migration, - project: $project - ); - - Realtime::send( - projectId: 'console', - payload: $migration->getArrayCopy(), - events: $allEvents, - channels: $target['channels'], - roles: $target['roles'], - ); - - Realtime::send( - projectId: $project->getId(), - payload: $migration->getArrayCopy(), - events: $allEvents, - channels: $target['channels'], - roles: $target['roles'], - ); + $queueForRealtime + ->setProject($project) + ->setProjectId($project->getId()) + ->setEvent('migrations.[migrationId].update') + ->setParam('migrationId', $migration->getId()) + ->setPayload($migration->getArrayCopy()) + ->trigger(); return $this->dbForProject->updateDocument('migrations', $migration->getId(), $migration); } @@ -241,7 +231,7 @@ class Migrations extends Action * @throws \Utopia\Database\Exception * @throws Exception */ - protected function processMigration(Document $migration): void + protected function processMigration(Document $migration, Realtime $queueForRealtime): void { $project = $this->project; $projectDocument = $this->dbForPlatform->getDocument('projects', $project->getId()); @@ -265,7 +255,7 @@ class Migrations extends Action $migration->setAttribute('stage', 'processing'); $migration->setAttribute('status', 'processing'); - $this->updateMigrationDocument($migration, $projectDocument); + $this->updateMigrationDocument($migration, $projectDocument, $queueForRealtime); $source = $this->processSource($migration); $destination = $this->processDestination($migration, $tempAPIKey); @@ -279,14 +269,14 @@ class Migrations extends Action /** Start Transfer */ $migration->setAttribute('stage', 'migrating'); - $this->updateMigrationDocument($migration, $projectDocument); + $this->updateMigrationDocument($migration, $projectDocument, $queueForRealtime); $transfer->run( $migration->getAttribute('resources'), - function () use ($migration, $transfer, $projectDocument) { + function () use ($migration, $transfer, $projectDocument, $queueForRealtime) { $migration->setAttribute('resourceData', json_encode($transfer->getCache())); $migration->setAttribute('statusCounters', json_encode($transfer->getStatusCounters())); - $this->updateMigrationDocument($migration, $projectDocument); + $this->updateMigrationDocument($migration, $projectDocument, $queueForRealtime); }, $migration->getAttribute('resourceId'), $migration->getAttribute('resourceType') @@ -323,7 +313,7 @@ class Migrations extends Action } $migration->setAttribute('errors', $errorMessages); - $this->updateMigrationDocument($migration, $projectDocument); + $this->updateMigrationDocument($migration, $projectDocument, $queueForRealtime); return; } @@ -364,7 +354,7 @@ class Migrations extends Action $migration->setAttribute('errors', $errorMessages); } } finally { - $this->updateMigrationDocument($migration, $projectDocument); + $this->updateMigrationDocument($migration, $projectDocument, $queueForRealtime); if ($migration->getAttribute('status', '') === 'failed') { Console::error('Migration('.$migration->getInternalId().':'.$migration->getId().') failed, Project('.$this->project->getInternalId().':'.$this->project->getId().')'); From bcdae7f4e37ff7d348d54f39f2991676a89e77c4 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Tue, 11 Feb 2025 04:21:36 +0000 Subject: [PATCH 014/143] chore: refactor certificate realtime, fix database --- .../Platform/Workers/Certificates.php | 76 ++++++++----------- src/Appwrite/Platform/Workers/Databases.php | 23 +++--- 2 files changed, 46 insertions(+), 53 deletions(-) diff --git a/src/Appwrite/Platform/Workers/Certificates.php b/src/Appwrite/Platform/Workers/Certificates.php index 7e220b2734..c7f345f77a 100644 --- a/src/Appwrite/Platform/Workers/Certificates.php +++ b/src/Appwrite/Platform/Workers/Certificates.php @@ -6,7 +6,7 @@ use Appwrite\Certificates\Adapter as CertificatesAdapter; use Appwrite\Event\Event; use Appwrite\Event\Func; use Appwrite\Event\Mail; -use Appwrite\Messaging\Adapter\Realtime; +use Appwrite\Event\Realtime; use Appwrite\Network\Validator\CNAME; use Appwrite\Template\Template; use Appwrite\Utopia\Response\Model\Rule; @@ -47,11 +47,12 @@ class Certificates extends Action ->inject('queueForMails') ->inject('queueForEvents') ->inject('queueForFunctions') + ->inject('queueForRealtime') ->inject('log') ->inject('certificates') ->callback( - fn (Message $message, Database $dbForPlatform, Mail $queueForMails, Event $queueForEvents, Func $queueForFunctions, Log $log, CertificatesAdapter $certificates) => - $this->action($message, $dbForPlatform, $queueForMails, $queueForEvents, $queueForFunctions, $log, $certificates) + fn (Message $message, Database $dbForPlatform, Mail $queueForMails, Event $queueForEvents, Func $queueForFunctions, Realtime $queueForRealtime, Log $log, CertificatesAdapter $certificates) => + $this->action($message, $dbForPlatform, $queueForMails, $queueForEvents, $queueForFunctions, $queueForRealtime, $log, $certificates) ); } @@ -61,13 +62,14 @@ class Certificates extends Action * @param Mail $queueForMails * @param Event $queueForEvents * @param Func $queueForFunctions + * @param Realtime $queueForRealtime * @param Log $log * @param CertificatesAdapter $certificates * @return void * @throws Throwable * @throws \Utopia\Database\Exception */ - public function action(Message $message, Database $dbForPlatform, Mail $queueForMails, Event $queueForEvents, Func $queueForFunctions, Log $log, CertificatesAdapter $certificates): void + public function action(Message $message, Database $dbForPlatform, Mail $queueForMails, Event $queueForEvents, Func $queueForFunctions, Realtime $queueForRealtime, Log $log, CertificatesAdapter $certificates): void { $payload = $message->getPayload() ?? []; @@ -81,7 +83,7 @@ class Certificates extends Action $log->addTag('domain', $domain->get()); - $this->execute($domain, $dbForPlatform, $queueForMails, $queueForEvents, $queueForFunctions, $log, $certificates, $skipRenewCheck); + $this->execute($domain, $dbForPlatform, $queueForMails, $queueForEvents, $queueForFunctions, $queueForRealtime, $log, $certificates, $skipRenewCheck); } /** @@ -90,13 +92,14 @@ class Certificates extends Action * @param Mail $queueForMails * @param Event $queueForEvents * @param Func $queueForFunctions + * @param Realtime $queueForRealtime * @param CertificatesAdapter $certificates * @param bool $skipRenewCheck * @return void * @throws Throwable * @throws \Utopia\Database\Exception */ - private function execute(Domain $domain, Database $dbForPlatform, Mail $queueForMails, Event $queueForEvents, Func $queueForFunctions, Log $log, CertificatesAdapter $certificates, bool $skipRenewCheck = false): void + private function execute(Domain $domain, Database $dbForPlatform, Mail $queueForMails, Event $queueForEvents, Func $queueForFunctions, Realtime $queueForRealtime, Log $log, CertificatesAdapter $certificates, bool $skipRenewCheck = false): void { /** * 1. Read arguments and validate domain @@ -186,7 +189,7 @@ class Certificates extends Action $certificate->setAttribute('updated', DateTime::now()); // Save all changes we made to certificate document into database - $this->saveCertificateDocument($domain->get(), $certificate, $success, $dbForPlatform, $queueForEvents, $queueForFunctions); + $this->saveCertificateDocument($domain->get(), $certificate, $success, $dbForPlatform, $queueForEvents, $queueForFunctions, $queueForRealtime); } } @@ -199,13 +202,14 @@ class Certificates extends Action * @param Database $dbForPlatform Database connection for console * @param Event $queueForEvents * @param Func $queueForFunctions + * @param Realtime $queueForRealtime * @return void * @throws \Utopia\Database\Exception * @throws Authorization * @throws Conflict * @throws Structure */ - private function saveCertificateDocument(string $domain, Document $certificate, bool $success, Database $dbForPlatform, Event $queueForEvents, Func $queueForFunctions): void + private function saveCertificateDocument(string $domain, Document $certificate, bool $success, Database $dbForPlatform, Event $queueForEvents, Func $queueForFunctions, Realtime $queueForRealtime): void { // Check if update or insert required $certificateDocument = $dbForPlatform->findOne('certificates', [Query::equal('domain', [$domain])]); @@ -219,7 +223,7 @@ class Certificates extends Action } $certificateId = $certificate->getId(); - $this->updateDomainDocuments($certificateId, $domain, $success, $dbForPlatform, $queueForEvents, $queueForFunctions); + $this->updateDomainDocuments($certificateId, $domain, $success, $dbForPlatform, $queueForEvents, $queueForFunctions, $queueForRealtime); } /** @@ -338,7 +342,7 @@ class Certificates extends Action * * @return void */ - private function updateDomainDocuments(string $certificateId, string $domain, bool $success, Database $dbForPlatform, Event $queueForEvents, Func $queueForFunctions): void + private function updateDomainDocuments(string $certificateId, string $domain, bool $success, Database $dbForPlatform, Event $queueForEvents, Func $queueForFunctions, Realtime $queueForRealtime): void { // TODO: @christyjacob remove once we migrate the rules in 1.7.x if (System::getEnv('_APP_RULES_FORMAT') === 'md5') { @@ -367,50 +371,34 @@ class Certificates extends Action return; } - /** Trigger Webhook */ $ruleModel = new Rule(); + $queueForEvents + ->setProject($project) + ->setEvent('rules.[ruleId].update') + ->setParam('ruleId', $rule->getId()) + ->setPayload($rule->getArrayCopy(array_keys($ruleModel->getRules()))); + + /** Trigger Webhook */ $queueForEvents ->setQueue(Event::WEBHOOK_QUEUE_NAME) ->setClass(Event::WEBHOOK_CLASS_NAME) - ->setProject($project) - ->setEvent('rules.[ruleId].update') - ->setParam('ruleId', $rule->getId()) - ->setPayload($rule->getArrayCopy(array_keys($ruleModel->getRules()))) ->trigger(); - /** Trigger Functions */ $queueForFunctions - ->setProject($project) - ->setEvent('rules.[ruleId].update') - ->setParam('ruleId', $rule->getId()) - ->setPayload($rule->getArrayCopy(array_keys($ruleModel->getRules()))) + ->from($queueForEvents) ->trigger(); - /** Trigger realtime event */ - $allEvents = Event::generateEvents('rules.[ruleId].update', [ - 'ruleId' => $rule->getId(), - ]); - $target = Realtime::fromPayload( - // Pass first, most verbose event pattern - event: $allEvents[0], - payload: $rule, - project: $project - ); - Realtime::send( - projectId: 'console', - payload: $rule->getArrayCopy(), - events: $allEvents, - channels: $target['channels'], - roles: $target['roles'] - ); - Realtime::send( - projectId: $project->getId(), - payload: $rule->getArrayCopy(), - events: $allEvents, - channels: $target['channels'], - roles: $target['roles'] - ); + /** Trigger Realtime Events */ + $queueForRealtime + ->from($queueForEvents) + ->setProjectId('console') + ->trigger(); + + $queueForRealtime + ->from($queueForEvents) + ->setProjectId($project->getId()) + ->trigger(); } } } diff --git a/src/Appwrite/Platform/Workers/Databases.php b/src/Appwrite/Platform/Workers/Databases.php index 6db88a618f..b617da3e33 100644 --- a/src/Appwrite/Platform/Workers/Databases.php +++ b/src/Appwrite/Platform/Workers/Databases.php @@ -36,9 +36,9 @@ class Databases extends Action ->inject('project') ->inject('dbForPlatform') ->inject('dbForProject') - ->inject('log') ->inject('queueForRealtime') - ->callback(fn (Message $message, Document $project, Database $dbForPlatform, Database $dbForProject, Log $log, Realtime $queueForRealtime) => $this->action($message, $project, $dbForPlatform, $dbForProject, $log, $queueForRealtime)); + ->inject('log') + ->callback(fn (Message $message, Document $project, Database $dbForPlatform, Database $dbForProject, Realtime $queueForRealtime, Log $log) => $this->action($message, $project, $dbForPlatform, $dbForProject, $queueForRealtime, $log)); } /** @@ -46,12 +46,12 @@ class Databases extends Action * @param Document $project * @param Database $dbForPlatform * @param Database $dbForProject - * @param Log $log * @param Realtime $queueForRealtime + * @param Log $log * @return void * @throws \Exception */ - public function action(Message $message, Document $project, Database $dbForPlatform, Database $dbForProject, Log $log, Realtime $queueForRealtime): void + public function action(Message $message, Document $project, Database $dbForPlatform, Database $dbForProject, Realtime $queueForRealtime, Log $log): void { $payload = $message->getPayload() ?? []; @@ -108,6 +108,7 @@ class Databases extends Action } $projectId = $project->getId(); + $event = "databases.[databaseId].collections.[collectionId].attributes.[attributeId].update"; /** * TODO @christyjacob4 verify if this is still the case * Fetch attribute from the database, since with Resque float values are loosing informations. @@ -196,7 +197,7 @@ class Databases extends Action throw $e; } finally { - $this->trigger($database, $collection, $attribute, $project, $queueForRealtime); + $this->trigger($database, $collection, $attribute, $project, $event, $queueForRealtime); if (! $relatedCollection->isEmpty()) { $dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $relatedCollection->getId()); @@ -230,6 +231,7 @@ class Databases extends Action } $projectId = $project->getId(); + $event = 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].delete'; $collectionId = $collection->getId(); $key = $attribute->getAttribute('key', ''); $type = $attribute->getAttribute('type', ''); @@ -302,7 +304,7 @@ class Databases extends Action throw $e; } finally { - $this->trigger($database, $collection, $attribute, $project, $queueForRealtime); + $this->trigger($database, $collection, $attribute, $project, $event, $queueForRealtime); } // The underlying database removes/rebuilds indexes when attribute is removed @@ -389,6 +391,7 @@ class Databases extends Action } $projectId = $project->getId(); + $event = 'databases.[databaseId].collections.[collectionId].indexes.[indexId].update'; $collectionId = $collection->getId(); $key = $index->getAttribute('key', ''); $type = $index->getAttribute('type', ''); @@ -415,7 +418,7 @@ class Databases extends Action throw $e; } finally { - $this->trigger($database, $collection, $index, $project, $queueForRealtime); + $this->trigger($database, $collection, $index, $project, $event, $queueForRealtime); $dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $collectionId); } } @@ -445,6 +448,7 @@ class Databases extends Action } $projectId = $project->getId(); + $event = 'databases.[databaseId].collections.[collectionId].indexes.[indexId].delete'; $key = $index->getAttribute('key'); $status = $index->getAttribute('status', ''); $project = $dbForPlatform->getDocument('projects', $projectId); @@ -470,7 +474,7 @@ class Databases extends Action throw $e; } finally { - $this->trigger($database, $collection, $index, $project, $queueForRealtime); + $this->trigger($database, $collection, $index, $project, $event, $queueForRealtime); $dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $collection->getId()); } } @@ -605,12 +609,13 @@ class Databases extends Action Document $collection, Document $attribute, Document $project, + string $event, Realtime $queueForRealtime ): void { $queueForRealtime ->setProject($project) ->setProjectId('console') - ->setEvent('databases.[databaseId].collections.[collectionId].attributes.[attributeId].update') + ->setEvent($event) ->setParam('databaseId', $database->getId()) ->setParam('collectionId', $collection->getId()) ->setParam('attributeId', $attribute->getId()) From f666e18154ecf2b9b0e51dd3c64b2fe400cd0c80 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Tue, 11 Feb 2025 05:00:45 +0000 Subject: [PATCH 015/143] chore: fix databases worker realtime queueing --- src/Appwrite/Platform/Workers/Databases.php | 30 ++++++++++++++------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/src/Appwrite/Platform/Workers/Databases.php b/src/Appwrite/Platform/Workers/Databases.php index b617da3e33..b0c6a9df73 100644 --- a/src/Appwrite/Platform/Workers/Databases.php +++ b/src/Appwrite/Platform/Workers/Databases.php @@ -197,7 +197,7 @@ class Databases extends Action throw $e; } finally { - $this->trigger($database, $collection, $attribute, $project, $event, $queueForRealtime); + $this->trigger($database, $collection, $project, $attribute, null, $attribute, $event, $queueForRealtime); if (! $relatedCollection->isEmpty()) { $dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $relatedCollection->getId()); @@ -304,7 +304,7 @@ class Databases extends Action throw $e; } finally { - $this->trigger($database, $collection, $attribute, $project, $event, $queueForRealtime); + $this->trigger($database, $collection, $project, $attribute, null, $attribute, $event, $queueForRealtime); } // The underlying database removes/rebuilds indexes when attribute is removed @@ -418,7 +418,7 @@ class Databases extends Action throw $e; } finally { - $this->trigger($database, $collection, $index, $project, $event, $queueForRealtime); + $this->trigger($database, $collection, $project, null, $index, $index, $event, $queueForRealtime); $dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $collectionId); } } @@ -474,7 +474,7 @@ class Databases extends Action throw $e; } finally { - $this->trigger($database, $collection, $index, $project, $event, $queueForRealtime); + $this->trigger($database, $collection, $project, null, $index, $index, $event, $queueForRealtime); $dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $collection->getId()); } } @@ -599,16 +599,20 @@ class Databases extends Action /** * @param Document $database * @param Document $collection - * @param Document $attribute * @param Document $project + * @param Document|null $attribute + * @param Document|null $index + * @param Document $payload * @param Realtime $queueForRealtime * @return void */ protected function trigger( Document $database, Document $collection, - Document $attribute, Document $project, + Document|null $attribute = null, + Document|null $index = null, + Document $payload, string $event, Realtime $queueForRealtime ): void { @@ -617,9 +621,17 @@ class Databases extends Action ->setProjectId('console') ->setEvent($event) ->setParam('databaseId', $database->getId()) - ->setParam('collectionId', $collection->getId()) - ->setParam('attributeId', $attribute->getId()) - ->setPayload($attribute->getArrayCopy()) + ->setParam('collectionId', $collection->getId()); + + if ($attribute !== null) { + $queueForRealtime->setParam('attributeId', $attribute->getId()); + } + if ($index !== null) { + $queueForRealtime->setParam('indexId', $index->getId()); + } + + $queueForRealtime + ->setPayload($payload->getArrayCopy()) ->trigger(); } } From 532160705ffbde2ec6f008546d66ddcbe5e8048c Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Tue, 11 Feb 2025 05:50:30 +0000 Subject: [PATCH 016/143] chore: refactor realtime queueing in functions worker --- src/Appwrite/Platform/Workers/Functions.php | 58 +++++++++------------ 1 file changed, 25 insertions(+), 33 deletions(-) diff --git a/src/Appwrite/Platform/Workers/Functions.php b/src/Appwrite/Platform/Workers/Functions.php index 0a7c39c02f..f485db8648 100644 --- a/src/Appwrite/Platform/Workers/Functions.php +++ b/src/Appwrite/Platform/Workers/Functions.php @@ -5,8 +5,8 @@ namespace Appwrite\Platform\Workers; use Ahc\Jwt\JWT; use Appwrite\Event\Event; use Appwrite\Event\Func; +use Appwrite\Event\Realtime; use Appwrite\Event\StatsUsage; -use Appwrite\Messaging\Adapter\Realtime; use Appwrite\Utopia\Response\Model\Execution; use Exception; use Executor\Executor; @@ -45,14 +45,15 @@ class Functions extends Action ->inject('message') ->inject('dbForProject') ->inject('queueForFunctions') + ->inject('queueForRealtime') ->inject('queueForEvents') ->inject('queueForStatsUsage') ->inject('log') ->inject('isResourceBlocked') - ->callback(fn (Document $project, Message $message, Database $dbForProject, Func $queueForFunctions, Event $queueForEvents, StatsUsage $queueForStatsUsage, Log $log, callable $isResourceBlocked) => $this->action($project, $message, $dbForProject, $queueForFunctions, $queueForEvents, $queueForStatsUsage, $log, $isResourceBlocked)); + ->callback(fn (Document $project, Message $message, Database $dbForProject, Func $queueForFunctions, Realtime $queueForRealtime, Event $queueForEvents, StatsUsage $queueForStatsUsage, Log $log, callable $isResourceBlocked) => $this->action($project, $message, $dbForProject, $queueForFunctions, $queueForRealtime, $queueForEvents, $queueForStatsUsage, $log, $isResourceBlocked)); } - public function action(Document $project, Message $message, Database $dbForProject, Func $queueForFunctions, Event $queueForEvents, StatsUsage $queueForStatsUsage, Log $log, callable $isResourceBlocked): void + public function action(Document $project, Message $message, Database $dbForProject, Func $queueForFunctions, Realtime $queueForRealtime, Event $queueForEvents, StatsUsage $queueForStatsUsage, Log $log, callable $isResourceBlocked): void { $payload = $message->getPayload() ?? []; @@ -137,6 +138,7 @@ class Functions extends Action log: $log, dbForProject: $dbForProject, queueForFunctions: $queueForFunctions, + queueForRealtime: $queueForRealtime, queueForStatsUsage: $queueForStatsUsage, queueForEvents: $queueForEvents, project: $project, @@ -177,6 +179,7 @@ class Functions extends Action log: $log, dbForProject: $dbForProject, queueForFunctions: $queueForFunctions, + queueForRealtime: $queueForRealtime, queueForStatsUsage: $queueForStatsUsage, queueForEvents: $queueForEvents, project: $project, @@ -199,6 +202,7 @@ class Functions extends Action log: $log, dbForProject: $dbForProject, queueForFunctions: $queueForFunctions, + queueForRealtime: $queueForRealtime, queueForStatsUsage: $queueForStatsUsage, queueForEvents: $queueForEvents, project: $project, @@ -284,6 +288,7 @@ class Functions extends Action * @param Log $log * @param Database $dbForProject * @param Func $queueForFunctions + * @param Realtime $queueForRealtime * @param StatsUsage $queueForStatsUsage * @param Event $queueForEvents * @param Document $project @@ -308,6 +313,7 @@ class Functions extends Action Log $log, Database $dbForProject, Func $queueForFunctions, + Realtime $queueForRealtime, StatsUsage $queueForStatsUsage, Event $queueForEvents, Document $project, @@ -564,20 +570,21 @@ class Functions extends Action ; } - $execution = $dbForProject->updateDocument('executions', $executionId, $execution); - /** Trigger Webhook */ $executionModel = new Execution(); $queueForEvents - ->setQueue(Event::WEBHOOK_QUEUE_NAME) - ->setClass(Event::WEBHOOK_CLASS_NAME) ->setProject($project) ->setUser($user) ->setEvent('functions.[functionId].executions.[executionId].update') ->setParam('functionId', $function->getId()) ->setParam('executionId', $execution->getId()) - ->setPayload($execution->getArrayCopy(array_keys($executionModel->getRules()))) + ->setPayload($execution->getArrayCopy(array_keys($executionModel->getRules()))); + + /** Trigger Webhook */ + $queueForEvents + ->setQueue(Event::WEBHOOK_QUEUE_NAME) + ->setClass(Event::WEBHOOK_CLASS_NAME) ->trigger(); /** Trigger Functions */ @@ -585,31 +592,16 @@ class Functions extends Action ->from($queueForEvents) ->trigger(); - /** Trigger realtime event */ - $allEvents = Event::generateEvents('functions.[functionId].executions.[executionId].update', [ - 'functionId' => $function->getId(), - 'executionId' => $execution->getId() - ]); - $target = Realtime::fromPayload( - // Pass first, most verbose event pattern - event: $allEvents[0], - payload: $execution, - project: $project - ); - Realtime::send( - projectId: 'console', - payload: $execution->getArrayCopy(), - events: $allEvents, - channels: $target['channels'], - roles: $target['roles'] - ); - Realtime::send( - projectId: $project->getId(), - payload: $execution->getArrayCopy(), - events: $allEvents, - channels: $target['channels'], - roles: $target['roles'] - ); + /** Trigger Realtime Events */ + $queueForRealtime + ->from($queueForEvents) + ->setProjectId('console') + ->trigger(); + + $queueForRealtime + ->from($queueForEvents) + ->setProjectId($project->getId()) + ->trigger(); if (!empty($error)) { throw new Exception($error, $errorCode); From 5076303c33a600da6b91a4a8ebe7cb18d79d325c Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Tue, 11 Feb 2025 06:06:43 +0000 Subject: [PATCH 017/143] chore: refactor realtime queueing in builds worker --- src/Appwrite/Platform/Workers/Builds.php | 115 +++++++++-------------- 1 file changed, 47 insertions(+), 68 deletions(-) diff --git a/src/Appwrite/Platform/Workers/Builds.php b/src/Appwrite/Platform/Workers/Builds.php index e7cbbd5088..f3f733f5eb 100644 --- a/src/Appwrite/Platform/Workers/Builds.php +++ b/src/Appwrite/Platform/Workers/Builds.php @@ -5,8 +5,8 @@ namespace Appwrite\Platform\Workers; use Ahc\Jwt\JWT; use Appwrite\Event\Event; use Appwrite\Event\Func; +use Appwrite\Event\Realtime; use Appwrite\Event\StatsUsage; -use Appwrite\Messaging\Adapter\Realtime; use Appwrite\Utopia\Response\Model\Deployment; use Appwrite\Vcs\Comment; use Exception; @@ -50,12 +50,13 @@ class Builds extends Action ->inject('dbForPlatform') ->inject('queueForEvents') ->inject('queueForFunctions') + ->inject('queueForRealtime') ->inject('queueForStatsUsage') ->inject('cache') ->inject('dbForProject') ->inject('deviceForFunctions') ->inject('log') - ->callback(fn ($message, Document $project, Database $dbForPlatform, Event $queueForEvents, Func $queueForFunctions, StatsUsage $usage, Cache $cache, Database $dbForProject, Device $deviceForFunctions, Log $log) => $this->action($message, $project, $dbForPlatform, $queueForEvents, $queueForFunctions, $usage, $cache, $dbForProject, $deviceForFunctions, $log)); + ->callback(fn ($message, Document $project, Database $dbForPlatform, Event $queueForEvents, Func $queueForFunctions, Realtime $queueForRealtime, StatsUsage $usage, Cache $cache, Database $dbForProject, Device $deviceForFunctions, Log $log) => $this->action($message, $project, $dbForPlatform, $queueForEvents, $queueForFunctions, $queueForRealtime, $usage, $cache, $dbForProject, $deviceForFunctions, $log)); } /** @@ -64,6 +65,7 @@ class Builds extends Action * @param Database $dbForPlatform * @param Event $queueForEvents * @param Func $queueForFunctions + * @param Realtime $queueForRealtime * @param StatsUsage $queueForStatsUsage * @param Cache $cache * @param Database $dbForProject @@ -72,7 +74,7 @@ class Builds extends Action * @return void * @throws \Utopia\Database\Exception */ - public function action(Message $message, Document $project, Database $dbForPlatform, Event $queueForEvents, Func $queueForFunctions, StatsUsage $queueForStatsUsage, Cache $cache, Database $dbForProject, Device $deviceForFunctions, Log $log): void + public function action(Message $message, Document $project, Database $dbForPlatform, Event $queueForEvents, Func $queueForFunctions, Realtime $queueForRealtime, StatsUsage $queueForStatsUsage, Cache $cache, Database $dbForProject, Device $deviceForFunctions, Log $log): void { $payload = $message->getPayload() ?? []; @@ -93,7 +95,7 @@ class Builds extends Action case BUILD_TYPE_RETRY: Console::info('Creating build for deployment: ' . $deployment->getId()); $github = new GitHub($cache); - $this->buildDeployment($deviceForFunctions, $queueForFunctions, $queueForEvents, $queueForStatsUsage, $dbForPlatform, $dbForProject, $github, $project, $resource, $deployment, $template, $log); + $this->buildDeployment($deviceForFunctions, $queueForFunctions, $queueForRealtime, $queueForEvents, $queueForStatsUsage, $dbForPlatform, $dbForProject, $github, $project, $resource, $deployment, $template, $log); break; default: @@ -104,6 +106,7 @@ class Builds extends Action /** * @param Device $deviceForFunctions * @param Func $queueForFunctions + * @param Realtime $queueForRealtime * @param Event $queueForEvents * @param StatsUsage $queueForStatsUsage * @param Database $dbForPlatform @@ -118,7 +121,7 @@ class Builds extends Action * @throws \Utopia\Database\Exception * @throws Exception */ - protected function buildDeployment(Device $deviceForFunctions, Func $queueForFunctions, Event $queueForEvents, StatsUsage $queueForStatsUsage, Database $dbForPlatform, Database $dbForProject, GitHub $github, Document $project, Document $function, Document $deployment, Document $template, Log $log): void + protected function buildDeployment(Device $deviceForFunctions, Func $queueForFunctions, Realtime $queueForRealtime, Event $queueForEvents, StatsUsage $queueForStatsUsage, Database $dbForPlatform, Database $dbForProject, GitHub $github, Document $project, Document $function, Document $deployment, Document $template, Log $log): void { $executor = new Executor(System::getEnv('_APP_EXECUTOR_HOST')); @@ -152,10 +155,7 @@ class Builds extends Action } // Realtime preparation - $allEvents = Event::generateEvents('functions.[functionId].deployments.[deploymentId].update', [ - 'functionId' => $function->getId(), - 'deploymentId' => $deployment->getId() - ]); + $event = "functions.[functionId].deployments.[deploymentId].update"; $startTime = DateTime::now(); $durationStart = \microtime(true); @@ -369,21 +369,16 @@ class Builds extends Action $deployment = $dbForProject->updateDocument('deployments', $deployment->getId(), $deployment); /** - * Send realtime Event + * Trigger Realtime Event */ - $target = Realtime::fromPayload( - // Pass first, most verbose event pattern - event: $allEvents[0], - payload: $build, - project: $project - ); - Realtime::send( - projectId: 'console', - payload: $build->getArrayCopy(), - events: $allEvents, - channels: $target['channels'], - roles: $target['roles'] - ); + $queueForRealtime + ->setProject($project) + ->setProjectId('console') + ->setEvent($event) + ->setParam('functionId', $function->getId()) + ->setParam('deploymentId', $deployment->getId()) + ->setPayload($build->getArrayCopy()) + ->trigger(); } $tmpPath = '/tmp/builds/' . $buildId; @@ -449,21 +444,15 @@ class Builds extends Action ->from($deploymentUpdate) ->trigger(); - /** Trigger Realtime */ - $target = Realtime::fromPayload( - // Pass first, most verbose event pattern - event: $allEvents[0], - payload: $build, - project: $project - ); - - Realtime::send( - projectId: 'console', - payload: $build->getArrayCopy(), - events: $allEvents, - channels: $target['channels'], - roles: $target['roles'] - ); + /** Trigger Realtime Event */ + $queueForRealtime + ->setProject($project) + ->setProjectId('console') + ->setEvent($event) + ->setParam('functionId', $function->getId()) + ->setParam('deploymentId', $deployment->getId()) + ->setPayload($build->getArrayCopy()) + ->trigger(); $vars = []; @@ -556,12 +545,12 @@ class Builds extends Action $err = $error; } }), - Co\go(function () use ($executor, $project, $deployment, &$response, &$build, $dbForProject, $allEvents, &$err, &$isCanceled) { + Co\go(function () use ($executor, $project, $function, $deployment, &$response, &$build, $dbForProject, $event, &$err, $queueForRealtime, &$isCanceled) { try { $executor->getLogs( deploymentId: $deployment->getId(), projectId: $project->getId(), - callback: function ($logs) use (&$response, &$err, &$build, $dbForProject, $allEvents, $project, &$isCanceled) { + callback: function ($logs) use (&$response, &$err, &$build, $dbForProject, $event, $project, $function, $deployment, $queueForRealtime, &$isCanceled) { if ($isCanceled) { return; } @@ -586,21 +575,16 @@ class Builds extends Action $build = $dbForProject->updateDocument('builds', $build->getId(), $build); /** - * Send realtime Event + * Trigger Realtime Event */ - $target = Realtime::fromPayload( - // Pass first, most verbose event pattern - event: $allEvents[0], - payload: $build, - project: $project - ); - Realtime::send( - projectId: 'console', - payload: $build->getArrayCopy(), - events: $allEvents, - channels: $target['channels'], - roles: $target['roles'] - ); + $queueForRealtime + ->setProject($project) + ->setProjectId('console') + ->setEvent($event) + ->setParam('functionId', $function->getId()) + ->setParam('deploymentId', $deployment->getId()) + ->setPayload($build->getArrayCopy()) + ->trigger(); } } ); @@ -688,21 +672,16 @@ class Builds extends Action } } finally { /** - * Send realtime Event + * Trigger Realtime Event */ - $target = Realtime::fromPayload( - // Pass first, most verbose event pattern - event: $allEvents[0], - payload: $build, - project: $project - ); - Realtime::send( - projectId: 'console', - payload: $build->getArrayCopy(), - events: $allEvents, - channels: $target['channels'], - roles: $target['roles'] - ); + $queueForRealtime + ->setProject($project) + ->setProjectId('console') + ->setEvent($event) + ->setParam('functionId', $function->getId()) + ->setParam('deploymentId', $deployment->getId()) + ->setPayload($build->getArrayCopy()) + ->trigger(); /** Trigger usage queue */ if ($build->getAttribute('status') === 'ready') { From 940f4d12af617977b5f8312658e0454dee4f7d5f Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Tue, 11 Feb 2025 07:52:37 +0000 Subject: [PATCH 018/143] Remove usage and usage dump in favor of stats-usage and stats-usage-dump --- Dockerfile | 4 +- app/init.php | 3 - app/worker.php | 8 - bin/worker-usage | 3 - bin/worker-usage-dump | 3 - src/Appwrite/Platform/Services/Workers.php | 8 - src/Appwrite/Platform/Workers/Usage.php | 292 -------------------- src/Appwrite/Platform/Workers/UsageDump.php | 286 ------------------- 8 files changed, 1 insertion(+), 606 deletions(-) delete mode 100644 bin/worker-usage delete mode 100644 bin/worker-usage-dump delete mode 100644 src/Appwrite/Platform/Workers/Usage.php delete mode 100644 src/Appwrite/Platform/Workers/UsageDump.php diff --git a/Dockerfile b/Dockerfile index 2bb9f80d9e..01ad5d3fba 100755 --- a/Dockerfile +++ b/Dockerfile @@ -88,9 +88,7 @@ RUN chmod +x /usr/local/bin/doctor && \ chmod +x /usr/local/bin/worker-stats-usage && \ chmod +x /usr/local/bin/worker-stats-usage-dump && \ chmod +x /usr/local/bin/stats-resources && \ - chmod +x /usr/local/bin/worker-stats-resources && \ - chmod +x /usr/local/bin/worker-usage && \ - chmod +x /usr/local/bin/worker-usage-dump + chmod +x /usr/local/bin/worker-stats-resources # Letsencrypt Permissions RUN mkdir -p /etc/letsencrypt/live/ && chmod -Rf 755 /etc/letsencrypt/live/ diff --git a/app/init.php b/app/init.php index 0c6746de3c..9bd8cf839c 100644 --- a/app/init.php +++ b/app/init.php @@ -1197,9 +1197,6 @@ App::setResource('queueForAudits', function (Queue\Publisher $publisher) { App::setResource('queueForFunctions', function (Queue\Publisher $publisher) { return new Func($publisher); }, ['publisher']); -App::setResource('queueForUsage', function (Queue\Publisher $publisher) { - return new Usage($publisher); -}, ['publisher']); App::setResource('queueForCertificates', function (Queue\Publisher $publisher) { return new Certificate($publisher); }, ['publisher']); diff --git a/app/worker.php b/app/worker.php index 605474e9f1..dc02da1bb7 100644 --- a/app/worker.php +++ b/app/worker.php @@ -269,14 +269,6 @@ Server::setResource('consumer', function (Group $pools) { return $pools->get('consumer')->pop()->getResource(); }, ['pools']); -Server::setResource('queueForUsage', function (Publisher $publisher) { - return new Usage($publisher); -}, ['publisher']); - -Server::setResource('queueForUsageDump', function (Publisher $publisher) { - return new UsageDump($publisher); -}, ['publisher']); - Server::setResource('queueForStatsUsage', function (Publisher $publisher) { return new StatsUsage($publisher); }, ['publisher']); diff --git a/bin/worker-usage b/bin/worker-usage deleted file mode 100644 index e39ce8477c..0000000000 --- a/bin/worker-usage +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh - -php /usr/src/code/app/worker.php usage $@ \ No newline at end of file diff --git a/bin/worker-usage-dump b/bin/worker-usage-dump deleted file mode 100644 index 43ca87fcb3..0000000000 --- a/bin/worker-usage-dump +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh - -php /usr/src/code/app/worker.php usage-dump $@ \ No newline at end of file diff --git a/src/Appwrite/Platform/Services/Workers.php b/src/Appwrite/Platform/Services/Workers.php index e121ee35f7..4f4095aca4 100644 --- a/src/Appwrite/Platform/Services/Workers.php +++ b/src/Appwrite/Platform/Services/Workers.php @@ -14,10 +14,6 @@ use Appwrite\Platform\Workers\Migrations; use Appwrite\Platform\Workers\StatsResources; use Appwrite\Platform\Workers\StatsUsage; use Appwrite\Platform\Workers\StatsUsageDump; -/** remove */ -use Appwrite\Platform\Workers\Usage; -use Appwrite\Platform\Workers\UsageDump; -/** /remove */ use Appwrite\Platform\Workers\Webhooks; use Utopia\Platform\Service; @@ -40,10 +36,6 @@ class Workers extends Service ->addAction(StatsUsage::getName(), new StatsUsage()) ->addAction(Migrations::getName(), new Migrations()) ->addAction(StatsResources::getName(), new StatsResources()) - /** Remove */ - ->addAction(UsageDump::getName(), new UsageDump()) - ->addAction(Usage::getName(), new Usage()) - /** /remove */ ; } } diff --git a/src/Appwrite/Platform/Workers/Usage.php b/src/Appwrite/Platform/Workers/Usage.php deleted file mode 100644 index 3687eeab67..0000000000 --- a/src/Appwrite/Platform/Workers/Usage.php +++ /dev/null @@ -1,292 +0,0 @@ -desc('Usage worker') - ->inject('message') - ->inject('project') - ->inject('getProjectDB') - ->inject('queueForUsageDump') - ->callback(function (Message $message, Document $project, callable $getProjectDB, UsageDump $queueForUsageDump) { - $this->action($message, $project, $getProjectDB, $queueForUsageDump); - }); - - $this->aggregationInterval = (int) System::getEnv('_APP_USAGE_AGGREGATION_INTERVAL', '20'); - $this->lastTriggeredTime = time(); - } - - /** - * @param Message $message - * @param Document $project - * @param callable $getProjectDB - * @param UsageDump $queueForUsageDump - * @return void - * @throws \Utopia\Database\Exception - * @throws Exception - */ - public function action(Message $message, Document $project, callable $getProjectDB, UsageDump $queueForUsageDump): void - { - $payload = $message->getPayload() ?? []; - if (empty($payload)) { - throw new Exception('Missing payload'); - } - - - if (empty($project->getAttribute('database'))) { - var_dump($payload); - return; - } - - $projectId = $project->getInternalId(); - foreach ($payload['reduce'] ?? [] as $document) { - if (empty($document)) { - continue; - } - - $this->reduce( - project: $project, - document: new Document($document), - metrics: $payload['metrics'], - getProjectDB: $getProjectDB - ); - } - - - $this->stats[$projectId]['project'] = [ - '$id' => $project->getId(), - '$internalId' => $project->getInternalId(), - 'database' => $project->getAttribute('database'), - ]; - $this->stats[$projectId]['receivedAt'] = DateTime::now(); - foreach ($payload['metrics'] ?? [] as $metric) { - $this->keys++; - if (!isset($this->stats[$projectId]['keys'][$metric['key']])) { - $this->stats[$projectId]['keys'][$metric['key']] = $metric['value']; - continue; - } - - $this->stats[$projectId]['keys'][$metric['key']] += $metric['value']; - } - - // If keys crossed threshold or X time passed since the last send and there are some keys in the array ($this->stats) - if ( - $this->keys >= self::KEYS_THRESHOLD || - (time() - $this->lastTriggeredTime > $this->aggregationInterval && $this->keys > 0) - ) { - Console::warning('[' . DateTime::now() . '] Aggregated ' . $this->keys . ' keys'); - - $queueForUsageDump - ->setStats($this->stats) - ->trigger(); - - $this->stats = []; - $this->keys = 0; - $this->lastTriggeredTime = time(); - } - } - - /** - * On Documents that tied by relations like functions>deployments>build || documents>collection>database || buckets>files. - * When we remove a parent document we need to deduct his children aggregation from the project scope. - * @param Document $project - * @param Document $document - * @param array $metrics - * @param callable $getProjectDB - * @return void - */ - private function reduce(Document $project, Document $document, array &$metrics, callable $getProjectDB): void - { - $dbForProject = $getProjectDB($project); - - try { - switch (true) { - case $document->getCollection() === 'users': // users - $sessions = count($document->getAttribute(METRIC_SESSIONS, 0)); - if (!empty($sessions)) { - $metrics[] = [ - 'key' => METRIC_SESSIONS, - 'value' => ($sessions * -1), - ]; - } - break; - case $document->getCollection() === 'databases': // databases - $collections = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{databaseInternalId}', $document->getInternalId(), METRIC_DATABASE_ID_COLLECTIONS))); - $documents = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{databaseInternalId}', $document->getInternalId(), METRIC_DATABASE_ID_DOCUMENTS))); - if (!empty($collections['value'])) { - $metrics[] = [ - 'key' => METRIC_COLLECTIONS, - 'value' => ($collections['value'] * -1), - ]; - } - - if (!empty($documents['value'])) { - $metrics[] = [ - 'key' => METRIC_DOCUMENTS, - 'value' => ($documents['value'] * -1), - ]; - } - break; - case str_starts_with($document->getCollection(), 'database_') && !str_contains($document->getCollection(), 'collection'): //collections - $parts = explode('_', $document->getCollection()); - $databaseInternalId = $parts[1] ?? 0; - $documents = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace(['{databaseInternalId}', '{collectionInternalId}'], [$databaseInternalId, $document->getInternalId()], METRIC_DATABASE_ID_COLLECTION_ID_DOCUMENTS))); - - if (!empty($documents['value'])) { - $metrics[] = [ - 'key' => METRIC_DOCUMENTS, - 'value' => ($documents['value'] * -1), - ]; - $metrics[] = [ - 'key' => str_replace('{databaseInternalId}', $databaseInternalId, METRIC_DATABASE_ID_DOCUMENTS), - 'value' => ($documents['value'] * -1), - ]; - } - break; - - case $document->getCollection() === 'buckets': - $files = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{bucketInternalId}', $document->getInternalId(), METRIC_BUCKET_ID_FILES))); - $storage = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{bucketInternalId}', $document->getInternalId(), METRIC_BUCKET_ID_FILES_STORAGE))); - - if (!empty($files['value'])) { - $metrics[] = [ - 'key' => METRIC_FILES, - 'value' => ($files['value'] * -1), - ]; - } - - if (!empty($storage['value'])) { - $metrics[] = [ - 'key' => METRIC_FILES_STORAGE, - 'value' => ($storage['value'] * -1), - ]; - } - break; - - case $document->getCollection() === 'functions': - $deployments = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace(['{resourceType}', '{resourceInternalId}'], ['functions', $document->getInternalId()], METRIC_FUNCTION_ID_DEPLOYMENTS))); - $deploymentsStorage = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace(['{resourceType}', '{resourceInternalId}'], ['functions', $document->getInternalId()], METRIC_FUNCTION_ID_DEPLOYMENTS_STORAGE))); - $builds = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{functionInternalId}', $document->getInternalId(), METRIC_FUNCTION_ID_BUILDS))); - $buildsSuccess = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{functionInternalId}', $document->getInternalId(), METRIC_FUNCTION_ID_BUILDS_SUCCESS))); - $buildsFailed = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{functionInternalId}', $document->getInternalId(), METRIC_FUNCTION_ID_BUILDS_FAILED))); - $buildsStorage = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{functionInternalId}', $document->getInternalId(), METRIC_FUNCTION_ID_BUILDS_STORAGE))); - $buildsCompute = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{functionInternalId}', $document->getInternalId(), METRIC_FUNCTION_ID_BUILDS_COMPUTE))); - $buildsComputeSuccess = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{functionInternalId}', $document->getInternalId(), METRIC_FUNCTION_ID_BUILDS_COMPUTE_SUCCESS))); - $buildsComputeFailed = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{functionInternalId}', $document->getInternalId(), METRIC_FUNCTION_ID_BUILDS_COMPUTE_FAILED))); - $executions = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{functionInternalId}', $document->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS))); - $executionsCompute = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{functionInternalId}', $document->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS_COMPUTE))); - - if (!empty($deployments['value'])) { - $metrics[] = [ - 'key' => METRIC_DEPLOYMENTS, - 'value' => ($deployments['value'] * -1), - ]; - } - - if (!empty($deploymentsStorage['value'])) { - $metrics[] = [ - 'key' => METRIC_DEPLOYMENTS_STORAGE, - 'value' => ($deploymentsStorage['value'] * -1), - ]; - } - - if (!empty($builds['value'])) { - $metrics[] = [ - 'key' => METRIC_BUILDS, - 'value' => ($builds['value'] * -1), - ]; - } - - if (!empty($buildsSuccess['value'])) { - $metrics[] = [ - 'key' => METRIC_BUILDS_SUCCESS, - 'value' => ($buildsSuccess['value'] * -1), - ]; - } - - if (!empty($buildsFailed['value'])) { - $metrics[] = [ - 'key' => METRIC_BUILDS_FAILED, - 'value' => ($buildsFailed['value'] * -1), - ]; - } - - if (!empty($buildsStorage['value'])) { - $metrics[] = [ - 'key' => METRIC_BUILDS_STORAGE, - 'value' => ($buildsStorage['value'] * -1), - ]; - } - - if (!empty($buildsCompute['value'])) { - $metrics[] = [ - 'key' => METRIC_BUILDS_COMPUTE, - 'value' => ($buildsCompute['value'] * -1), - ]; - } - - if (!empty($buildsComputeSuccess['value'])) { - $metrics[] = [ - 'key' => METRIC_BUILDS_COMPUTE_SUCCESS, - 'value' => ($buildsComputeSuccess['value'] * -1), - ]; - } - - if (!empty($buildsComputeFailed['value'])) { - $metrics[] = [ - 'key' => METRIC_BUILDS_COMPUTE_FAILED, - 'value' => ($buildsComputeFailed['value'] * -1), - ]; - } - - if (!empty($executions['value'])) { - $metrics[] = [ - 'key' => METRIC_EXECUTIONS, - 'value' => ($executions['value'] * -1), - ]; - } - - if (!empty($executionsCompute['value'])) { - $metrics[] = [ - 'key' => METRIC_EXECUTIONS_COMPUTE, - 'value' => ($executionsCompute['value'] * -1), - ]; - } - break; - default: - break; - } - } catch (\Throwable $e) { - console::error("[reducer] " . " {DateTime::now()} " . " {$project->getInternalId()} " . " {$e->getMessage()}"); - } - } -} diff --git a/src/Appwrite/Platform/Workers/UsageDump.php b/src/Appwrite/Platform/Workers/UsageDump.php deleted file mode 100644 index 2f1d13f29a..0000000000 --- a/src/Appwrite/Platform/Workers/UsageDump.php +++ /dev/null @@ -1,286 +0,0 @@ - 'Y-m-d H:00', - '1d' => 'Y-m-d 00:00', - 'inf' => '0000-00-00 00:00' - ]; - - public static function getName(): string - { - return 'usage-dump'; - } - - /** - * @throws \Exception - */ - public function __construct() - { - $this - ->inject('message') - ->inject('getProjectDB') - ->callback(function (Message $message, callable $getProjectDB) { - $this->action($message, $getProjectDB); - }); - } - - /** - * @param Message $message - * @param callable $getProjectDB - * @return void - * @throws Exception - * @throws \Utopia\Database\Exception - */ - public function action(Message $message, callable $getProjectDB): void - { - $payload = $message->getPayload() ?? []; - if (empty($payload)) { - throw new Exception('Missing payload'); - } - - - foreach ($payload['stats'] ?? [] as $stats) { - $project = new Document($stats['project'] ?? []); - - /** - * End temp bug fallback - */ - $numberOfKeys = !empty($stats['keys']) ? count($stats['keys']) : 0; - $receivedAt = $stats['receivedAt'] ?? 'NONE'; - if ($numberOfKeys === 0) { - continue; - } - - console::log('['.DateTime::now().'] Id: '.$project->getId(). ' InternalId: '.$project->getInternalId(). ' Db: '.$project->getAttribute('database').' ReceivedAt: '.$receivedAt. ' Keys: '.$numberOfKeys); - - try { - $dbForProject = $getProjectDB($project); - foreach ($stats['keys'] ?? [] as $key => $value) { - if ($value == 0) { - continue; - } - - if (str_contains($key, METRIC_DATABASES_STORAGE)) { - try { - $this->handleDatabaseStorage($key, $dbForProject); - } catch (\Exception $e) { - console::error('[' . DateTime::now() . '] failed to calculate database storage for key [' . $key . '] ' . $e->getMessage()); - } - continue; - } - - foreach ($this->periods as $period => $format) { - $time = 'inf' === $period ? null : date($format, time()); - $id = \md5("{$time}_{$period}_{$key}"); - - try { - $dbForProject->createDocument('stats', new Document([ - '$id' => $id, - 'period' => $period, - 'time' => $time, - 'metric' => $key, - 'value' => $value, - 'region' => System::getEnv('_APP_REGION', 'default'), - ])); - } catch (Duplicate $th) { - if ($value < 0) { - $dbForProject->decreaseDocumentAttribute( - 'stats', - $id, - 'value', - abs($value) - ); - } else { - $dbForProject->increaseDocumentAttribute( - 'stats', - $id, - 'value', - $value - ); - } - } - } - } - } catch (\Exception $e) { - console::error('[' . DateTime::now() . '] project [' . $project->getInternalId() . '] database [' . $project['database'] . '] ' . ' ' . $e->getMessage()); - } - } - } - - private function handleDatabaseStorage(string $key, Database $dbForProject): void - { - $data = explode('.', $key); - $start = microtime(true); - - $updateMetric = function (Database $dbForProject, int $value, string $key, string $period, string|null $time) { - $id = \md5("{$time}_{$period}_{$key}"); - - try { - $dbForProject->createDocument('stats', new Document([ - '$id' => $id, - 'period' => $period, - 'time' => $time, - 'metric' => $key, - 'value' => $value, - 'region' => System::getEnv('_APP_REGION', 'default'), - ])); - } catch (Duplicate $th) { - if ($value < 0) { - $dbForProject->decreaseDocumentAttribute( - 'stats', - $id, - 'value', - abs($value) - ); - } else { - $dbForProject->increaseDocumentAttribute( - 'stats', - $id, - 'value', - $value - ); - } - } - }; - - foreach ($this->periods as $period => $format) { - $time = 'inf' === $period ? null : date($format, time()); - $id = \md5("{$time}_{$period}_{$key}"); - - $value = 0; - $previousValue = 0; - try { - $previousValue = ($dbForProject->getDocument('stats', $id))->getAttribute('value', 0); - } catch (\Exception $e) { - // No previous value - } - - switch (count($data)) { - // Collection Level - case METRIC_COLLECTION_LEVEL_STORAGE: - Console::log('[' . DateTime::now() . '] Collection Level Storage Calculation [' . $key . ']'); - $databaseInternalId = $data[0]; - $collectionInternalId = $data[1]; - - try { - $value = $dbForProject->getSizeOfCollection('database_' . $databaseInternalId . '_collection_' . $collectionInternalId); - } catch (\Exception $e) { - // Collection not found - if ($e->getMessage() !== 'Collection not found') { - throw $e; - } - } - - // Compare with previous value - $diff = $value - $previousValue; - - if ($diff === 0) { - break; - } - - // Update Collection - $updateMetric($dbForProject, $diff, $key, $period, $time); - - // Update Database - $databaseKey = str_replace(['{databaseInternalId}'], [$data[0]], METRIC_DATABASE_ID_STORAGE); - $updateMetric($dbForProject, $diff, $databaseKey, $period, $time); - - // Update Project - $projectKey = METRIC_DATABASES_STORAGE; - $updateMetric($dbForProject, $diff, $projectKey, $period, $time); - break; - // Database Level - case METRIC_DATABASE_LEVEL_STORAGE: - Console::log('[' . DateTime::now() . '] Database Level Storage Calculation [' . $key . ']'); - $databaseInternalId = $data[0]; - - $collections = []; - try { - $collections = $dbForProject->find('database_' . $databaseInternalId); - } catch (\Exception $e) { - // Database not found - if ($e->getMessage() !== 'Collection not found') { - throw $e; - } - } - - foreach ($collections as $collection) { - try { - $value += $dbForProject->getSizeOfCollection('database_' . $databaseInternalId . '_collection_' . $collection->getInternalId()); - } catch (\Exception $e) { - // Collection not found - if ($e->getMessage() !== 'Collection not found') { - throw $e; - } - } - } - - $diff = $value - $previousValue; - - if ($diff === 0) { - break; - } - - // Update Database - $databaseKey = str_replace(['{databaseInternalId}'], [$data[0]], METRIC_DATABASE_ID_STORAGE); - $updateMetric($dbForProject, $diff, $databaseKey, $period, $time); - - // Update Project - $projectKey = METRIC_DATABASES_STORAGE; - $updateMetric($dbForProject, $diff, $projectKey, $period, $time); - break; - // Project Level - case METRIC_PROJECT_LEVEL_STORAGE: - Console::log('[' . DateTime::now() . '] Project Level Storage Calculation [' . $key . ']'); - // Get all project databases - $databases = $dbForProject->find('database'); - - // Recalculate all databases - foreach ($databases as $database) { - $collections = $dbForProject->find('database_' . $database->getInternalId()); - - foreach ($collections as $collection) { - try { - $value += $dbForProject->getSizeOfCollection('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId()); - } catch (\Exception $e) { - // Collection not found - if ($e->getMessage() !== 'Collection not found') { - throw $e; - } - } - } - } - - $diff = $value - $previousValue; - - // Update Project - $projectKey = METRIC_DATABASES_STORAGE; - $updateMetric($dbForProject, $diff, $projectKey, $period, $time); - break; - } - } - - $end = microtime(true); - - console::log('[' . DateTime::now() . '] DB Storage Calculation [' . $key . '] took ' . (($end - $start) * 1000) . ' milliseconds'); - } -} From f2fc45da02af88d2e1d69d3e431d78065d6f9a9f Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Tue, 11 Feb 2025 09:46:00 +0000 Subject: [PATCH 019/143] fix format --- app/worker.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/worker.php b/app/worker.php index dc02da1bb7..7fb96f1208 100644 --- a/app/worker.php +++ b/app/worker.php @@ -16,8 +16,6 @@ use Appwrite\Event\Migration; use Appwrite\Event\StatsUsage; use Appwrite\Event\StatsUsageDump; /** remove */ -use Appwrite\Event\Usage; -use Appwrite\Event\UsageDump; /** /remove */ use Appwrite\Platform\Appwrite; use Swoole\Runtime; From 721b9b9aea7a025ffbbc6ea950773eaf3eb71ca3 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Thu, 13 Feb 2025 00:52:20 +0000 Subject: [PATCH 020/143] remove old events --- src/Appwrite/Event/Usage.php | 78 -------------------------------- src/Appwrite/Event/UsageDump.php | 44 ------------------ 2 files changed, 122 deletions(-) delete mode 100644 src/Appwrite/Event/Usage.php delete mode 100644 src/Appwrite/Event/UsageDump.php diff --git a/src/Appwrite/Event/Usage.php b/src/Appwrite/Event/Usage.php deleted file mode 100644 index c70cea5c73..0000000000 --- a/src/Appwrite/Event/Usage.php +++ /dev/null @@ -1,78 +0,0 @@ -setQueue(Event::USAGE_QUEUE_NAME) - ->setClass(Event::USAGE_CLASS_NAME); - } - - /** - * Add reduce. - * - * @param Document $document - * @return self - */ - public function addReduce(Document $document): self - { - $this->reduce[] = $document; - - return $this; - } - - /** - * Add metric. - * - * @param string $key - * @param int $value - * @return self - */ - public function addMetric(string $key, int $value): self - { - - $this->metrics[] = [ - 'key' => $key, - 'value' => $value, - ]; - - return $this; - } - - /** - * Prepare the payload for the usage event. - * - * @return array - */ - protected function preparePayload(): array - { - return [ - 'project' => $this->project, - 'reduce' => $this->reduce, - 'metrics' => $this->metrics, - ]; - } - - /** - * Sends metrics to the usage worker. - * - * @return string|bool - */ - public function trigger(): string|bool - { - parent::trigger(); - $this->metrics = []; - return true; - } -} diff --git a/src/Appwrite/Event/UsageDump.php b/src/Appwrite/Event/UsageDump.php deleted file mode 100644 index a70716e94f..0000000000 --- a/src/Appwrite/Event/UsageDump.php +++ /dev/null @@ -1,44 +0,0 @@ -setQueue(Event::USAGE_DUMP_QUEUE_NAME) - ->setClass(Event::USAGE_DUMP_CLASS_NAME); - } - - /** - * Add Stats. - * - * @param array $stats - * @return self - */ - public function setStats(array $stats): self - { - $this->stats = $stats; - - return $this; - } - - /** - * Prepare the payload for the usage dump event. - * - * @return array - */ - protected function preparePayload(): array - { - return [ - 'stats' => $this->stats, - ]; - } -} From a74c6e0b10368c16977e576e90fef76be0a9e2cc Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Tue, 18 Feb 2025 07:58:56 +0000 Subject: [PATCH 021/143] chore: added targets to realtime, refactor webhook queues --- app/controllers/api/functions.php | 22 ++++----- src/Appwrite/Event/Event.php | 45 +++++++++---------- src/Appwrite/Event/Realtime.php | 38 +++++++++++----- src/Appwrite/Platform/Workers/Builds.php | 30 +++++++------ .../Platform/Workers/Certificates.php | 33 +++++++------- src/Appwrite/Platform/Workers/Databases.php | 2 +- src/Appwrite/Platform/Workers/Functions.php | 22 ++++----- src/Appwrite/Platform/Workers/Migrations.php | 10 +---- 8 files changed, 103 insertions(+), 99 deletions(-) diff --git a/app/controllers/api/functions.php b/app/controllers/api/functions.php index 0f9f5211f4..644aee4336 100644 --- a/app/controllers/api/functions.php +++ b/app/controllers/api/functions.php @@ -9,6 +9,7 @@ use Appwrite\Event\Func; use Appwrite\Event\Realtime; use Appwrite\Event\StatsUsage; use Appwrite\Event\Validator\FunctionEvent; +use Appwrite\Event\Webhook; use Appwrite\Extend\Exception; use Appwrite\Extend\Exception as AppwriteException; use Appwrite\Functions\Validator\Headers; @@ -194,10 +195,12 @@ App::post('/v1/functions') ->inject('user') ->inject('queueForEvents') ->inject('queueForBuilds') + ->inject('queueForWebhooks') + ->inject('queueForFunctions') ->inject('queueForRealtime') ->inject('dbForPlatform') ->inject('gitHub') - ->action(function (string $functionId, string $name, string $runtime, array $execute, array $events, string $schedule, int $timeout, bool $enabled, bool $logging, string $entrypoint, string $commands, array $scopes, string $installationId, string $providerRepositoryId, string $providerBranch, bool $providerSilentMode, string $providerRootDirectory, string $templateRepository, string $templateOwner, string $templateRootDirectory, string $templateVersion, string $specification, Request $request, Response $response, Database $dbForProject, callable $timelimit, Document $project, Document $user, Event $queueForEvents, Build $queueForBuilds, Realtime $queueForRealtime, Database $dbForPlatform, GitHub $github) use ($redeployVcs) { + ->action(function (string $functionId, string $name, string $runtime, array $execute, array $events, string $schedule, int $timeout, bool $enabled, bool $logging, string $entrypoint, string $commands, array $scopes, string $installationId, string $providerRepositoryId, string $providerBranch, bool $providerSilentMode, string $providerRootDirectory, string $templateRepository, string $templateOwner, string $templateRootDirectory, string $templateVersion, string $specification, Request $request, Response $response, Database $dbForProject, callable $timelimit, Document $project, Document $user, Event $queueForEvents, Build $queueForBuilds, Webhook $queueForWebhooks, Func $queueForFunctions, Realtime $queueForRealtime, Database $dbForPlatform, GitHub $github) use ($redeployVcs) { $functionId = ($functionId == 'unique()') ? ID::unique() : $functionId; // Temporary abuse check @@ -396,26 +399,19 @@ App::post('/v1/functions') ->setPayload($rule->getArrayCopy(array_keys($ruleModel->getRules()))); /** Trigger Webhook */ - $ruleCreate - ->setClass(Event::WEBHOOK_CLASS_NAME) - ->setQueue(Event::WEBHOOK_QUEUE_NAME) + $queueForWebhooks + ->from($ruleCreate) ->trigger(); /** Trigger Functions */ - $ruleCreate - ->setClass(Event::FUNCTIONS_CLASS_NAME) - ->setQueue(Event::FUNCTIONS_QUEUE_NAME) + $queueForFunctions + ->from($ruleCreate) ->trigger(); /** Trigger Realtime Events */ $queueForRealtime ->from($ruleCreate) - ->setProjectId('console') - ->trigger(); - - $queueForRealtime - ->from($ruleCreate) - ->setProjectId($project->getId()) + ->setTargets(['console', $project->getId()]) ->trigger(); } diff --git a/src/Appwrite/Event/Event.php b/src/Appwrite/Event/Event.php index 8085a836a8..2187210e34 100644 --- a/src/Appwrite/Event/Event.php +++ b/src/Appwrite/Event/Event.php @@ -62,11 +62,11 @@ class Event protected array $params = []; protected array $sensitive = []; protected array $payload = []; + protected array $targets = []; protected array $context = []; protected ?Document $project = null; protected ?Document $user = null; protected ?string $userId = null; - protected ?string $projectId = null; protected bool $paused = false; /** @@ -152,18 +152,6 @@ class Event return $this; } - /** - * Set projectId for this event. - * - * @param string $projectId - * @return self - */ - public function setProjectId(string $projectId): self - { - $this->projectId = $projectId; - return $this; - } - /** * Get project for this event. * @@ -174,16 +162,6 @@ class Event return $this->project; } - /** - * Get projectId for this event. - * - * @return ?string - */ - public function getProjectId(): ?string - { - return $this->projectId; - } - /** * Set user for this event. * @@ -255,6 +233,27 @@ class Event return $this->payload; } + /** + * Get targets for this event. + * + * @return array + */ + public function setTargets(array $targets): self + { + $this->targets = $targets; + return $this; + } + + /** + * Get targets for this event. + * + * @return array + */ + public function getTargets(): array + { + return $this->targets; + } + /** * Set context for this event. * diff --git a/src/Appwrite/Event/Realtime.php b/src/Appwrite/Event/Realtime.php index 8c302bbabf..d16057ad2c 100644 --- a/src/Appwrite/Event/Realtime.php +++ b/src/Appwrite/Event/Realtime.php @@ -53,17 +53,33 @@ class Realtime extends Event bucket: $bucket, ); - RealtimeAdapter::send( - projectId: $this->getProjectId() ?? $target['projectId'] ?? $this->getProject()->getId(), - payload: $this->getRealtimePayload(), - events: $allEvents, - channels: $target['channels'], - roles: $target['roles'], - options: [ - 'permissionsChanged' => $target['permissionsChanged'], - 'userId' => $this->getParam('userId') - ] - ); + if (!empty($this->getTargets())) { + foreach ($this->getTargets() as $targetProjectId) { + RealtimeAdapter::send( + projectId: $targetProjectId, + payload: $this->getRealtimePayload(), + events: $allEvents, + channels: $target['channels'], + roles: $target['roles'], + options: [ + 'permissionsChanged' => $target['permissionsChanged'], + 'userId' => $this->getParam('userId') + ] + ); + } + } else { + RealtimeAdapter::send( + projectId: $target['projectId'] ?? $this->getProject()->getId(), + payload: $this->getRealtimePayload(), + events: $allEvents, + channels: $target['channels'], + roles: $target['roles'], + options: [ + 'permissionsChanged' => $target['permissionsChanged'], + 'userId' => $this->getParam('userId') + ] + ); + } return true; } diff --git a/src/Appwrite/Platform/Workers/Builds.php b/src/Appwrite/Platform/Workers/Builds.php index 0bc0937da0..8120adccb6 100644 --- a/src/Appwrite/Platform/Workers/Builds.php +++ b/src/Appwrite/Platform/Workers/Builds.php @@ -7,6 +7,7 @@ use Appwrite\Event\Event; use Appwrite\Event\Func; use Appwrite\Event\Realtime; use Appwrite\Event\StatsUsage; +use Appwrite\Event\Webhook; use Appwrite\Utopia\Response\Model\Deployment; use Appwrite\Vcs\Comment; use Exception; @@ -49,6 +50,7 @@ class Builds extends Action ->inject('project') ->inject('dbForPlatform') ->inject('queueForEvents') + ->inject('queueForWebhooks') ->inject('queueForFunctions') ->inject('queueForRealtime') ->inject('queueForStatsUsage') @@ -57,8 +59,8 @@ class Builds extends Action ->inject('deviceForFunctions') ->inject('isResourceBlocked') ->inject('log') - ->callback(fn ($message, Document $project, Database $dbForPlatform, Event $queueForEvents, Func $queueForFunctions, Realtime $queueForRealtime, StatsUsage $usage, Cache $cache, Database $dbForProject, Device $deviceForFunctions, callable $isResourceBlocked, Log $log) => - $this->action($message, $project, $dbForPlatform, $queueForEvents, $queueForFunctions, $queueForRealtime, $usage, $cache, $dbForProject, $deviceForFunctions, $isResourceBlocked, $log)); + ->callback(fn ($message, Document $project, Database $dbForPlatform, Event $queueForEvents, Webhook $queueForWebhooks, Func $queueForFunctions, Realtime $queueForRealtime, StatsUsage $usage, Cache $cache, Database $dbForProject, Device $deviceForFunctions, callable $isResourceBlocked, Log $log) => + $this->action($message, $project, $dbForPlatform, $queueForEvents, $queueForWebhooks, $queueForFunctions, $queueForRealtime, $usage, $cache, $dbForProject, $deviceForFunctions, $isResourceBlocked, $log)); } /** @@ -66,6 +68,7 @@ class Builds extends Action * @param Document $project * @param Database $dbForPlatform * @param Event $queueForEvents + * @param Webhook $queueForWebhooks * @param Func $queueForFunctions * @param Realtime $queueForRealtime * @param StatsUsage $queueForStatsUsage @@ -76,7 +79,7 @@ class Builds extends Action * @return void * @throws \Utopia\Database\Exception */ - public function action(Message $message, Document $project, Database $dbForPlatform, Event $queueForEvents, Func $queueForFunctions, Realtime $queueForRealtime, StatsUsage $queueForStatsUsage, Cache $cache, Database $dbForProject, Device $deviceForFunctions, callable $isResourceBlocked, Log $log): void + public function action(Message $message, Document $project, Database $dbForPlatform, Event $queueForEvents, Webhook $queueForWebhooks, Func $queueForFunctions, Realtime $queueForRealtime, StatsUsage $queueForStatsUsage, Cache $cache, Database $dbForProject, Device $deviceForFunctions, callable $isResourceBlocked, Log $log): void { $payload = $message->getPayload() ?? []; @@ -97,7 +100,7 @@ class Builds extends Action case BUILD_TYPE_RETRY: Console::info('Creating build for deployment: ' . $deployment->getId()); $github = new GitHub($cache); - $this->buildDeployment($deviceForFunctions, $queueForFunctions, $queueForRealtime, $queueForEvents, $queueForStatsUsage, $dbForPlatform, $dbForProject, $github, $project, $resource, $deployment, $template, $isResourceBlocked, $log); + $this->buildDeployment($deviceForFunctions, $queueForWebhooks, $queueForFunctions, $queueForRealtime, $queueForEvents, $queueForStatsUsage, $dbForPlatform, $dbForProject, $github, $project, $resource, $deployment, $template, $isResourceBlocked, $log); break; default: @@ -107,6 +110,7 @@ class Builds extends Action /** * @param Device $deviceForFunctions + * @param Webhook $queueForWebhooks * @param Func $queueForFunctions * @param Realtime $queueForRealtime * @param Event $queueForEvents @@ -123,7 +127,7 @@ class Builds extends Action * @throws \Utopia\Database\Exception * @throws Exception */ - protected function buildDeployment(Device $deviceForFunctions, Func $queueForFunctions, Realtime $queueForRealtime, Event $queueForEvents, StatsUsage $queueForStatsUsage, Database $dbForPlatform, Database $dbForProject, GitHub $github, Document $project, Document $function, Document $deployment, Document $template, callable $isResourceBlocked, Log $log): void + protected function buildDeployment(Device $deviceForFunctions, Webhooks $queueForWebhooks, Func $queueForFunctions, Realtime $queueForRealtime, Event $queueForEvents, StatsUsage $queueForStatsUsage, Database $dbForPlatform, Database $dbForProject, GitHub $github, Document $project, Document $function, Document $deployment, Document $template, callable $isResourceBlocked, Log $log): void { $executor = new Executor(System::getEnv('_APP_EXECUTOR_HOST')); @@ -379,7 +383,7 @@ class Builds extends Action */ $queueForRealtime ->setProject($project) - ->setProjectId('console') + ->setTargets(['console']) ->setEvent($event) ->setParam('functionId', $function->getId()) ->setParam('deploymentId', $deployment->getId()) @@ -431,19 +435,19 @@ class Builds extends Action $this->runGitAction('building', $github, $providerCommitHash, $owner, $repositoryName, $project, $function, $deployment->getId(), $dbForProject, $dbForPlatform); } - /** Trigger Webhook */ $deploymentModel = new Deployment(); $deploymentUpdate = $queueForEvents - ->setQueue(Event::WEBHOOK_QUEUE_NAME) - ->setClass(Event::WEBHOOK_CLASS_NAME) ->setProject($project) ->setEvent('functions.[functionId].deployments.[deploymentId].update') ->setParam('functionId', $function->getId()) ->setParam('deploymentId', $deployment->getId()) ->setPayload($deployment->getArrayCopy(array_keys($deploymentModel->getRules()))); - $deploymentUpdate->trigger(); + /** Trigger Webhook */ + $queueForWebhooks + ->from($deploymentUpdate) + ->trigger(); /** Trigger Functions */ $queueForFunctions @@ -453,7 +457,7 @@ class Builds extends Action /** Trigger Realtime Event */ $queueForRealtime ->setProject($project) - ->setProjectId('console') + ->setTargets(['console']) ->setEvent($event) ->setParam('functionId', $function->getId()) ->setParam('deploymentId', $deployment->getId()) @@ -585,7 +589,7 @@ class Builds extends Action */ $queueForRealtime ->setProject($project) - ->setProjectId('console') + ->setTargets(['console']) ->setEvent($event) ->setParam('functionId', $function->getId()) ->setParam('deploymentId', $deployment->getId()) @@ -682,7 +686,7 @@ class Builds extends Action */ $queueForRealtime ->setProject($project) - ->setProjectId('console') + ->setTargets(['console']) ->setEvent($event) ->setParam('functionId', $function->getId()) ->setParam('deploymentId', $deployment->getId()) diff --git a/src/Appwrite/Platform/Workers/Certificates.php b/src/Appwrite/Platform/Workers/Certificates.php index c7f345f77a..009ac24021 100644 --- a/src/Appwrite/Platform/Workers/Certificates.php +++ b/src/Appwrite/Platform/Workers/Certificates.php @@ -7,6 +7,7 @@ use Appwrite\Event\Event; use Appwrite\Event\Func; use Appwrite\Event\Mail; use Appwrite\Event\Realtime; +use Appwrite\Event\Webhook; use Appwrite\Network\Validator\CNAME; use Appwrite\Template\Template; use Appwrite\Utopia\Response\Model\Rule; @@ -46,13 +47,14 @@ class Certificates extends Action ->inject('dbForPlatform') ->inject('queueForMails') ->inject('queueForEvents') + ->inject('queueForWebhooks') ->inject('queueForFunctions') ->inject('queueForRealtime') ->inject('log') ->inject('certificates') ->callback( - fn (Message $message, Database $dbForPlatform, Mail $queueForMails, Event $queueForEvents, Func $queueForFunctions, Realtime $queueForRealtime, Log $log, CertificatesAdapter $certificates) => - $this->action($message, $dbForPlatform, $queueForMails, $queueForEvents, $queueForFunctions, $queueForRealtime, $log, $certificates) + fn (Message $message, Database $dbForPlatform, Mail $queueForMails, Event $queueForEvents, Webhook $queueForWebhooks, Func $queueForFunctions, Realtime $queueForRealtime, Log $log, CertificatesAdapter $certificates) => + $this->action($message, $dbForPlatform, $queueForMails, $queueForEvents, $queueForWebhooks, $queueForFunctions, $queueForRealtime, $log, $certificates) ); } @@ -61,6 +63,7 @@ class Certificates extends Action * @param Database $dbForPlatform * @param Mail $queueForMails * @param Event $queueForEvents + * @param Webhook $queueForWebhooks * @param Func $queueForFunctions * @param Realtime $queueForRealtime * @param Log $log @@ -69,7 +72,7 @@ class Certificates extends Action * @throws Throwable * @throws \Utopia\Database\Exception */ - public function action(Message $message, Database $dbForPlatform, Mail $queueForMails, Event $queueForEvents, Func $queueForFunctions, Realtime $queueForRealtime, Log $log, CertificatesAdapter $certificates): void + public function action(Message $message, Database $dbForPlatform, Mail $queueForMails, Event $queueForEvents, Webhook $queueForWebhooks, Func $queueForFunctions, Realtime $queueForRealtime, Log $log, CertificatesAdapter $certificates): void { $payload = $message->getPayload() ?? []; @@ -83,7 +86,7 @@ class Certificates extends Action $log->addTag('domain', $domain->get()); - $this->execute($domain, $dbForPlatform, $queueForMails, $queueForEvents, $queueForFunctions, $queueForRealtime, $log, $certificates, $skipRenewCheck); + $this->execute($domain, $dbForPlatform, $queueForMails, $queueForEvents, $queueForWebhooks, $queueForFunctions, $queueForRealtime, $log, $certificates, $skipRenewCheck); } /** @@ -99,7 +102,7 @@ class Certificates extends Action * @throws Throwable * @throws \Utopia\Database\Exception */ - private function execute(Domain $domain, Database $dbForPlatform, Mail $queueForMails, Event $queueForEvents, Func $queueForFunctions, Realtime $queueForRealtime, Log $log, CertificatesAdapter $certificates, bool $skipRenewCheck = false): void + private function execute(Domain $domain, Database $dbForPlatform, Mail $queueForMails, Event $queueForEvents, Webhook $queueForWebhooks, Func $queueForFunctions, Realtime $queueForRealtime, Log $log, CertificatesAdapter $certificates, bool $skipRenewCheck = false): void { /** * 1. Read arguments and validate domain @@ -189,7 +192,7 @@ class Certificates extends Action $certificate->setAttribute('updated', DateTime::now()); // Save all changes we made to certificate document into database - $this->saveCertificateDocument($domain->get(), $certificate, $success, $dbForPlatform, $queueForEvents, $queueForFunctions, $queueForRealtime); + $this->saveCertificateDocument($domain->get(), $certificate, $success, $dbForPlatform, $queueForEvents, $queueForWebhooks, $queueForFunctions, $queueForRealtime); } } @@ -209,7 +212,7 @@ class Certificates extends Action * @throws Conflict * @throws Structure */ - private function saveCertificateDocument(string $domain, Document $certificate, bool $success, Database $dbForPlatform, Event $queueForEvents, Func $queueForFunctions, Realtime $queueForRealtime): void + private function saveCertificateDocument(string $domain, Document $certificate, bool $success, Database $dbForPlatform, Event $queueForEvents, Webhook $queueForWebhooks, Func $queueForFunctions, Realtime $queueForRealtime): void { // Check if update or insert required $certificateDocument = $dbForPlatform->findOne('certificates', [Query::equal('domain', [$domain])]); @@ -223,7 +226,7 @@ class Certificates extends Action } $certificateId = $certificate->getId(); - $this->updateDomainDocuments($certificateId, $domain, $success, $dbForPlatform, $queueForEvents, $queueForFunctions, $queueForRealtime); + $this->updateDomainDocuments($certificateId, $domain, $success, $dbForPlatform, $queueForEvents, $queueForWebhooks, $queueForFunctions, $queueForRealtime); } /** @@ -342,7 +345,7 @@ class Certificates extends Action * * @return void */ - private function updateDomainDocuments(string $certificateId, string $domain, bool $success, Database $dbForPlatform, Event $queueForEvents, Func $queueForFunctions, Realtime $queueForRealtime): void + private function updateDomainDocuments(string $certificateId, string $domain, bool $success, Database $dbForPlatform, Event $queueForEvents, Webhook $queueForWebhooks, Func $queueForFunctions, Realtime $queueForRealtime): void { // TODO: @christyjacob remove once we migrate the rules in 1.7.x if (System::getEnv('_APP_RULES_FORMAT') === 'md5') { @@ -379,9 +382,8 @@ class Certificates extends Action ->setPayload($rule->getArrayCopy(array_keys($ruleModel->getRules()))); /** Trigger Webhook */ - $queueForEvents - ->setQueue(Event::WEBHOOK_QUEUE_NAME) - ->setClass(Event::WEBHOOK_CLASS_NAME) + $queueForWebhooks + ->from($queueForEvents) ->trigger(); /** Trigger Functions */ @@ -392,12 +394,7 @@ class Certificates extends Action /** Trigger Realtime Events */ $queueForRealtime ->from($queueForEvents) - ->setProjectId('console') - ->trigger(); - - $queueForRealtime - ->from($queueForEvents) - ->setProjectId($project->getId()) + ->setTargets(['console', $projectId]) ->trigger(); } } diff --git a/src/Appwrite/Platform/Workers/Databases.php b/src/Appwrite/Platform/Workers/Databases.php index 7d58521b82..ae75a74deb 100644 --- a/src/Appwrite/Platform/Workers/Databases.php +++ b/src/Appwrite/Platform/Workers/Databases.php @@ -604,7 +604,7 @@ class Databases extends Action ): void { $queueForRealtime ->setProject($project) - ->setProjectId('console') + ->setTargets(['console']) ->setEvent($event) ->setParam('databaseId', $database->getId()) ->setParam('collectionId', $collection->getId()); diff --git a/src/Appwrite/Platform/Workers/Functions.php b/src/Appwrite/Platform/Workers/Functions.php index f485db8648..73b02be85e 100644 --- a/src/Appwrite/Platform/Workers/Functions.php +++ b/src/Appwrite/Platform/Workers/Functions.php @@ -7,6 +7,7 @@ use Appwrite\Event\Event; use Appwrite\Event\Func; use Appwrite\Event\Realtime; use Appwrite\Event\StatsUsage; +use Appwrite\Event\Webhook; use Appwrite\Utopia\Response\Model\Execution; use Exception; use Executor\Executor; @@ -44,16 +45,17 @@ class Functions extends Action ->inject('project') ->inject('message') ->inject('dbForProject') + ->inject('queueForWebhooks') ->inject('queueForFunctions') ->inject('queueForRealtime') ->inject('queueForEvents') ->inject('queueForStatsUsage') ->inject('log') ->inject('isResourceBlocked') - ->callback(fn (Document $project, Message $message, Database $dbForProject, Func $queueForFunctions, Realtime $queueForRealtime, Event $queueForEvents, StatsUsage $queueForStatsUsage, Log $log, callable $isResourceBlocked) => $this->action($project, $message, $dbForProject, $queueForFunctions, $queueForRealtime, $queueForEvents, $queueForStatsUsage, $log, $isResourceBlocked)); + ->callback(fn (Document $project, Message $message, Database $dbForProject, Webhook $queueForWebhooks, Func $queueForFunctions, Realtime $queueForRealtime, Event $queueForEvents, StatsUsage $queueForStatsUsage, Log $log, callable $isResourceBlocked) => $this->action($project, $message, $dbForProject, $queueForWebhooks, $queueForFunctions, $queueForRealtime, $queueForEvents, $queueForStatsUsage, $log, $isResourceBlocked)); } - public function action(Document $project, Message $message, Database $dbForProject, Func $queueForFunctions, Realtime $queueForRealtime, Event $queueForEvents, StatsUsage $queueForStatsUsage, Log $log, callable $isResourceBlocked): void + public function action(Document $project, Message $message, Database $dbForProject, Webhook $queueForWebhooks, Func $queueForFunctions, Realtime $queueForRealtime, Event $queueForEvents, StatsUsage $queueForStatsUsage, Log $log, callable $isResourceBlocked): void { $payload = $message->getPayload() ?? []; @@ -137,6 +139,7 @@ class Functions extends Action $this->execute( log: $log, dbForProject: $dbForProject, + queueForWebhooks: $queueForWebhooks, queueForFunctions: $queueForFunctions, queueForRealtime: $queueForRealtime, queueForStatsUsage: $queueForStatsUsage, @@ -178,6 +181,7 @@ class Functions extends Action $this->execute( log: $log, dbForProject: $dbForProject, + queueForWebhooks: $queueForWebhooks, queueForFunctions: $queueForFunctions, queueForRealtime: $queueForRealtime, queueForStatsUsage: $queueForStatsUsage, @@ -201,6 +205,7 @@ class Functions extends Action $this->execute( log: $log, dbForProject: $dbForProject, + queueForWebhooks: $queueForWebhooks, queueForFunctions: $queueForFunctions, queueForRealtime: $queueForRealtime, queueForStatsUsage: $queueForStatsUsage, @@ -312,6 +317,7 @@ class Functions extends Action private function execute( Log $log, Database $dbForProject, + Webhook $queueForWebhooks, Func $queueForFunctions, Realtime $queueForRealtime, StatsUsage $queueForStatsUsage, @@ -582,9 +588,8 @@ class Functions extends Action ->setPayload($execution->getArrayCopy(array_keys($executionModel->getRules()))); /** Trigger Webhook */ - $queueForEvents - ->setQueue(Event::WEBHOOK_QUEUE_NAME) - ->setClass(Event::WEBHOOK_CLASS_NAME) + $queueForWebhooks + ->from($queueForEvents) ->trigger(); /** Trigger Functions */ @@ -595,12 +600,7 @@ class Functions extends Action /** Trigger Realtime Events */ $queueForRealtime ->from($queueForEvents) - ->setProjectId('console') - ->trigger(); - - $queueForRealtime - ->from($queueForEvents) - ->setProjectId($project->getId()) + ->setTargets(['console', $project->getId()]) ->trigger(); if (!empty($error)) { diff --git a/src/Appwrite/Platform/Workers/Migrations.php b/src/Appwrite/Platform/Workers/Migrations.php index 50d6002d28..541c171a22 100644 --- a/src/Appwrite/Platform/Workers/Migrations.php +++ b/src/Appwrite/Platform/Workers/Migrations.php @@ -160,15 +160,7 @@ class Migrations extends Action /** Trigger Realtime Events */ $queueForRealtime ->setProject($project) - ->setProjectId('console') - ->setEvent('migrations.[migrationId].update') - ->setParam('migrationId', $migration->getId()) - ->setPayload($migration->getArrayCopy()) - ->trigger(); - - $queueForRealtime - ->setProject($project) - ->setProjectId($project->getId()) + ->setTargets(['console', $project->getId()]) ->setEvent('migrations.[migrationId].update') ->setParam('migrationId', $migration->getId()) ->setPayload($migration->getArrayCopy()) From 235a357ca3aa22876881840d262ca34853473331 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Tue, 18 Feb 2025 11:37:07 +0000 Subject: [PATCH 022/143] chore: initialized queueForWebhooks in worker init --- app/worker.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/worker.php b/app/worker.php index ad6bf475f9..1088a9b6db 100644 --- a/app/worker.php +++ b/app/worker.php @@ -20,6 +20,7 @@ use Appwrite\Event\StatsUsageDump; use Appwrite\Event\Usage; use Appwrite\Event\UsageDump; /** /remove */ +use Appwrite\Event\Webhook; use Appwrite\Platform\Appwrite; use Swoole\Runtime; use Utopia\Abuse\Adapters\TimeLimit\Redis as TimeLimitRedis; @@ -314,6 +315,10 @@ Server::setResource('queueForAudits', function (Publisher $publisher) { return new Audit($publisher); }, ['publisher']); +Server::setResource('queueForWebhooks', function (Publisher $publisher) { + return new Webhook($publisher); +}, ['publisher']); + Server::setResource('queueForFunctions', function (Publisher $publisher) { return new Func($publisher); }, ['publisher']); From 9f7aaf702913548ce52cc7311973f0ba7de97f03 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Tue, 18 Feb 2025 12:51:19 +0000 Subject: [PATCH 023/143] chore: fix warning in database worker --- src/Appwrite/Event/Event.php | 3 ++- src/Appwrite/Platform/Workers/Databases.php | 28 ++++++++++----------- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/src/Appwrite/Event/Event.php b/src/Appwrite/Event/Event.php index 2187210e34..7e15cabca9 100644 --- a/src/Appwrite/Event/Event.php +++ b/src/Appwrite/Event/Event.php @@ -234,8 +234,9 @@ class Event } /** - * Get targets for this event. + * Set targets for this event. * + * @param array $targets * @return array */ public function setTargets(array $targets): self diff --git a/src/Appwrite/Platform/Workers/Databases.php b/src/Appwrite/Platform/Workers/Databases.php index ae75a74deb..b85285ae72 100644 --- a/src/Appwrite/Platform/Workers/Databases.php +++ b/src/Appwrite/Platform/Workers/Databases.php @@ -197,7 +197,7 @@ class Databases extends Action throw $e; } finally { - $this->trigger($database, $collection, $project, $attribute, null, $attribute, $event, $queueForRealtime); + $this->trigger($database, $collection, $project, $event, $queueForRealtime, $attribute); if (! $relatedCollection->isEmpty()) { $dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $relatedCollection->getId()); @@ -304,7 +304,7 @@ class Databases extends Action throw $e; } finally { - $this->trigger($database, $collection, $project, $attribute, null, $attribute, $event, $queueForRealtime); + $this->trigger($database, $collection, $project, $event, $queueForRealtime, $attribute); } // The underlying database removes/rebuilds indexes when attribute is removed @@ -418,7 +418,7 @@ class Databases extends Action throw $e; } finally { - $this->trigger($database, $collection, $project, null, $index, $index, $event, $queueForRealtime); + $this->trigger($database, $collection, $project, $event, $queueForRealtime, null, $index); $dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $collectionId); } } @@ -474,7 +474,7 @@ class Databases extends Action throw $e; } finally { - $this->trigger($database, $collection, $project, null, $index, $index, $event, $queueForRealtime); + $this->trigger($database, $collection, $project, $event, $queueForRealtime, null, $index); $dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $collection->getId()); } } @@ -586,21 +586,19 @@ class Databases extends Action * @param Document $database * @param Document $collection * @param Document $project + * @param Realtime $queueForRealtime * @param Document|null $attribute * @param Document|null $index - * @param Document $payload - * @param Realtime $queueForRealtime * @return void */ protected function trigger( Document $database, Document $collection, Document $project, + string $event, + Realtime $queueForRealtime, Document|null $attribute = null, Document|null $index = null, - Document $payload, - string $event, - Realtime $queueForRealtime ): void { $queueForRealtime ->setProject($project) @@ -610,14 +608,16 @@ class Databases extends Action ->setParam('collectionId', $collection->getId()); if ($attribute !== null) { - $queueForRealtime->setParam('attributeId', $attribute->getId()); + $queueForRealtime + ->setParam('attributeId', $attribute->getId()) + ->setPayload($attribute->getArrayCopy()); } if ($index !== null) { - $queueForRealtime->setParam('indexId', $index->getId()); + $queueForRealtime + ->setParam('indexId', $index->getId()) + ->setPayload($index->getArrayCopy()); } - $queueForRealtime - ->setPayload($payload->getArrayCopy()) - ->trigger(); + $queueForRealtime->trigger(); } } From 88a6616cf8572a6a1132b1527dfde654cbb8a819 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Tue, 18 Feb 2025 13:05:50 +0000 Subject: [PATCH 024/143] chore: fix naming --- src/Appwrite/Platform/Workers/Builds.php | 2 +- src/Appwrite/Platform/Workers/Databases.php | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Appwrite/Platform/Workers/Builds.php b/src/Appwrite/Platform/Workers/Builds.php index 8120adccb6..c3853a1dde 100644 --- a/src/Appwrite/Platform/Workers/Builds.php +++ b/src/Appwrite/Platform/Workers/Builds.php @@ -127,7 +127,7 @@ class Builds extends Action * @throws \Utopia\Database\Exception * @throws Exception */ - protected function buildDeployment(Device $deviceForFunctions, Webhooks $queueForWebhooks, Func $queueForFunctions, Realtime $queueForRealtime, Event $queueForEvents, StatsUsage $queueForStatsUsage, Database $dbForPlatform, Database $dbForProject, GitHub $github, Document $project, Document $function, Document $deployment, Document $template, callable $isResourceBlocked, Log $log): void + protected function buildDeployment(Device $deviceForFunctions, Webhook $queueForWebhooks, Func $queueForFunctions, Realtime $queueForRealtime, Event $queueForEvents, StatsUsage $queueForStatsUsage, Database $dbForPlatform, Database $dbForProject, GitHub $github, Document $project, Document $function, Document $deployment, Document $template, callable $isResourceBlocked, Log $log): void { $executor = new Executor(System::getEnv('_APP_EXECUTOR_HOST')); diff --git a/src/Appwrite/Platform/Workers/Databases.php b/src/Appwrite/Platform/Workers/Databases.php index b85285ae72..74101dd1eb 100644 --- a/src/Appwrite/Platform/Workers/Databases.php +++ b/src/Appwrite/Platform/Workers/Databases.php @@ -516,7 +516,6 @@ class Databases extends Action $collectionId = $collection->getId(); $collectionInternalId = $collection->getInternalId(); - $databaseId = $database->getId(); $databaseInternalId = $database->getInternalId(); $dbForProject->deleteCollection('database_' . $databaseInternalId . '_collection_' . $collection->getInternalId()); From 55e7633bd12c2631f03f9bc75e5967e1b600f0cd Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Tue, 18 Feb 2025 14:04:59 +0000 Subject: [PATCH 025/143] chore: shift targets to realtime --- src/Appwrite/Event/Event.php | 23 ----------------------- src/Appwrite/Event/Realtime.php | 29 +++++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 23 deletions(-) diff --git a/src/Appwrite/Event/Event.php b/src/Appwrite/Event/Event.php index 7e15cabca9..0edffdf4dc 100644 --- a/src/Appwrite/Event/Event.php +++ b/src/Appwrite/Event/Event.php @@ -62,7 +62,6 @@ class Event protected array $params = []; protected array $sensitive = []; protected array $payload = []; - protected array $targets = []; protected array $context = []; protected ?Document $project = null; protected ?Document $user = null; @@ -233,28 +232,6 @@ class Event return $this->payload; } - /** - * Set targets for this event. - * - * @param array $targets - * @return array - */ - public function setTargets(array $targets): self - { - $this->targets = $targets; - return $this; - } - - /** - * Get targets for this event. - * - * @return array - */ - public function getTargets(): array - { - return $this->targets; - } - /** * Set context for this event. * diff --git a/src/Appwrite/Event/Realtime.php b/src/Appwrite/Event/Realtime.php index d16057ad2c..e04bbfffb4 100644 --- a/src/Appwrite/Event/Realtime.php +++ b/src/Appwrite/Event/Realtime.php @@ -7,10 +7,17 @@ use Utopia\Database\Document; class Realtime extends Event { + protected array $targets = []; + public function __construct() { } + /** + * Get Realtime payload for this event. + * + * @return array + */ public function getRealtimePayload(): array { $payload = []; @@ -24,6 +31,28 @@ class Realtime extends Event return $payload; } + /** + * Set targets for this realtime event. + * + * @param array $targets + * @return array + */ + public function setTargets(array $targets): self + { + $this->targets = $targets; + return $this; + } + + /** + * Get targets for this realtime event. + * + * @return array + */ + public function getTargets(): array + { + return $this->targets; + } + /** * Execute Event. * From 6235a1c729627302bb991afdf419906abb848b99 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Thu, 20 Feb 2025 10:33:58 +0000 Subject: [PATCH 026/143] chore: make min/max params optional for attribute update --- app/controllers/api/databases.php | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/app/controllers/api/databases.php b/app/controllers/api/databases.php index c563d1e8e0..a4b5683512 100644 --- a/app/controllers/api/databases.php +++ b/app/controllers/api/databases.php @@ -2339,14 +2339,19 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/integ ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') ->param('key', '', new Key(), 'Attribute Key.') ->param('required', null, new Boolean(), 'Is attribute required?') - ->param('min', null, new Integer(), 'Minimum value to enforce on new documents') - ->param('max', null, new Integer(), 'Maximum value to enforce on new documents') + ->param('min', null, new Integer(), 'Minimum value to enforce on new documents', true) + ->param('max', null, new Integer(), 'Maximum value to enforce on new documents', true) ->param('default', null, new Nullable(new Integer()), 'Default value for attribute when not provided. Cannot be set when attribute is required.') ->param('newKey', null, new Key(), 'New attribute key.', true) ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?int $min, ?int $max, ?int $default, ?string $newKey, Response $response, Database $dbForProject, Event $queueForEvents) { + + // Ensure attribute default is within range + $min = \is_null($min) ? PHP_INT_MIN : $min; + $max = \is_null($max) ? PHP_INT_MAX : $max; + $attribute = updateAttribute( databaseId: $databaseId, collectionId: $collectionId, @@ -2398,14 +2403,19 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/float ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') ->param('key', '', new Key(), 'Attribute Key.') ->param('required', null, new Boolean(), 'Is attribute required?') - ->param('min', null, new FloatValidator(), 'Minimum value to enforce on new documents') - ->param('max', null, new FloatValidator(), 'Maximum value to enforce on new documents') + ->param('min', null, new FloatValidator(), 'Minimum value to enforce on new documents', true) + ->param('max', null, new FloatValidator(), 'Maximum value to enforce on new documents', true) ->param('default', null, new Nullable(new FloatValidator()), 'Default value for attribute when not provided. Cannot be set when attribute is required.') ->param('newKey', null, new Key(), 'New attribute key.', true) ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?float $min, ?float $max, ?float $default, ?string $newKey, Response $response, Database $dbForProject, Event $queueForEvents) { + + // Ensure attribute default is within range + $min = \is_null($min) ? -PHP_FLOAT_MAX : $min; + $max = \is_null($max) ? PHP_FLOAT_MAX : $max; + $attribute = updateAttribute( databaseId: $databaseId, collectionId: $collectionId, From ccb7dfa839311a78c624c516800ed1cc0d142a53 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Thu, 20 Feb 2025 11:15:05 +0000 Subject: [PATCH 027/143] chore: added test cases --- .../e2e/Services/Databases/DatabasesBase.php | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/tests/e2e/Services/Databases/DatabasesBase.php b/tests/e2e/Services/Databases/DatabasesBase.php index 0f57f94515..6a4839274c 100644 --- a/tests/e2e/Services/Databases/DatabasesBase.php +++ b/tests/e2e/Services/Databases/DatabasesBase.php @@ -464,6 +464,38 @@ trait DatabasesBase $this->assertEquals(400, $attribute['headers']['status-code']); $this->assertStringContainsString('Index length is longer than the maximum: 76', $attribute['body']['message']); + + $integer = $this->client->call(Client::METHOD_POST, '/databases/'.$databaseId.'/collections/'.$collection['body']['$id'].'/attributes/integer', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'integer', + 'required' => false, + 'min' => 1, + 'max' => 5, + 'default' => 3 + ]); + $this->assertEquals(202, $integer['headers']['status-code']); + + sleep(1); + + /** + * Update integer default value to 10 + */ + $integer = $this->client->call(Client::METHOD_PATCH, '/databases/'.$databaseId.'/collections/'.$collection['body']['$id'].'/attributes/integer/'.$integer['body']['key'], array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]), [ + 'key' => 'integer', + 'required' => false, + 'default' => 10 + ]); + + $this->assertEquals(200, $integer['headers']['status-code']); + $this->assertEquals(PHP_INT_MIN, $integer['body']['min']); + $this->assertEquals(PHP_INT_MAX, $integer['body']['max']); } public function testUpdateAttributeEnum(): void From b0ced9b351746a23c4fd976f6ff0e022e459a541 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Thu, 20 Feb 2025 13:37:13 +0000 Subject: [PATCH 028/143] chore: updated tests --- .../e2e/Services/Databases/DatabasesBase.php | 32 ------ .../Databases/DatabasesCustomServerTest.php | 102 +++++++++--------- 2 files changed, 50 insertions(+), 84 deletions(-) diff --git a/tests/e2e/Services/Databases/DatabasesBase.php b/tests/e2e/Services/Databases/DatabasesBase.php index 6a4839274c..0f57f94515 100644 --- a/tests/e2e/Services/Databases/DatabasesBase.php +++ b/tests/e2e/Services/Databases/DatabasesBase.php @@ -464,38 +464,6 @@ trait DatabasesBase $this->assertEquals(400, $attribute['headers']['status-code']); $this->assertStringContainsString('Index length is longer than the maximum: 76', $attribute['body']['message']); - - $integer = $this->client->call(Client::METHOD_POST, '/databases/'.$databaseId.'/collections/'.$collection['body']['$id'].'/attributes/integer', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'] - ]), [ - 'key' => 'integer', - 'required' => false, - 'min' => 1, - 'max' => 5, - 'default' => 3 - ]); - $this->assertEquals(202, $integer['headers']['status-code']); - - sleep(1); - - /** - * Update integer default value to 10 - */ - $integer = $this->client->call(Client::METHOD_PATCH, '/databases/'.$databaseId.'/collections/'.$collection['body']['$id'].'/attributes/integer/'.$integer['body']['key'], array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'], - ]), [ - 'key' => 'integer', - 'required' => false, - 'default' => 10 - ]); - - $this->assertEquals(200, $integer['headers']['status-code']); - $this->assertEquals(PHP_INT_MIN, $integer['body']['min']); - $this->assertEquals(PHP_INT_MAX, $integer['body']['max']); } public function testUpdateAttributeEnum(): void diff --git a/tests/e2e/Services/Databases/DatabasesCustomServerTest.php b/tests/e2e/Services/Databases/DatabasesCustomServerTest.php index b501e2119e..ac1df82c9d 100644 --- a/tests/e2e/Services/Databases/DatabasesCustomServerTest.php +++ b/tests/e2e/Services/Databases/DatabasesCustomServerTest.php @@ -2309,6 +2309,30 @@ class DatabasesCustomServerTest extends Scope $this->assertEquals(100, $new['body']['min']); $this->assertEquals(2000, $new['body']['max']); + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/integer/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'required' => false, + 'default' => 100, + 'min' => 0, + ]); + + $this->assertEquals(200, $update['headers']['status-code']); + + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/integer/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'required' => false, + 'default' => 100, + 'max' => 0, + ]); + + $this->assertEquals(200, $update['headers']['status-code']); + /** * Test against failure */ @@ -2368,32 +2392,6 @@ class DatabasesCustomServerTest extends Scope $this->assertEquals(400, $update['headers']['status-code']); $this->assertEquals(AppwriteException::GENERAL_ARGUMENT_INVALID, $update['body']['type']); - $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/integer/' . $key, array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'] - ]), [ - 'required' => false, - 'default' => 100, - 'min' => 0, - ]); - - $this->assertEquals(400, $update['headers']['status-code']); - $this->assertEquals(AppwriteException::GENERAL_ARGUMENT_INVALID, $update['body']['type']); - - $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/integer/' . $key, array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'] - ]), [ - 'required' => false, - 'default' => 100, - 'max' => 0, - ]); - - $this->assertEquals(400, $update['headers']['status-code']); - $this->assertEquals(AppwriteException::GENERAL_ARGUMENT_INVALID, $update['body']['type']); - $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/integer/' . $key, array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -2572,6 +2570,32 @@ class DatabasesCustomServerTest extends Scope $this->assertEquals(123.456, $new['body']['min']); $this->assertEquals(2000, $new['body']['max']); + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/float/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'required' => false, + 'default' => 123.456, + 'min' => 0.0, + ]); + + $this->assertEquals(200, $update['headers']['status-code']); + $this->assertEquals(AppwriteException::GENERAL_ARGUMENT_INVALID, $update['body']['type']); + + $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/float/' . $key, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'required' => false, + 'default' => 123.456, + 'max' => 0.0, + ]); + + $this->assertEquals(200, $update['headers']['status-code']); + $this->assertEquals(AppwriteException::GENERAL_ARGUMENT_INVALID, $update['body']['type']); + /** * Test against failure */ @@ -2631,32 +2655,6 @@ class DatabasesCustomServerTest extends Scope $this->assertEquals(400, $update['headers']['status-code']); $this->assertEquals(AppwriteException::GENERAL_ARGUMENT_INVALID, $update['body']['type']); - $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/float/' . $key, array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'] - ]), [ - 'required' => false, - 'default' => 123.456, - 'min' => 0.0, - ]); - - $this->assertEquals(400, $update['headers']['status-code']); - $this->assertEquals(AppwriteException::GENERAL_ARGUMENT_INVALID, $update['body']['type']); - - $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/float/' . $key, array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'] - ]), [ - 'required' => false, - 'default' => 123.456, - 'max' => 0.0, - ]); - - $this->assertEquals(400, $update['headers']['status-code']); - $this->assertEquals(AppwriteException::GENERAL_ARGUMENT_INVALID, $update['body']['type']); - $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/float/' . $key, array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], From 077fe10d808a04c2a500c6d560633b5163375429 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Thu, 20 Feb 2025 14:12:14 +0000 Subject: [PATCH 029/143] chore: fix tests --- tests/e2e/Services/Databases/DatabasesCustomServerTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/e2e/Services/Databases/DatabasesCustomServerTest.php b/tests/e2e/Services/Databases/DatabasesCustomServerTest.php index ac1df82c9d..213584c3df 100644 --- a/tests/e2e/Services/Databases/DatabasesCustomServerTest.php +++ b/tests/e2e/Services/Databases/DatabasesCustomServerTest.php @@ -2327,7 +2327,7 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ 'required' => false, - 'default' => 100, + 'default' => -10, 'max' => 0, ]); @@ -2589,7 +2589,7 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ 'required' => false, - 'default' => 123.456, + 'default' => -123.456, 'max' => 0.0, ]); From f9cc7882aa78e3ab6e66a202afdf0ebfcbbe3f5c Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Thu, 20 Feb 2025 14:43:21 +0000 Subject: [PATCH 030/143] chore: fix tests --- tests/e2e/Services/Databases/DatabasesCustomServerTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/e2e/Services/Databases/DatabasesCustomServerTest.php b/tests/e2e/Services/Databases/DatabasesCustomServerTest.php index 213584c3df..ad581eecb6 100644 --- a/tests/e2e/Services/Databases/DatabasesCustomServerTest.php +++ b/tests/e2e/Services/Databases/DatabasesCustomServerTest.php @@ -2581,7 +2581,6 @@ class DatabasesCustomServerTest extends Scope ]); $this->assertEquals(200, $update['headers']['status-code']); - $this->assertEquals(AppwriteException::GENERAL_ARGUMENT_INVALID, $update['body']['type']); $update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/float/' . $key, array_merge([ 'content-type' => 'application/json', From c660b9d858b9cb9b0e2ee85f8e5f64d30d555cb5 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Thu, 20 Feb 2025 14:56:23 +0000 Subject: [PATCH 031/143] chore: fix tests --- tests/e2e/Services/Databases/DatabasesCustomServerTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/e2e/Services/Databases/DatabasesCustomServerTest.php b/tests/e2e/Services/Databases/DatabasesCustomServerTest.php index ad581eecb6..75526b9928 100644 --- a/tests/e2e/Services/Databases/DatabasesCustomServerTest.php +++ b/tests/e2e/Services/Databases/DatabasesCustomServerTest.php @@ -2593,7 +2593,6 @@ class DatabasesCustomServerTest extends Scope ]); $this->assertEquals(200, $update['headers']['status-code']); - $this->assertEquals(AppwriteException::GENERAL_ARGUMENT_INVALID, $update['body']['type']); /** * Test against failure From b1629b24ad2fbde59a3867ae061e307f28ef2504 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Fri, 21 Feb 2025 11:44:04 +0000 Subject: [PATCH 032/143] chore: added file transformation stats to usage endpoint for bucket --- app/controllers/api/storage.php | 3 +++ src/Appwrite/Utopia/Response/Model/UsageBuckets.php | 13 +++++++++++++ 2 files changed, 16 insertions(+) diff --git a/app/controllers/api/storage.php b/app/controllers/api/storage.php index f180c22acf..efd82ba40e 100644 --- a/app/controllers/api/storage.php +++ b/app/controllers/api/storage.php @@ -1894,6 +1894,7 @@ App::get('/v1/storage/:bucketId/usage') $metrics = [ str_replace('{bucketInternalId}', $bucket->getInternalId(), METRIC_BUCKET_ID_FILES), str_replace('{bucketInternalId}', $bucket->getInternalId(), METRIC_BUCKET_ID_FILES_STORAGE), + str_replace('{bucketInternalId}', $bucket->getInternalId(), METRIC_FILES_TRANSFORMATIONS), ]; @@ -1948,5 +1949,7 @@ App::get('/v1/storage/:bucketId/usage') 'filesStorageTotal' => $usage[$metrics[1]]['total'], 'files' => $usage[$metrics[0]]['data'], 'storage' => $usage[$metrics[1]]['data'], + 'fileTransformations' => $usage[$metrics[2]]['data'], + 'fileTransformationsTotal' => $usage[$metrics[2]]['total'], ]), Response::MODEL_USAGE_BUCKETS); }); diff --git a/src/Appwrite/Utopia/Response/Model/UsageBuckets.php b/src/Appwrite/Utopia/Response/Model/UsageBuckets.php index 2f528ac9d1..ab8d129857 100644 --- a/src/Appwrite/Utopia/Response/Model/UsageBuckets.php +++ b/src/Appwrite/Utopia/Response/Model/UsageBuckets.php @@ -42,6 +42,19 @@ class UsageBuckets extends Model 'example' => [], 'array' => true ]) + ->addRule('fileTransformations', [ + 'type' => Response::MODEL_METRIC, + 'description' => 'Aggregated number of files transformations per period.', + 'default' => [], + 'example' => [], + 'array' => true + ]) + ->addRule('fileTransformationsTotal', [ + 'type' => self::TYPE_INTEGER, + 'description' => 'Total aggregated number of files transformations.', + 'default' => 0, + 'example' => 0, + ]) ; } From 4ed4c660d5aff0ee10cbdaad8ff2e15293b3ed88 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Fri, 21 Feb 2025 12:53:17 +0000 Subject: [PATCH 033/143] chore: added tests for file transformations --- tests/e2e/Services/Storage/StorageConsoleClientTest.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/e2e/Services/Storage/StorageConsoleClientTest.php b/tests/e2e/Services/Storage/StorageConsoleClientTest.php index 5b6731b35e..b3a6ad9274 100644 --- a/tests/e2e/Services/Storage/StorageConsoleClientTest.php +++ b/tests/e2e/Services/Storage/StorageConsoleClientTest.php @@ -104,5 +104,7 @@ class StorageConsoleClientTest extends Scope $this->assertIsNumeric($response['body']['filesStorageTotal']); $this->assertIsArray($response['body']['files']); $this->assertIsArray($response['body']['storage']); + $this->assertIsArray($response['body']['fileTransformations']); + $this->assertIsNumeric($response['body']['fileTransformationsTotal']); } } From 0e0bab4c40a54a310b9d07599372ec8be8d06ac1 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Fri, 21 Feb 2025 13:28:49 +0000 Subject: [PATCH 034/143] chore: updated test --- tests/e2e/Services/Storage/StorageConsoleClientTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/e2e/Services/Storage/StorageConsoleClientTest.php b/tests/e2e/Services/Storage/StorageConsoleClientTest.php index b3a6ad9274..b2624746ca 100644 --- a/tests/e2e/Services/Storage/StorageConsoleClientTest.php +++ b/tests/e2e/Services/Storage/StorageConsoleClientTest.php @@ -98,7 +98,7 @@ class StorageConsoleClientTest extends Scope ]); $this->assertEquals(200, $response['headers']['status-code']); - $this->assertEquals(5, count($response['body'])); + $this->assertEquals(7, count($response['body'])); $this->assertEquals('24h', $response['body']['range']); $this->assertIsNumeric($response['body']['filesTotal']); $this->assertIsNumeric($response['body']['filesStorageTotal']); From 65dd5fe9b3f0e8a178effc388c9287b5157f7559 Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Sun, 23 Feb 2025 11:34:28 +0530 Subject: [PATCH 035/143] feat: compute usage for OPTIONS and 4XX errors --- app/controllers/general.php | 15 +++++++++++++-- app/controllers/shared/api.php | 1 + docker-compose.yml | 14 +++++++------- 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/app/controllers/general.php b/app/controllers/general.php index 6db4a8b28d..ae6db43c70 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -737,7 +737,8 @@ App::options() ->inject('geodb') ->inject('isResourceBlocked') ->inject('previewHostname') - ->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Database $dbForPlatform, callable $getProjectDB, Event $queueForEvents, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Reader $geodb, callable $isResourceBlocked, string $previewHostname) { + ->inject('project') + ->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Database $dbForPlatform, callable $getProjectDB, Event $queueForEvents, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Reader $geodb, callable $isResourceBlocked, string $previewHostname, Document $project) { /* * Appwrite Router */ @@ -760,6 +761,16 @@ App::options() ->addHeader('Access-Control-Allow-Origin', $origin) ->addHeader('Access-Control-Allow-Credentials', 'true') ->noContent(); + + /** OPTIONS requests in utopia do not execute shutdown handlers, as a result we need to track the OPTIONS requests explicitly + * @see https://github.com/utopia-php/http/blob/0.33.16/src/App.php#L825-L855 + */ + $queueForStatsUsage + ->addMetric(METRIC_NETWORK_REQUESTS, 1) + ->addMetric(METRIC_NETWORK_INBOUND, $request->getSize()) + ->addMetric(METRIC_NETWORK_OUTBOUND, $response->getSize()) + ->setProject($project) + ->trigger(); }); App::error() @@ -874,7 +885,7 @@ App::error() } } - if ($publish && $project->getId() !== 'console') { + if ($project->getId() !== 'console') { if (!Auth::isPrivilegedUser(Authorization::getRoles())) { $fileSize = 0; $file = $request->getFiles('file'); diff --git a/app/controllers/shared/api.php b/app/controllers/shared/api.php index 7f7b73ab0c..64a9980bcb 100644 --- a/app/controllers/shared/api.php +++ b/app/controllers/shared/api.php @@ -803,6 +803,7 @@ App::shutdown() } } + var_dump("in shutdown"); if ($project->getId() !== 'console') { if (!Auth::isPrivilegedUser(Authorization::getRoles())) { $fileSize = 0; diff --git a/docker-compose.yml b/docker-compose.yml index facf0e6db9..3316e7dcf8 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -934,13 +934,13 @@ services: - _APP_DB_PASS - _APP_DATABASE_SHARED_TABLES - appwrite-assistant: - container_name: appwrite-assistant - image: appwrite/assistant:0.7.0 - networks: - - appwrite - environment: - - _APP_ASSISTANT_OPENAI_API_KEY + # appwrite-assistant: + # container_name: appwrite-assistant + # image: appwrite/assistant:0.7.0 + # networks: + # - appwrite + # environment: + # - _APP_ASSISTANT_OPENAI_API_KEY openruntimes-executor: container_name: openruntimes-executor From 12b494982ba2b016081660197dd5c76557a88521 Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Sun, 23 Feb 2025 11:37:54 +0530 Subject: [PATCH 036/143] feat: compute usage for OPTIONS and 4XX errors --- app/controllers/general.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/controllers/general.php b/app/controllers/general.php index ae6db43c70..65979d3475 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -885,7 +885,10 @@ App::error() } } - if ($project->getId() !== 'console') { + /** + * If its not a publishable error, track usage stats. Publishable errors are >= 500 or those explicitly marked as publish=true in errors.php + */ + if (!$publish && $project->getId() !== 'console') { if (!Auth::isPrivilegedUser(Authorization::getRoles())) { $fileSize = 0; $file = $request->getFiles('file'); From 2359c1adb6734afd4088de7609fa58107a30d185 Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Sun, 23 Feb 2025 11:38:59 +0530 Subject: [PATCH 037/143] feat: enable assistant --- docker-compose.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 3316e7dcf8..facf0e6db9 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -934,13 +934,13 @@ services: - _APP_DB_PASS - _APP_DATABASE_SHARED_TABLES - # appwrite-assistant: - # container_name: appwrite-assistant - # image: appwrite/assistant:0.7.0 - # networks: - # - appwrite - # environment: - # - _APP_ASSISTANT_OPENAI_API_KEY + appwrite-assistant: + container_name: appwrite-assistant + image: appwrite/assistant:0.7.0 + networks: + - appwrite + environment: + - _APP_ASSISTANT_OPENAI_API_KEY openruntimes-executor: container_name: openruntimes-executor From 30327f86fd967f92486040deb5f929d4e95bdb2c Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Sun, 23 Feb 2025 11:39:18 +0530 Subject: [PATCH 038/143] feat: enable assistant --- app/controllers/shared/api.php | 1 - 1 file changed, 1 deletion(-) diff --git a/app/controllers/shared/api.php b/app/controllers/shared/api.php index 64a9980bcb..7f7b73ab0c 100644 --- a/app/controllers/shared/api.php +++ b/app/controllers/shared/api.php @@ -803,7 +803,6 @@ App::shutdown() } } - var_dump("in shutdown"); if ($project->getId() !== 'console') { if (!Auth::isPrivilegedUser(Authorization::getRoles())) { $fileSize = 0; From cf41d44de4b299d2535a1098b6b769be5aac96a7 Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Sun, 23 Feb 2025 14:47:57 +0530 Subject: [PATCH 039/143] fix: general tests --- src/Appwrite/Platform/Workers/StatsUsage.php | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/Appwrite/Platform/Workers/StatsUsage.php b/src/Appwrite/Platform/Workers/StatsUsage.php index c4d8b0e8d2..a755f723a0 100644 --- a/src/Appwrite/Platform/Workers/StatsUsage.php +++ b/src/Appwrite/Platform/Workers/StatsUsage.php @@ -17,13 +17,20 @@ class StatsUsage extends Action private int $lastTriggeredTime = 0; private int $keys = 0; private const INFINITY_PERIOD = '_inf_'; - private const KEYS_THRESHOLD = 10000; + private const BATCH_SIZE_DEVELOPMENT = 1; + private const BATCH_SIZE_PRODUCTION = 10_000; public static function getName(): string { return 'stats-usage'; } + private function getBatchSize(): int + { + return System::getEnv('_APP_ENV', 'development') === 'development' + ? self::BATCH_SIZE_DEVELOPMENT + : self::BATCH_SIZE_PRODUCTION; + } /** * @throws Exception */ @@ -86,7 +93,7 @@ class StatsUsage extends Action // If keys crossed threshold or X time passed since the last send and there are some keys in the array ($this->stats) if ( - $this->keys >= self::KEYS_THRESHOLD || + $this->keys >= $this->getBatchSize() || (time() - $this->lastTriggeredTime > $aggregationInterval && $this->keys > 0) ) { Console::warning('[' . DateTime::now() . '] Aggregated ' . $this->keys . ' keys'); From f682e4ac6f8f04cd4b9158633b5c4c04a895cf7e Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Sun, 23 Feb 2025 14:56:15 +0530 Subject: [PATCH 040/143] chore: remoove sleep() for usage stats, since batch size is now 1 --- tests/e2e/General/UsageTest.php | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/tests/e2e/General/UsageTest.php b/tests/e2e/General/UsageTest.php index c0d0c80eb1..3e22f827bd 100644 --- a/tests/e2e/General/UsageTest.php +++ b/tests/e2e/General/UsageTest.php @@ -134,8 +134,6 @@ class UsageTest extends Scope #[Retry(count: 1)] public function testUsersStats(array $data): array { - sleep(self::WAIT); - $requestsTotal = $data['requestsTotal']; $response = $this->client->call( @@ -318,8 +316,6 @@ class UsageTest extends Scope $storageTotal = $data['storageTotal']; $filesTotal = $data['filesTotal']; - sleep(self::WAIT); - $response = $this->client->call( Client::METHOD_GET, '/project/usage', @@ -476,8 +472,6 @@ class UsageTest extends Scope $requestsTotal += 1; - sleep(self::WAIT); - for ($i = 0; $i < self::CREATE; $i++) { $name = uniqid() . ' collection'; @@ -539,8 +533,6 @@ class UsageTest extends Scope $collectionsTotal = $data['collectionsTotal']; $documentsTotal = $data['documentsTotal']; - sleep(self::WAIT); - $response = $this->client->call( Client::METHOD_GET, '/project/usage', @@ -709,8 +701,6 @@ class UsageTest extends Scope // $this->assertEquals(201, $response['headers']['status-code']); // } - // sleep(self::WAIT); - // for ($i = 0; $i < 3; $i++) { // try { // $newProjectMetrics = $this->client->call( @@ -752,7 +742,6 @@ class UsageTest extends Scope // if ($i === 2) { // throw $e; // } - // sleep(self::WAIT); // continue; // } // } @@ -792,8 +781,6 @@ class UsageTest extends Scope // $this->assertEquals(204, $response['headers']['status-code']); // } - // sleep(self::WAIT); - // for ($i = 0; $i < 3; $i++) { // try { // $newProjectMetrics = $this->client->call( @@ -835,7 +822,6 @@ class UsageTest extends Scope // if ($i === 2) { // throw $e; // } - // sleep(self::WAIT); // continue; // } // } @@ -1027,8 +1013,6 @@ class UsageTest extends Scope $executionTime = $data['executionTime']; $executions = $data['executions']; - sleep(self::WAIT); - $response = $this->client->call( Client::METHOD_GET, '/functions/' . $functionId . '/usage?range=30d', @@ -1152,7 +1136,6 @@ class UsageTest extends Scope $this->assertEquals(200, $response['headers']['status-code']); - sleep(self::WAIT + 20); $tries = 0; while (true) { From 94108e6f3fd9bbc7ba277d6a974e05c36feb2eb9 Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Sun, 23 Feb 2025 15:23:34 +0530 Subject: [PATCH 041/143] chore: remove sleep() for usage stats, since batch size is now 1 --- docker-compose.yml | 14 +++++++------- phpunit.xml | 2 +- tests/e2e/General/UsageTest.php | 4 +++- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index facf0e6db9..3316e7dcf8 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -934,13 +934,13 @@ services: - _APP_DB_PASS - _APP_DATABASE_SHARED_TABLES - appwrite-assistant: - container_name: appwrite-assistant - image: appwrite/assistant:0.7.0 - networks: - - appwrite - environment: - - _APP_ASSISTANT_OPENAI_API_KEY + # appwrite-assistant: + # container_name: appwrite-assistant + # image: appwrite/assistant:0.7.0 + # networks: + # - appwrite + # environment: + # - _APP_ASSISTANT_OPENAI_API_KEY openruntimes-executor: container_name: openruntimes-executor diff --git a/phpunit.xml b/phpunit.xml index 4c4e55ea4e..598b730908 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -6,7 +6,7 @@ convertNoticesToExceptions="true" convertWarningsToExceptions="true" processIsolation="false" - stopOnFailure="false" + stopOnFailure="true" > diff --git a/tests/e2e/General/UsageTest.php b/tests/e2e/General/UsageTest.php index 3e22f827bd..8b15461857 100644 --- a/tests/e2e/General/UsageTest.php +++ b/tests/e2e/General/UsageTest.php @@ -307,7 +307,7 @@ class UsageTest extends Scope /** * @depends testPrepareStorageStats */ - #[Retry(count: 1)] + #[Retry(count: 10)] public function testStorageStats(array $data): array { $bucketId = $data['bucketId']; @@ -488,6 +488,8 @@ class UsageTest extends Scope ] ); + var_dump($response['body']); + $this->assertEquals($name, $response['body']['name']); $this->assertNotEmpty($response['body']['$id']); From f25df670021e9a5c801ebf4cf4dec871b5a6814f Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Sun, 23 Feb 2025 15:39:37 +0530 Subject: [PATCH 042/143] chore: remove sleep() for usage stats, since batch size is now 1 --- tests/e2e/General/UsageTest.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/e2e/General/UsageTest.php b/tests/e2e/General/UsageTest.php index 8b15461857..df780d8f47 100644 --- a/tests/e2e/General/UsageTest.php +++ b/tests/e2e/General/UsageTest.php @@ -23,7 +23,7 @@ class UsageTest extends Scope use SideServer; use FunctionsBase; - private const WAIT = 35; + private const WAIT = 5; private const CREATE = 20; protected string $projectId; @@ -470,6 +470,8 @@ class UsageTest extends Scope $this->assertEquals('name', $response['body']['key']); + sleep(self::WAIT); + $requestsTotal += 1; for ($i = 0; $i < self::CREATE; $i++) { @@ -488,8 +490,6 @@ class UsageTest extends Scope ] ); - var_dump($response['body']); - $this->assertEquals($name, $response['body']['name']); $this->assertNotEmpty($response['body']['$id']); @@ -535,6 +535,8 @@ class UsageTest extends Scope $collectionsTotal = $data['collectionsTotal']; $documentsTotal = $data['documentsTotal']; + sleep(self::WAIT); + $response = $this->client->call( Client::METHOD_GET, '/project/usage', From 4c492723892ed80c848a230bd662165d21bf397a Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Sun, 23 Feb 2025 15:40:54 +0530 Subject: [PATCH 043/143] chore: enable assistant --- docker-compose.yml | 14 +++++++------- phpunit.xml | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 3316e7dcf8..facf0e6db9 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -934,13 +934,13 @@ services: - _APP_DB_PASS - _APP_DATABASE_SHARED_TABLES - # appwrite-assistant: - # container_name: appwrite-assistant - # image: appwrite/assistant:0.7.0 - # networks: - # - appwrite - # environment: - # - _APP_ASSISTANT_OPENAI_API_KEY + appwrite-assistant: + container_name: appwrite-assistant + image: appwrite/assistant:0.7.0 + networks: + - appwrite + environment: + - _APP_ASSISTANT_OPENAI_API_KEY openruntimes-executor: container_name: openruntimes-executor diff --git a/phpunit.xml b/phpunit.xml index 598b730908..4c4e55ea4e 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -6,7 +6,7 @@ convertNoticesToExceptions="true" convertWarningsToExceptions="true" processIsolation="false" - stopOnFailure="true" + stopOnFailure="false" > From 3f50532c916c4a4ceeff9935968e88a5ef668b48 Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Sun, 23 Feb 2025 15:42:53 +0530 Subject: [PATCH 044/143] chore: remove sleep in functions tests --- tests/e2e/Services/Functions/FunctionsCustomServerTest.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php index 3ed4ca727e..806d07f9f7 100644 --- a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php +++ b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php @@ -1686,9 +1686,6 @@ class FunctionsCustomServerTest extends Scope $this->assertEquals(1, count($executions['body']['executions'])); }); - // Await Aggregation - sleep(System::getEnv('_APP_USAGE_AGGREGATION_INTERVAL', 30)); - $this->assertEventually(function () use ($functionId) { $response = $this->getFunctionUsage($functionId, [ 'range' => '24h' From 895807032725091310e8cfdfe5a4fca54e08fa09 Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Sun, 23 Feb 2025 15:48:33 +0530 Subject: [PATCH 045/143] chore: linter --- tests/e2e/Services/Functions/FunctionsCustomServerTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php index 806d07f9f7..182e6902a3 100644 --- a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php +++ b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php @@ -13,7 +13,6 @@ use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Role; use Utopia\Database\Query; use Utopia\Database\Validator\Datetime as DatetimeValidator; -use Utopia\System\System; class FunctionsCustomServerTest extends Scope { From 7ac4a5bf271b7079210996c01b39bc186aa4f207 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Tue, 25 Feb 2025 04:43:44 +0000 Subject: [PATCH 046/143] chore: update naming --- app/controllers/api/storage.php | 6 +++--- src/Appwrite/Utopia/Response/Model/UsageBuckets.php | 4 ++-- tests/e2e/Services/Storage/StorageConsoleClientTest.php | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/app/controllers/api/storage.php b/app/controllers/api/storage.php index efd82ba40e..072a2d4ae4 100644 --- a/app/controllers/api/storage.php +++ b/app/controllers/api/storage.php @@ -1894,7 +1894,7 @@ App::get('/v1/storage/:bucketId/usage') $metrics = [ str_replace('{bucketInternalId}', $bucket->getInternalId(), METRIC_BUCKET_ID_FILES), str_replace('{bucketInternalId}', $bucket->getInternalId(), METRIC_BUCKET_ID_FILES_STORAGE), - str_replace('{bucketInternalId}', $bucket->getInternalId(), METRIC_FILES_TRANSFORMATIONS), + str_replace('{bucketInternalId}', $bucket->getInternalId(), METRIC_FILES_IMAGES_TRANSFORMED), ]; @@ -1949,7 +1949,7 @@ App::get('/v1/storage/:bucketId/usage') 'filesStorageTotal' => $usage[$metrics[1]]['total'], 'files' => $usage[$metrics[0]]['data'], 'storage' => $usage[$metrics[1]]['data'], - 'fileTransformations' => $usage[$metrics[2]]['data'], - 'fileTransformationsTotal' => $usage[$metrics[2]]['total'], + 'imageTransformations' => $usage[$metrics[2]]['data'], + 'imageTransformationsTotal' => $usage[$metrics[2]]['total'], ]), Response::MODEL_USAGE_BUCKETS); }); diff --git a/src/Appwrite/Utopia/Response/Model/UsageBuckets.php b/src/Appwrite/Utopia/Response/Model/UsageBuckets.php index ab8d129857..ee624a29ad 100644 --- a/src/Appwrite/Utopia/Response/Model/UsageBuckets.php +++ b/src/Appwrite/Utopia/Response/Model/UsageBuckets.php @@ -42,14 +42,14 @@ class UsageBuckets extends Model 'example' => [], 'array' => true ]) - ->addRule('fileTransformations', [ + ->addRule('imageTransformations', [ 'type' => Response::MODEL_METRIC, 'description' => 'Aggregated number of files transformations per period.', 'default' => [], 'example' => [], 'array' => true ]) - ->addRule('fileTransformationsTotal', [ + ->addRule('imageTransformationsTotal', [ 'type' => self::TYPE_INTEGER, 'description' => 'Total aggregated number of files transformations.', 'default' => 0, diff --git a/tests/e2e/Services/Storage/StorageConsoleClientTest.php b/tests/e2e/Services/Storage/StorageConsoleClientTest.php index b2624746ca..bbb14fb136 100644 --- a/tests/e2e/Services/Storage/StorageConsoleClientTest.php +++ b/tests/e2e/Services/Storage/StorageConsoleClientTest.php @@ -104,7 +104,7 @@ class StorageConsoleClientTest extends Scope $this->assertIsNumeric($response['body']['filesStorageTotal']); $this->assertIsArray($response['body']['files']); $this->assertIsArray($response['body']['storage']); - $this->assertIsArray($response['body']['fileTransformations']); - $this->assertIsNumeric($response['body']['fileTransformationsTotal']); + $this->assertIsArray($response['body']['imageTransformations']); + $this->assertIsNumeric($response['body']['imageTransformationsTotal']); } } From ab6c5c9f8dbecff758429d86abe5e33e9afe53ae Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Tue, 25 Feb 2025 04:47:35 +0000 Subject: [PATCH 047/143] chore: bump utopia messaging --- composer.json | 2 +- composer.lock | 26 +++++++++++++------------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/composer.json b/composer.json index e182ac0fff..748b3e5649 100644 --- a/composer.json +++ b/composer.json @@ -59,7 +59,7 @@ "utopia-php/image": "0.8.*", "utopia-php/locale": "0.4.*", "utopia-php/logger": "0.6.*", - "utopia-php/messaging": "0.14.*", + "utopia-php/messaging": "0.16.*", "utopia-php/migration": "0.6.*", "utopia-php/orchestration": "0.9.*", "utopia-php/platform": "0.7.3", diff --git a/composer.lock b/composer.lock index 123ff3eade..2a3bb56ed4 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "b1d26d441fe1fb6a26978d99ef01985f", + "content-hash": "5e45619efebf755a1753eb91535bf387", "packages": [ { "name": "adhocore/jwt", @@ -3919,16 +3919,16 @@ }, { "name": "utopia-php/framework", - "version": "0.33.16", + "version": "0.33.17", "source": { "type": "git", "url": "https://github.com/utopia-php/http.git", - "reference": "e91d4c560d1b809e25faa63d564fef034363b50f" + "reference": "73fac6fbce9f56282dba4e52a58cf836ec434644" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/http/zipball/e91d4c560d1b809e25faa63d564fef034363b50f", - "reference": "e91d4c560d1b809e25faa63d564fef034363b50f", + "url": "https://api.github.com/repos/utopia-php/http/zipball/73fac6fbce9f56282dba4e52a58cf836ec434644", + "reference": "73fac6fbce9f56282dba4e52a58cf836ec434644", "shasum": "" }, "require": { @@ -3960,9 +3960,9 @@ ], "support": { "issues": "https://github.com/utopia-php/http/issues", - "source": "https://github.com/utopia-php/http/tree/0.33.16" + "source": "https://github.com/utopia-php/http/tree/0.33.17" }, - "time": "2025-01-16T15:58:50+00:00" + "time": "2025-02-24T17:35:48+00:00" }, { "name": "utopia-php/image", @@ -4120,16 +4120,16 @@ }, { "name": "utopia-php/messaging", - "version": "0.14.1", + "version": "0.16.0", "source": { "type": "git", "url": "https://github.com/utopia-php/messaging.git", - "reference": "4ba356a3aa382802727f7e13e0f0152bcc1fc535" + "reference": "5f3083697102b1821d6624938186761b1e09c54e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/messaging/zipball/4ba356a3aa382802727f7e13e0f0152bcc1fc535", - "reference": "4ba356a3aa382802727f7e13e0f0152bcc1fc535", + "url": "https://api.github.com/repos/utopia-php/messaging/zipball/5f3083697102b1821d6624938186761b1e09c54e", + "reference": "5f3083697102b1821d6624938186761b1e09c54e", "shasum": "" }, "require": { @@ -4165,9 +4165,9 @@ ], "support": { "issues": "https://github.com/utopia-php/messaging/issues", - "source": "https://github.com/utopia-php/messaging/tree/0.14.1" + "source": "https://github.com/utopia-php/messaging/tree/0.16.0" }, - "time": "2025-01-28T06:14:28+00:00" + "time": "2025-02-18T08:27:00+00:00" }, { "name": "utopia-php/migration", From 676431c355d93597d34853b95c286d9bd73525e3 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Tue, 25 Feb 2025 07:16:42 +0000 Subject: [PATCH 048/143] chore: shifted to logsDb --- app/controllers/api/storage.php | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/app/controllers/api/storage.php b/app/controllers/api/storage.php index 072a2d4ae4..972232167b 100644 --- a/app/controllers/api/storage.php +++ b/app/controllers/api/storage.php @@ -1879,9 +1879,12 @@ App::get('/v1/storage/:bucketId/usage') ->param('bucketId', '', new UID(), 'Bucket ID.') ->param('range', '30d', new WhiteList(['24h', '30d', '90d'], true), 'Date range.', true) ->inject('response') + ->inject('project') ->inject('dbForProject') - ->action(function (string $bucketId, string $range, Response $response, Database $dbForProject) { + ->inject('getLogsDB') + ->action(function (string $bucketId, string $range, Response $response, Document $project, Database $dbForProject, callable $getLogsDB) { + $dbForLogs = call_user_func($getLogsDB, $project); $bucket = $dbForProject->getDocument('buckets', $bucketId); if ($bucket->isEmpty()) { @@ -1894,13 +1897,16 @@ App::get('/v1/storage/:bucketId/usage') $metrics = [ str_replace('{bucketInternalId}', $bucket->getInternalId(), METRIC_BUCKET_ID_FILES), str_replace('{bucketInternalId}', $bucket->getInternalId(), METRIC_BUCKET_ID_FILES_STORAGE), - str_replace('{bucketInternalId}', $bucket->getInternalId(), METRIC_FILES_IMAGES_TRANSFORMED), + str_replace('{bucketInternalId}', $bucket->getInternalId(), METRIC_BUCKET_ID_FILES_IMAGES_TRANSFORMED), ]; - - Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats, &$total) { + Authorization::skip(function () use ($dbForProject, $dbForLogs, $bucket, $days, $metrics, &$stats) { foreach ($metrics as $metric) { - $result = $dbForProject->findOne('stats', [ + $db = ($metric === str_replace('{bucketInternalId}', $bucket->getInternalId(), METRIC_BUCKET_ID_FILES_IMAGES_TRANSFORMED)) + ? $dbForLogs + : $dbForProject; + + $result = $db->findOne('stats', [ Query::equal('metric', [$metric]), Query::equal('period', ['inf']) ]); @@ -1908,7 +1914,7 @@ App::get('/v1/storage/:bucketId/usage') $stats[$metric]['total'] = $result['value'] ?? 0; $limit = $days['limit']; $period = $days['period']; - $results = $dbForProject->find('stats', [ + $results = $db->find('stats', [ Query::equal('metric', [$metric]), Query::equal('period', [$period]), Query::limit($limit), From 542e34bc943d35344e7294ecd8af4c3de1664858 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Tue, 25 Feb 2025 12:28:05 +0000 Subject: [PATCH 049/143] chore: bump docker-base --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 2bb9f80d9e..8ef6c204c7 100755 --- a/Dockerfile +++ b/Dockerfile @@ -12,7 +12,7 @@ RUN composer install --ignore-platform-reqs --optimize-autoloader \ --no-plugins --no-scripts --prefer-dist \ `if [ "$TESTING" != "true" ]; then echo "--no-dev"; fi` -FROM appwrite/base:0.9.5 AS final +FROM appwrite/base:0.10.0 AS final LABEL maintainer="team@appwrite.io" From ae78e793dd3d026e376def042d2b88fbec9ecd80 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Wed, 26 Feb 2025 08:52:06 +0000 Subject: [PATCH 050/143] chore: added blocking to file preview endpoint --- app/config/errors.php | 5 +++++ app/controllers/api/storage.php | 18 ++++++++++-------- src/Appwrite/Extend/Exception.php | 1 + 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/app/config/errors.php b/app/config/errors.php index 7c7f6dc9ec..d8a5c798d3 100644 --- a/app/config/errors.php +++ b/app/config/errors.php @@ -480,6 +480,11 @@ return [ 'description' => 'The requested file is not publicly readable.', 'code' => 403, ], + Exception::STORAGE_FILE_PREVIEW_BLOCKED => [ + 'name' => Exception::STORAGE_FILE_PREVIEW_BLOCKED, + 'description' => 'File preview is not available on your pricing current tier.', + 'code' => 403, + ], /** VCS */ Exception::INSTALLATION_NOT_FOUND => [ diff --git a/app/controllers/api/storage.php b/app/controllers/api/storage.php index 972232167b..38e74ad2ee 100644 --- a/app/controllers/api/storage.php +++ b/app/controllers/api/storage.php @@ -935,15 +935,13 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview') ->param('rotation', 0, new Range(-360, 360), 'Preview image rotation in degrees. Pass an integer between -360 and 360.', true) ->param('background', '', new HexColor(), 'Preview image background color. Only works with transparent images (png). Use a valid HEX color, no # is needed for prefix.', true) ->param('output', '', new WhiteList(\array_keys(Config::getParam('storage-outputs')), true), 'Output format type (jpeg, jpg, png, gif and webp).', true) - ->inject('request') ->inject('response') - ->inject('project') + ->inject('plan') ->inject('dbForProject') - ->inject('mode') ->inject('deviceForFiles') ->inject('deviceForLocal') ->inject('queueForStatsUsage') - ->action(function (string $bucketId, string $fileId, int $width, int $height, string $gravity, int $quality, int $borderWidth, string $borderColor, int $borderRadius, float $opacity, int $rotation, string $background, string $output, Request $request, Response $response, Document $project, Database $dbForProject, string $mode, Device $deviceForFiles, Device $deviceForLocal, StatsUsage $queueForStatsUsage) { + ->action(function (string $bucketId, string $fileId, int $width, int $height, string $gravity, int $quality, int $borderWidth, string $borderColor, int $borderRadius, float $opacity, int $rotation, string $background, string $output, Response $response, array $plan, Database $dbForProject, Device $deviceForFiles, Device $deviceForLocal, StatsUsage $queueForStatsUsage) { if (!\extension_loaded('imagick')) { throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Imagick extension is missing'); @@ -965,6 +963,12 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview') throw new Exception(Exception::USER_UNAUTHORIZED); } + if (isset($plan['imageTransformations'])) { + if ($plan['imageTransformations'] === -1) { + throw new Exception(Exception::STORAGE_FILE_PREVIEW_BLOCKED); + } + } + if ($fileSecurity && !$valid) { $file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId); } else { @@ -1073,8 +1077,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview') $queueForStatsUsage ->addMetric(METRIC_FILES_TRANSFORMATIONS, 1) - ->addMetric(str_replace('{bucketInternalId}', $bucket->getInternalId(), METRIC_BUCKET_ID_FILES_TRANSFORMATIONS), 1) - ; + ->addMetric(str_replace('{bucketInternalId}', $bucket->getInternalId(), METRIC_BUCKET_ID_FILES_TRANSFORMATIONS), 1); $transformedAt = $file->getAttribute('transformedAt', ''); if (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_PROJECT_ACCESS)) > $transformedAt) { @@ -1085,8 +1088,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview') $response ->addHeader('Cache-Control', 'private, max-age=2592000') // 30 days ->setContentType($contentType) - ->file($data) - ; + ->file($data); unset($image); }); diff --git a/src/Appwrite/Extend/Exception.php b/src/Appwrite/Extend/Exception.php index d4f47ca177..7f601018fe 100644 --- a/src/Appwrite/Extend/Exception.php +++ b/src/Appwrite/Extend/Exception.php @@ -143,6 +143,7 @@ class Exception extends \Exception public const STORAGE_INVALID_RANGE = 'storage_invalid_range'; public const STORAGE_INVALID_APPWRITE_ID = 'storage_invalid_appwrite_id'; public const STORAGE_FILE_NOT_PUBLIC = 'storage_file_not_public'; + public const STORAGE_FILE_PREVIEW_BLOCKED = 'storage_file_preview_blocked'; /** VCS */ public const INSTALLATION_NOT_FOUND = 'installation_not_found'; From bb5ed1ccc68546176840410b957bcd742f473f34 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Wed, 26 Feb 2025 10:14:32 +0000 Subject: [PATCH 051/143] chore: bump base to 0.10.1 --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 8ef6c204c7..41592ef9fb 100755 --- a/Dockerfile +++ b/Dockerfile @@ -12,7 +12,7 @@ RUN composer install --ignore-platform-reqs --optimize-autoloader \ --no-plugins --no-scripts --prefer-dist \ `if [ "$TESTING" != "true" ]; then echo "--no-dev"; fi` -FROM appwrite/base:0.10.0 AS final +FROM appwrite/base:0.10.1 AS final LABEL maintainer="team@appwrite.io" From 0ddcb9b14c9a07a647a5eb3e5bf95f41b3e2b891 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Wed, 26 Feb 2025 16:24:05 +0000 Subject: [PATCH 052/143] chore: refactor adapter --- src/Appwrite/Event/Realtime.php | 42 ++++++++++++--------------------- 1 file changed, 15 insertions(+), 27 deletions(-) diff --git a/src/Appwrite/Event/Realtime.php b/src/Appwrite/Event/Realtime.php index e04bbfffb4..e7a61ea545 100644 --- a/src/Appwrite/Event/Realtime.php +++ b/src/Appwrite/Event/Realtime.php @@ -82,33 +82,21 @@ class Realtime extends Event bucket: $bucket, ); - if (!empty($this->getTargets())) { - foreach ($this->getTargets() as $targetProjectId) { - RealtimeAdapter::send( - projectId: $targetProjectId, - payload: $this->getRealtimePayload(), - events: $allEvents, - channels: $target['channels'], - roles: $target['roles'], - options: [ - 'permissionsChanged' => $target['permissionsChanged'], - 'userId' => $this->getParam('userId') - ] - ); - } - } else { - RealtimeAdapter::send( - projectId: $target['projectId'] ?? $this->getProject()->getId(), - payload: $this->getRealtimePayload(), - events: $allEvents, - channels: $target['channels'], - roles: $target['roles'], - options: [ - 'permissionsChanged' => $target['permissionsChanged'], - 'userId' => $this->getParam('userId') - ] - ); - } + $projectIds = !empty($this->getTargets()) + ? $this->getTargets() + : [$target['projectId'] ?? $this->getProject()->getId()]; + + RealtimeAdapter::send( + projectId: $projectIds, + payload: $this->getRealtimePayload(), + events: $allEvents, + channels: $target['channels'], + roles: $target['roles'], + options: [ + 'permissionsChanged' => $target['permissionsChanged'], + 'userId' => $this->getParam('userId') + ] + ); return true; } From f5947de50880f1568967db56097cc2629075c5b4 Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Wed, 26 Feb 2025 16:38:05 +0000 Subject: [PATCH 053/143] chore: bump cache 0.12.x --- composer.json | 4 ++-- composer.lock | 48 +++++++++++++++++++++++++----------------------- 2 files changed, 27 insertions(+), 25 deletions(-) diff --git a/composer.json b/composer.json index 6180773b24..0988810b47 100644 --- a/composer.json +++ b/composer.json @@ -48,7 +48,7 @@ "utopia-php/abuse": "0.51.*", "utopia-php/analytics": "0.10.*", "utopia-php/audit": "0.54.0", - "utopia-php/cache": "0.11.*", + "utopia-php/cache": "0.12.*", "utopia-php/cli": "0.15.*", "utopia-php/config": "0.2.*", "utopia-php/database": "0.60.*", @@ -71,7 +71,7 @@ "utopia-php/swoole": "0.8.*", "utopia-php/system": "0.9.*", "utopia-php/telemetry": "0.1.*", - "utopia-php/vcs": "0.8.*", + "utopia-php/vcs": "0.9.3", "utopia-php/websocket": "0.1.*", "matomo/device-detector": "6.1.*", "dragonmantank/cron-expression": "3.3.2", diff --git a/composer.lock b/composer.lock index 841d5b51c5..ad1ed0f14a 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "87a12bb6f58c5dad7488229590e9a3f5", + "content-hash": "7b90c906ef7ef71246bbed87f0b45955", "packages": [ { "name": "adhocore/jwt", @@ -3521,23 +3521,24 @@ }, { "name": "utopia-php/cache", - "version": "0.11.0", + "version": "0.12.0", "source": { "type": "git", "url": "https://github.com/utopia-php/cache.git", - "reference": "8ebcab5aac7606331cef69b0081f6c9eff2e58bc" + "reference": "646038f1d470b759c129348be8fc14da3c00bbd9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/cache/zipball/8ebcab5aac7606331cef69b0081f6c9eff2e58bc", - "reference": "8ebcab5aac7606331cef69b0081f6c9eff2e58bc", + "url": "https://api.github.com/repos/utopia-php/cache/zipball/646038f1d470b759c129348be8fc14da3c00bbd9", + "reference": "646038f1d470b759c129348be8fc14da3c00bbd9", "shasum": "" }, "require": { "ext-json": "*", "ext-memcached": "*", "ext-redis": "*", - "php": ">=8.0" + "php": ">=8.0", + "utopia-php/telemetry": "0.1.*" }, "require-dev": { "laravel/pint": "1.2.*", @@ -3565,9 +3566,9 @@ ], "support": { "issues": "https://github.com/utopia-php/cache/issues", - "source": "https://github.com/utopia-php/cache/tree/0.11.0" + "source": "https://github.com/utopia-php/cache/tree/0.12.0" }, - "time": "2024-11-05T16:53:58+00:00" + "time": "2025-02-25T09:09:21+00:00" }, { "name": "utopia-php/cli", @@ -3717,23 +3718,23 @@ }, { "name": "utopia-php/database", - "version": "0.60.3", + "version": "0.60.4", "source": { "type": "git", "url": "https://github.com/utopia-php/database.git", - "reference": "c4bc4af3f09a91aea76aac75b4b78fa06598c61d" + "reference": "3a034f8eb275cd92d088f1e492a7cd00fd021427" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/database/zipball/c4bc4af3f09a91aea76aac75b4b78fa06598c61d", - "reference": "c4bc4af3f09a91aea76aac75b4b78fa06598c61d", + "url": "https://api.github.com/repos/utopia-php/database/zipball/3a034f8eb275cd92d088f1e492a7cd00fd021427", + "reference": "3a034f8eb275cd92d088f1e492a7cd00fd021427", "shasum": "" }, "require": { "ext-mbstring": "*", "ext-pdo": "*", "php": ">=8.1", - "utopia-php/cache": "0.11.*", + "utopia-php/cache": "0.12.*", "utopia-php/framework": "0.33.*", "utopia-php/mongo": "0.3.*" }, @@ -3767,9 +3768,9 @@ ], "support": { "issues": "https://github.com/utopia-php/database/issues", - "source": "https://github.com/utopia-php/database/tree/0.60.3" + "source": "https://github.com/utopia-php/database/tree/0.60.4" }, - "time": "2025-02-17T12:46:59+00:00" + "time": "2025-02-25T23:09:18+00:00" }, { "name": "utopia-php/domains", @@ -4820,23 +4821,24 @@ }, { "name": "utopia-php/vcs", - "version": "0.8.6", + "version": "0.9.3", "source": { "type": "git", "url": "https://github.com/utopia-php/vcs.git", - "reference": "b10225f54d5670f09f83e82e09de9d820ada6931" + "reference": "865a00c67e81a20938b883f9aa802303790dd3b5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/vcs/zipball/b10225f54d5670f09f83e82e09de9d820ada6931", - "reference": "b10225f54d5670f09f83e82e09de9d820ada6931", + "url": "https://api.github.com/repos/utopia-php/vcs/zipball/865a00c67e81a20938b883f9aa802303790dd3b5", + "reference": "865a00c67e81a20938b883f9aa802303790dd3b5", "shasum": "" }, "require": { "adhocore/jwt": "^1.1", "php": ">=8.0", - "utopia-php/cache": "^0.11.0", - "utopia-php/framework": "0.*.*" + "utopia-php/cache": "0.12.*", + "utopia-php/framework": "0.*.*", + "utopia-php/system": "0.9.*" }, "require-dev": { "laravel/pint": "1.2.*", @@ -4863,9 +4865,9 @@ ], "support": { "issues": "https://github.com/utopia-php/vcs/issues", - "source": "https://github.com/utopia-php/vcs/tree/0.8.6" + "source": "https://github.com/utopia-php/vcs/tree/0.9.3" }, - "time": "2024-12-10T13:13:23+00:00" + "time": "2025-02-26T16:33:35+00:00" }, { "name": "utopia-php/websocket", From 31d6fe24233f26b7851f3cc956a7d2153006215a Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Wed, 26 Feb 2025 16:40:19 +0000 Subject: [PATCH 054/143] chore: bump cache 0.12.x --- composer.json | 2 +- composer.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index 0988810b47..e096f9e4e7 100644 --- a/composer.json +++ b/composer.json @@ -71,7 +71,7 @@ "utopia-php/swoole": "0.8.*", "utopia-php/system": "0.9.*", "utopia-php/telemetry": "0.1.*", - "utopia-php/vcs": "0.9.3", + "utopia-php/vcs": "0.9.*", "utopia-php/websocket": "0.1.*", "matomo/device-detector": "6.1.*", "dragonmantank/cron-expression": "3.3.2", diff --git a/composer.lock b/composer.lock index ad1ed0f14a..61eee9b8dc 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "7b90c906ef7ef71246bbed87f0b45955", + "content-hash": "52270716c94a941bef634c33d85df905", "packages": [ { "name": "adhocore/jwt", From 7687784515ab4e0f78cf8f7d024281a968dff341 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Wed, 26 Feb 2025 16:41:26 +0000 Subject: [PATCH 055/143] chore: added fallback to old values in update --- app/controllers/api/databases.php | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/app/controllers/api/databases.php b/app/controllers/api/databases.php index a4b5683512..e14c504234 100644 --- a/app/controllers/api/databases.php +++ b/app/controllers/api/databases.php @@ -299,6 +299,9 @@ function updateAttribute( switch ($attribute->getAttribute('format')) { case APP_DATABASE_ATTRIBUTE_INT_RANGE: case APP_DATABASE_ATTRIBUTE_FLOAT_RANGE: + $min ??= $attribute->getAttribute('formatOptions')['min']; + $max ??= $attribute->getAttribute('formatOptions')['max']; + if ($min > $max) { throw new Exception(Exception::ATTRIBUTE_VALUE_INVALID, 'Minimum value must be lesser than maximum value'); } @@ -1550,8 +1553,8 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/intege ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?int $min, ?int $max, ?int $default, bool $array, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { // Ensure attribute default is within range - $min = \is_null($min) ? PHP_INT_MIN : $min; - $max = \is_null($max) ? PHP_INT_MAX : $max; + $min ??= PHP_INT_MIN; + $max ??= PHP_INT_MAX; if ($min > $max) { throw new Exception(Exception::ATTRIBUTE_VALUE_INVALID, 'Minimum value must be lesser than maximum value'); @@ -1627,8 +1630,8 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/float' ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?float $min, ?float $max, ?float $default, bool $array, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { // Ensure attribute default is within range - $min = \is_null($min) ? -PHP_FLOAT_MAX : $min; - $max = \is_null($max) ? PHP_FLOAT_MAX : $max; + $min ??= -PHP_FLOAT_MAX; + $max ??= PHP_FLOAT_MAX; if ($min > $max) { throw new Exception(Exception::ATTRIBUTE_VALUE_INVALID, 'Minimum value must be lesser than maximum value'); @@ -2347,11 +2350,6 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/integ ->inject('dbForProject') ->inject('queueForEvents') ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?int $min, ?int $max, ?int $default, ?string $newKey, Response $response, Database $dbForProject, Event $queueForEvents) { - - // Ensure attribute default is within range - $min = \is_null($min) ? PHP_INT_MIN : $min; - $max = \is_null($max) ? PHP_INT_MAX : $max; - $attribute = updateAttribute( databaseId: $databaseId, collectionId: $collectionId, @@ -2411,11 +2409,6 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/float ->inject('dbForProject') ->inject('queueForEvents') ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?float $min, ?float $max, ?float $default, ?string $newKey, Response $response, Database $dbForProject, Event $queueForEvents) { - - // Ensure attribute default is within range - $min = \is_null($min) ? -PHP_FLOAT_MAX : $min; - $max = \is_null($max) ? PHP_FLOAT_MAX : $max; - $attribute = updateAttribute( databaseId: $databaseId, collectionId: $collectionId, From 28896081d78cf12a0a7c06838f7fb2987f823bb1 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Wed, 26 Feb 2025 16:50:12 +0000 Subject: [PATCH 056/143] chore: fix forloop --- src/Appwrite/Event/Realtime.php | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/Appwrite/Event/Realtime.php b/src/Appwrite/Event/Realtime.php index e7a61ea545..324f140165 100644 --- a/src/Appwrite/Event/Realtime.php +++ b/src/Appwrite/Event/Realtime.php @@ -86,17 +86,19 @@ class Realtime extends Event ? $this->getTargets() : [$target['projectId'] ?? $this->getProject()->getId()]; - RealtimeAdapter::send( - projectId: $projectIds, - payload: $this->getRealtimePayload(), - events: $allEvents, - channels: $target['channels'], - roles: $target['roles'], - options: [ - 'permissionsChanged' => $target['permissionsChanged'], - 'userId' => $this->getParam('userId') - ] - ); + foreach ($projectIds as $projectId) { + RealtimeAdapter::send( + projectId: $projectId, + payload: $this->getRealtimePayload(), + events: $allEvents, + channels: $target['channels'], + roles: $target['roles'], + options: [ + 'permissionsChanged' => $target['permissionsChanged'], + 'userId' => $this->getParam('userId') + ] + ); + } return true; } From c79ed286b1cc79ba94778acda18983e8052da288 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Wed, 26 Feb 2025 17:35:06 +0000 Subject: [PATCH 057/143] chore: update tests --- app/controllers/api/databases.php | 4 ++-- .../e2e/Services/Databases/DatabasesCustomServerTest.php | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/controllers/api/databases.php b/app/controllers/api/databases.php index e14c504234..876c6bc82a 100644 --- a/app/controllers/api/databases.php +++ b/app/controllers/api/databases.php @@ -242,8 +242,8 @@ function updateAttribute( string $filter = null, string|bool|int|float $default = null, bool $required = null, - int|float $min = null, - int|float $max = null, + int|float|null $min = null, + int|float|null $max = null, array $elements = null, array $options = [], string $newKey = null, diff --git a/tests/e2e/Services/Databases/DatabasesCustomServerTest.php b/tests/e2e/Services/Databases/DatabasesCustomServerTest.php index 75526b9928..57e0b93634 100644 --- a/tests/e2e/Services/Databases/DatabasesCustomServerTest.php +++ b/tests/e2e/Services/Databases/DatabasesCustomServerTest.php @@ -2327,8 +2327,8 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ 'required' => false, - 'default' => -10, - 'max' => 0, + 'default' => 10, + 'max' => 100, ]); $this->assertEquals(200, $update['headers']['status-code']); @@ -2588,8 +2588,8 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ 'required' => false, - 'default' => -123.456, - 'max' => 0.0, + 'default' => 23.456, + 'max' => 100.0, ]); $this->assertEquals(200, $update['headers']['status-code']); From 57c7ebb53def528b366126e08b418a0d9fae6582 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Wed, 26 Feb 2025 17:51:00 +0000 Subject: [PATCH 058/143] chore: bump sdk --- composer.lock | 50 +++++++++++++++++++++++++------------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/composer.lock b/composer.lock index d23d9acc99..e1d1ac16e3 100644 --- a/composer.lock +++ b/composer.lock @@ -279,16 +279,16 @@ }, { "name": "brick/math", - "version": "0.12.1", + "version": "0.12.2", "source": { "type": "git", "url": "https://github.com/brick/math.git", - "reference": "f510c0a40911935b77b86859eb5223d58d660df1" + "reference": "901eddb1e45a8e0f689302e40af871c181ecbe40" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/brick/math/zipball/f510c0a40911935b77b86859eb5223d58d660df1", - "reference": "f510c0a40911935b77b86859eb5223d58d660df1", + "url": "https://api.github.com/repos/brick/math/zipball/901eddb1e45a8e0f689302e40af871c181ecbe40", + "reference": "901eddb1e45a8e0f689302e40af871c181ecbe40", "shasum": "" }, "require": { @@ -297,7 +297,7 @@ "require-dev": { "php-coveralls/php-coveralls": "^2.2", "phpunit/phpunit": "^10.1", - "vimeo/psalm": "5.16.0" + "vimeo/psalm": "6.8.8" }, "type": "library", "autoload": { @@ -327,7 +327,7 @@ ], "support": { "issues": "https://github.com/brick/math/issues", - "source": "https://github.com/brick/math/tree/0.12.1" + "source": "https://github.com/brick/math/tree/0.12.2" }, "funding": [ { @@ -335,7 +335,7 @@ "type": "github" } ], - "time": "2023-11-29T23:19:16+00:00" + "time": "2025-02-26T10:21:45+00:00" }, { "name": "chillerlan/php-qrcode", @@ -2694,16 +2694,16 @@ }, { "name": "symfony/http-client", - "version": "v7.2.3", + "version": "v7.2.4", "source": { "type": "git", "url": "https://github.com/symfony/http-client.git", - "reference": "7ce6078c79a4a7afff931c413d2959d3bffbfb8d" + "reference": "78981a2ffef6437ed92d4d7e2a86a82f256c6dc6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client/zipball/7ce6078c79a4a7afff931c413d2959d3bffbfb8d", - "reference": "7ce6078c79a4a7afff931c413d2959d3bffbfb8d", + "url": "https://api.github.com/repos/symfony/http-client/zipball/78981a2ffef6437ed92d4d7e2a86a82f256c6dc6", + "reference": "78981a2ffef6437ed92d4d7e2a86a82f256c6dc6", "shasum": "" }, "require": { @@ -2769,7 +2769,7 @@ "http" ], "support": { - "source": "https://github.com/symfony/http-client/tree/v7.2.3" + "source": "https://github.com/symfony/http-client/tree/v7.2.4" }, "funding": [ { @@ -2785,7 +2785,7 @@ "type": "tidelift" } ], - "time": "2025-01-28T15:51:35+00:00" + "time": "2025-02-13T10:27:23+00:00" }, { "name": "symfony/http-client-contracts", @@ -5052,16 +5052,16 @@ "packages-dev": [ { "name": "appwrite/sdk-generator", - "version": "0.40.0", + "version": "0.40.1", "source": { "type": "git", "url": "https://github.com/appwrite/sdk-generator.git", - "reference": "d2880132c900f64108d3e4484a6c1ed1bed2303c" + "reference": "df180676b6fbde7832ae1495af3e2f3e8f700837" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/d2880132c900f64108d3e4484a6c1ed1bed2303c", - "reference": "d2880132c900f64108d3e4484a6c1ed1bed2303c", + "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/df180676b6fbde7832ae1495af3e2f3e8f700837", + "reference": "df180676b6fbde7832ae1495af3e2f3e8f700837", "shasum": "" }, "require": { @@ -5097,9 +5097,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/0.40.0" + "source": "https://github.com/appwrite/sdk-generator/tree/0.40.1" }, - "time": "2025-02-04T12:47:33+00:00" + "time": "2025-02-26T07:07:10+00:00" }, { "name": "doctrine/annotations", @@ -8035,16 +8035,16 @@ }, { "name": "symfony/process", - "version": "v7.2.0", + "version": "v7.2.4", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "d34b22ba9390ec19d2dd966c40aa9e8462f27a7e" + "reference": "d8f411ff3c7ddc4ae9166fb388d1190a2df5b5cf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/d34b22ba9390ec19d2dd966c40aa9e8462f27a7e", - "reference": "d34b22ba9390ec19d2dd966c40aa9e8462f27a7e", + "url": "https://api.github.com/repos/symfony/process/zipball/d8f411ff3c7ddc4ae9166fb388d1190a2df5b5cf", + "reference": "d8f411ff3c7ddc4ae9166fb388d1190a2df5b5cf", "shasum": "" }, "require": { @@ -8076,7 +8076,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v7.2.0" + "source": "https://github.com/symfony/process/tree/v7.2.4" }, "funding": [ { @@ -8092,7 +8092,7 @@ "type": "tidelift" } ], - "time": "2024-11-06T14:24:19+00:00" + "time": "2025-02-05T08:33:46+00:00" }, { "name": "symfony/string", From 611f2cad6a579b623c71735c77757e4ae03f4e44 Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Wed, 26 Feb 2025 22:02:55 +0000 Subject: [PATCH 059/143] chore: bump cache 0.12.x --- composer.lock | 50 +++++++++++++++++++++++++------------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/composer.lock b/composer.lock index 61eee9b8dc..3d145438e2 100644 --- a/composer.lock +++ b/composer.lock @@ -279,16 +279,16 @@ }, { "name": "brick/math", - "version": "0.12.1", + "version": "0.12.2", "source": { "type": "git", "url": "https://github.com/brick/math.git", - "reference": "f510c0a40911935b77b86859eb5223d58d660df1" + "reference": "901eddb1e45a8e0f689302e40af871c181ecbe40" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/brick/math/zipball/f510c0a40911935b77b86859eb5223d58d660df1", - "reference": "f510c0a40911935b77b86859eb5223d58d660df1", + "url": "https://api.github.com/repos/brick/math/zipball/901eddb1e45a8e0f689302e40af871c181ecbe40", + "reference": "901eddb1e45a8e0f689302e40af871c181ecbe40", "shasum": "" }, "require": { @@ -297,7 +297,7 @@ "require-dev": { "php-coveralls/php-coveralls": "^2.2", "phpunit/phpunit": "^10.1", - "vimeo/psalm": "5.16.0" + "vimeo/psalm": "6.8.8" }, "type": "library", "autoload": { @@ -327,7 +327,7 @@ ], "support": { "issues": "https://github.com/brick/math/issues", - "source": "https://github.com/brick/math/tree/0.12.1" + "source": "https://github.com/brick/math/tree/0.12.2" }, "funding": [ { @@ -335,7 +335,7 @@ "type": "github" } ], - "time": "2023-11-29T23:19:16+00:00" + "time": "2025-02-26T10:21:45+00:00" }, { "name": "chillerlan/php-qrcode", @@ -2694,16 +2694,16 @@ }, { "name": "symfony/http-client", - "version": "v7.2.3", + "version": "v7.2.4", "source": { "type": "git", "url": "https://github.com/symfony/http-client.git", - "reference": "7ce6078c79a4a7afff931c413d2959d3bffbfb8d" + "reference": "78981a2ffef6437ed92d4d7e2a86a82f256c6dc6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client/zipball/7ce6078c79a4a7afff931c413d2959d3bffbfb8d", - "reference": "7ce6078c79a4a7afff931c413d2959d3bffbfb8d", + "url": "https://api.github.com/repos/symfony/http-client/zipball/78981a2ffef6437ed92d4d7e2a86a82f256c6dc6", + "reference": "78981a2ffef6437ed92d4d7e2a86a82f256c6dc6", "shasum": "" }, "require": { @@ -2769,7 +2769,7 @@ "http" ], "support": { - "source": "https://github.com/symfony/http-client/tree/v7.2.3" + "source": "https://github.com/symfony/http-client/tree/v7.2.4" }, "funding": [ { @@ -2785,7 +2785,7 @@ "type": "tidelift" } ], - "time": "2025-01-28T15:51:35+00:00" + "time": "2025-02-13T10:27:23+00:00" }, { "name": "symfony/http-client-contracts", @@ -5054,16 +5054,16 @@ "packages-dev": [ { "name": "appwrite/sdk-generator", - "version": "0.40.0", + "version": "0.40.1", "source": { "type": "git", "url": "https://github.com/appwrite/sdk-generator.git", - "reference": "d2880132c900f64108d3e4484a6c1ed1bed2303c" + "reference": "df180676b6fbde7832ae1495af3e2f3e8f700837" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/d2880132c900f64108d3e4484a6c1ed1bed2303c", - "reference": "d2880132c900f64108d3e4484a6c1ed1bed2303c", + "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/df180676b6fbde7832ae1495af3e2f3e8f700837", + "reference": "df180676b6fbde7832ae1495af3e2f3e8f700837", "shasum": "" }, "require": { @@ -5099,9 +5099,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/0.40.0" + "source": "https://github.com/appwrite/sdk-generator/tree/0.40.1" }, - "time": "2025-02-04T12:47:33+00:00" + "time": "2025-02-26T07:07:10+00:00" }, { "name": "doctrine/annotations", @@ -8037,16 +8037,16 @@ }, { "name": "symfony/process", - "version": "v7.2.0", + "version": "v7.2.4", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "d34b22ba9390ec19d2dd966c40aa9e8462f27a7e" + "reference": "d8f411ff3c7ddc4ae9166fb388d1190a2df5b5cf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/d34b22ba9390ec19d2dd966c40aa9e8462f27a7e", - "reference": "d34b22ba9390ec19d2dd966c40aa9e8462f27a7e", + "url": "https://api.github.com/repos/symfony/process/zipball/d8f411ff3c7ddc4ae9166fb388d1190a2df5b5cf", + "reference": "d8f411ff3c7ddc4ae9166fb388d1190a2df5b5cf", "shasum": "" }, "require": { @@ -8078,7 +8078,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v7.2.0" + "source": "https://github.com/symfony/process/tree/v7.2.4" }, "funding": [ { @@ -8094,7 +8094,7 @@ "type": "tidelift" } ], - "time": "2024-11-06T14:24:19+00:00" + "time": "2025-02-05T08:33:46+00:00" }, { "name": "symfony/string", From 0bbcdf8268b5aac551f94387dc559e3ba9ee4faf Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Wed, 26 Feb 2025 22:05:09 +0000 Subject: [PATCH 060/143] chore: bump cache 0.12.x --- composer.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.lock b/composer.lock index e2b25321a2..76f61e44d7 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "52270716c94a941bef634c33d85df905", + "content-hash": "58ad2e1375ec47d944b96864d4aae63f", "packages": [ { "name": "adhocore/jwt", From 6ad34a7bf7a20f04896cc8c072ff50fc01bf4a2c Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Thu, 27 Feb 2025 05:05:31 +0000 Subject: [PATCH 061/143] chore: sdk versions and corresponding docs --- app/config/platforms.php | 14 +++++++------- .../examples/account/update-mfa-challenge.md | 2 +- .../examples/account/update-mfa-challenge.md | 2 +- .../examples/account/update-mfa-challenge.md | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/app/config/platforms.php b/app/config/platforms.php index 92e325bd44..4b6a0d2da6 100644 --- a/app/config/platforms.php +++ b/app/config/platforms.php @@ -11,7 +11,7 @@ return [ [ 'key' => 'web', 'name' => 'Web', - 'version' => '16.1.0', + 'version' => '17.0.1', 'url' => 'https://github.com/appwrite/sdk-for-web', 'package' => 'https://www.npmjs.com/package/appwrite', 'enabled' => true, @@ -59,7 +59,7 @@ return [ [ 'key' => 'flutter', 'name' => 'Flutter', - 'version' => '13.1.1', + 'version' => '14.0.1', 'url' => 'https://github.com/appwrite/sdk-for-flutter', 'package' => 'https://pub.dev/packages/appwrite', 'enabled' => true, @@ -77,7 +77,7 @@ return [ [ 'key' => 'apple', 'name' => 'Apple', - 'version' => '7.1.0', + 'version' => '9.0.0', 'url' => 'https://github.com/appwrite/sdk-for-apple', 'package' => 'https://github.com/appwrite/sdk-for-apple', 'enabled' => true, @@ -217,7 +217,7 @@ return [ [ 'key' => 'cli', 'name' => 'Command Line', - 'version' => '6.2.0', + 'version' => '6.2.1', 'url' => 'https://github.com/appwrite/sdk-for-cli', 'package' => 'https://www.npmjs.com/package/appwrite-cli', 'enabled' => true, @@ -245,7 +245,7 @@ return [ [ 'key' => 'nodejs', 'name' => 'Node.js', - 'version' => '14.2.0', + 'version' => '15.0.1', 'url' => 'https://github.com/appwrite/sdk-for-node', 'package' => 'https://www.npmjs.com/package/node-appwrite', 'enabled' => true, @@ -371,7 +371,7 @@ return [ [ 'key' => 'dart', 'name' => 'Dart', - 'version' => '12.2.0', + 'version' => '13.0.1', 'url' => 'https://github.com/appwrite/sdk-for-dart', 'package' => 'https://pub.dev/packages/dart_appwrite', 'enabled' => true, @@ -411,7 +411,7 @@ return [ [ 'key' => 'swift', 'name' => 'Swift', - 'version' => '6.2.0', + 'version' => '8.0.0', 'url' => 'https://github.com/appwrite/sdk-for-swift', 'package' => 'https://github.com/appwrite/sdk-for-swift', 'enabled' => true, diff --git a/docs/examples/1.6.x/client-apple/examples/account/update-mfa-challenge.md b/docs/examples/1.6.x/client-apple/examples/account/update-mfa-challenge.md index a237537ae3..1c5874f784 100644 --- a/docs/examples/1.6.x/client-apple/examples/account/update-mfa-challenge.md +++ b/docs/examples/1.6.x/client-apple/examples/account/update-mfa-challenge.md @@ -6,7 +6,7 @@ let client = Client() let account = Account(client) -let result = try await account.updateMfaChallenge( +let session = try await account.updateMfaChallenge( challengeId: "", otp: "" ) diff --git a/docs/examples/1.6.x/client-flutter/examples/account/update-mfa-challenge.md b/docs/examples/1.6.x/client-flutter/examples/account/update-mfa-challenge.md index c8e1af7e90..bbe7c03470 100644 --- a/docs/examples/1.6.x/client-flutter/examples/account/update-mfa-challenge.md +++ b/docs/examples/1.6.x/client-flutter/examples/account/update-mfa-challenge.md @@ -6,7 +6,7 @@ Client client = Client() Account account = Account(client); - result = await account.updateMfaChallenge( +Session result = await account.updateMfaChallenge( challengeId: '', otp: '', ); diff --git a/docs/examples/1.6.x/server-dart/examples/account/update-mfa-challenge.md b/docs/examples/1.6.x/server-dart/examples/account/update-mfa-challenge.md index fd64c61cf9..2843d2f1b4 100644 --- a/docs/examples/1.6.x/server-dart/examples/account/update-mfa-challenge.md +++ b/docs/examples/1.6.x/server-dart/examples/account/update-mfa-challenge.md @@ -7,7 +7,7 @@ Client client = Client() Account account = Account(client); - result = await account.updateMfaChallenge( +Session result = await account.updateMfaChallenge( challengeId: '', otp: '', ); From 388affa3d0f9f0c231f731b30b86cc4127d2c129 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Thu, 27 Feb 2025 07:23:13 +0000 Subject: [PATCH 062/143] chore: updated naming for subscribers --- app/controllers/api/functions.php | 2 +- src/Appwrite/Event/Realtime.php | 20 +++++++++---------- src/Appwrite/Platform/Workers/Builds.php | 8 ++++---- .../Platform/Workers/Certificates.php | 2 +- src/Appwrite/Platform/Workers/Databases.php | 6 +++--- src/Appwrite/Platform/Workers/Functions.php | 2 +- src/Appwrite/Platform/Workers/Migrations.php | 2 +- 7 files changed, 21 insertions(+), 21 deletions(-) diff --git a/app/controllers/api/functions.php b/app/controllers/api/functions.php index a36313b7ab..583468f6c1 100644 --- a/app/controllers/api/functions.php +++ b/app/controllers/api/functions.php @@ -411,7 +411,7 @@ App::post('/v1/functions') /** Trigger Realtime Events */ $queueForRealtime ->from($ruleCreate) - ->setTargets(['console', $project->getId()]) + ->setSubscribers(['console', $project->getId()]) ->trigger(); } diff --git a/src/Appwrite/Event/Realtime.php b/src/Appwrite/Event/Realtime.php index 324f140165..28a1bb6a6d 100644 --- a/src/Appwrite/Event/Realtime.php +++ b/src/Appwrite/Event/Realtime.php @@ -7,7 +7,7 @@ use Utopia\Database\Document; class Realtime extends Event { - protected array $targets = []; + protected array $subscribers = []; public function __construct() { @@ -32,25 +32,25 @@ class Realtime extends Event } /** - * Set targets for this realtime event. + * Set subscribers for this realtime event. * - * @param array $targets + * @param array $subscribers * @return array */ - public function setTargets(array $targets): self + public function setSubscribers(array $subscribers): self { - $this->targets = $targets; + $this->subscribers = $subscribers; return $this; } /** - * Get targets for this realtime event. + * Get subscribers for this realtime event. * * @return array */ - public function getTargets(): array + public function getSubscribers(): array { - return $this->targets; + return $this->subscribers; } /** @@ -82,8 +82,8 @@ class Realtime extends Event bucket: $bucket, ); - $projectIds = !empty($this->getTargets()) - ? $this->getTargets() + $projectIds = !empty($this->getSubscribers()) + ? $this->getSubscribers() : [$target['projectId'] ?? $this->getProject()->getId()]; foreach ($projectIds as $projectId) { diff --git a/src/Appwrite/Platform/Workers/Builds.php b/src/Appwrite/Platform/Workers/Builds.php index c3853a1dde..6f26e9a80c 100644 --- a/src/Appwrite/Platform/Workers/Builds.php +++ b/src/Appwrite/Platform/Workers/Builds.php @@ -383,7 +383,7 @@ class Builds extends Action */ $queueForRealtime ->setProject($project) - ->setTargets(['console']) + ->setSubscribers(['console']) ->setEvent($event) ->setParam('functionId', $function->getId()) ->setParam('deploymentId', $deployment->getId()) @@ -457,7 +457,7 @@ class Builds extends Action /** Trigger Realtime Event */ $queueForRealtime ->setProject($project) - ->setTargets(['console']) + ->setSubscribers(['console']) ->setEvent($event) ->setParam('functionId', $function->getId()) ->setParam('deploymentId', $deployment->getId()) @@ -589,7 +589,7 @@ class Builds extends Action */ $queueForRealtime ->setProject($project) - ->setTargets(['console']) + ->setSubscribers(['console']) ->setEvent($event) ->setParam('functionId', $function->getId()) ->setParam('deploymentId', $deployment->getId()) @@ -686,7 +686,7 @@ class Builds extends Action */ $queueForRealtime ->setProject($project) - ->setTargets(['console']) + ->setSubscribers(['console']) ->setEvent($event) ->setParam('functionId', $function->getId()) ->setParam('deploymentId', $deployment->getId()) diff --git a/src/Appwrite/Platform/Workers/Certificates.php b/src/Appwrite/Platform/Workers/Certificates.php index 009ac24021..093e6fda5a 100644 --- a/src/Appwrite/Platform/Workers/Certificates.php +++ b/src/Appwrite/Platform/Workers/Certificates.php @@ -394,7 +394,7 @@ class Certificates extends Action /** Trigger Realtime Events */ $queueForRealtime ->from($queueForEvents) - ->setTargets(['console', $projectId]) + ->setSubscribers(['console', $projectId]) ->trigger(); } } diff --git a/src/Appwrite/Platform/Workers/Databases.php b/src/Appwrite/Platform/Workers/Databases.php index 74101dd1eb..4abd035599 100644 --- a/src/Appwrite/Platform/Workers/Databases.php +++ b/src/Appwrite/Platform/Workers/Databases.php @@ -601,17 +601,17 @@ class Databases extends Action ): void { $queueForRealtime ->setProject($project) - ->setTargets(['console']) + ->setSubscribers(['console']) ->setEvent($event) ->setParam('databaseId', $database->getId()) ->setParam('collectionId', $collection->getId()); - if ($attribute !== null) { + if ($attribute !== null && !empty($attribute)) { $queueForRealtime ->setParam('attributeId', $attribute->getId()) ->setPayload($attribute->getArrayCopy()); } - if ($index !== null) { + if ($index !== null && !empty($index)) { $queueForRealtime ->setParam('indexId', $index->getId()) ->setPayload($index->getArrayCopy()); diff --git a/src/Appwrite/Platform/Workers/Functions.php b/src/Appwrite/Platform/Workers/Functions.php index 73b02be85e..a7caa3207f 100644 --- a/src/Appwrite/Platform/Workers/Functions.php +++ b/src/Appwrite/Platform/Workers/Functions.php @@ -600,7 +600,7 @@ class Functions extends Action /** Trigger Realtime Events */ $queueForRealtime ->from($queueForEvents) - ->setTargets(['console', $project->getId()]) + ->setSubscribers(['console', $project->getId()]) ->trigger(); if (!empty($error)) { diff --git a/src/Appwrite/Platform/Workers/Migrations.php b/src/Appwrite/Platform/Workers/Migrations.php index 08c75d934a..f21a846a0d 100644 --- a/src/Appwrite/Platform/Workers/Migrations.php +++ b/src/Appwrite/Platform/Workers/Migrations.php @@ -160,7 +160,7 @@ class Migrations extends Action /** Trigger Realtime Events */ $queueForRealtime ->setProject($project) - ->setTargets(['console', $project->getId()]) + ->setSubscribers(['console', $project->getId()]) ->setEvent('migrations.[migrationId].update') ->setParam('migrationId', $migration->getId()) ->setPayload($migration->getArrayCopy()) From f31acd214ec73385d1f7668ed80787dfd4be85ba Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Thu, 27 Feb 2025 09:52:27 +0000 Subject: [PATCH 063/143] chore: remove plan blocking --- app/config/errors.php | 5 ----- app/controllers/api/storage.php | 9 +-------- src/Appwrite/Extend/Exception.php | 1 - 3 files changed, 1 insertion(+), 14 deletions(-) diff --git a/app/config/errors.php b/app/config/errors.php index d8a5c798d3..7c7f6dc9ec 100644 --- a/app/config/errors.php +++ b/app/config/errors.php @@ -480,11 +480,6 @@ return [ 'description' => 'The requested file is not publicly readable.', 'code' => 403, ], - Exception::STORAGE_FILE_PREVIEW_BLOCKED => [ - 'name' => Exception::STORAGE_FILE_PREVIEW_BLOCKED, - 'description' => 'File preview is not available on your pricing current tier.', - 'code' => 403, - ], /** VCS */ Exception::INSTALLATION_NOT_FOUND => [ diff --git a/app/controllers/api/storage.php b/app/controllers/api/storage.php index 38e74ad2ee..bee245b64f 100644 --- a/app/controllers/api/storage.php +++ b/app/controllers/api/storage.php @@ -936,12 +936,11 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview') ->param('background', '', new HexColor(), 'Preview image background color. Only works with transparent images (png). Use a valid HEX color, no # is needed for prefix.', true) ->param('output', '', new WhiteList(\array_keys(Config::getParam('storage-outputs')), true), 'Output format type (jpeg, jpg, png, gif and webp).', true) ->inject('response') - ->inject('plan') ->inject('dbForProject') ->inject('deviceForFiles') ->inject('deviceForLocal') ->inject('queueForStatsUsage') - ->action(function (string $bucketId, string $fileId, int $width, int $height, string $gravity, int $quality, int $borderWidth, string $borderColor, int $borderRadius, float $opacity, int $rotation, string $background, string $output, Response $response, array $plan, Database $dbForProject, Device $deviceForFiles, Device $deviceForLocal, StatsUsage $queueForStatsUsage) { + ->action(function (string $bucketId, string $fileId, int $width, int $height, string $gravity, int $quality, int $borderWidth, string $borderColor, int $borderRadius, float $opacity, int $rotation, string $background, string $output, Response $response, Database $dbForProject, Device $deviceForFiles, Device $deviceForLocal, StatsUsage $queueForStatsUsage) { if (!\extension_loaded('imagick')) { throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Imagick extension is missing'); @@ -963,12 +962,6 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview') throw new Exception(Exception::USER_UNAUTHORIZED); } - if (isset($plan['imageTransformations'])) { - if ($plan['imageTransformations'] === -1) { - throw new Exception(Exception::STORAGE_FILE_PREVIEW_BLOCKED); - } - } - if ($fileSecurity && !$valid) { $file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId); } else { diff --git a/src/Appwrite/Extend/Exception.php b/src/Appwrite/Extend/Exception.php index 7f601018fe..d4f47ca177 100644 --- a/src/Appwrite/Extend/Exception.php +++ b/src/Appwrite/Extend/Exception.php @@ -143,7 +143,6 @@ class Exception extends \Exception public const STORAGE_INVALID_RANGE = 'storage_invalid_range'; public const STORAGE_INVALID_APPWRITE_ID = 'storage_invalid_appwrite_id'; public const STORAGE_FILE_NOT_PUBLIC = 'storage_file_not_public'; - public const STORAGE_FILE_PREVIEW_BLOCKED = 'storage_file_preview_blocked'; /** VCS */ public const INSTALLATION_NOT_FOUND = 'installation_not_found'; From e9d8d13fbdf79dfe03fd6bd61474348d1f877e01 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Fri, 28 Feb 2025 08:28:21 +0000 Subject: [PATCH 064/143] chore: added imageTransformations to project usage --- app/controllers/api/project.php | 2 ++ src/Appwrite/Utopia/Response/Model/UsageProject.php | 7 +++++++ 2 files changed, 9 insertions(+) diff --git a/app/controllers/api/project.php b/app/controllers/api/project.php index ea2cd4436d..cf2a128939 100644 --- a/app/controllers/api/project.php +++ b/app/controllers/api/project.php @@ -63,6 +63,7 @@ App::get('/v1/project/usage') METRIC_BUILDS_STORAGE, METRIC_DATABASES_OPERATIONS_READS, METRIC_DATABASES_OPERATIONS_WRITES, + METRIC_FILES_IMAGES_TRANSFORMED, ], 'period' => [ METRIC_NETWORK_REQUESTS, @@ -363,6 +364,7 @@ App::get('/v1/project/usage') 'authPhoneTotal' => $authPhoneTotal, 'authPhoneEstimate' => $authPhoneEstimate, 'authPhoneCountryBreakdown' => $authPhoneCountryBreakdown, + 'imageTransformations' => $total[METRIC_FILES_IMAGES_TRANSFORMED], ]), Response::MODEL_USAGE_PROJECT); }); diff --git a/src/Appwrite/Utopia/Response/Model/UsageProject.php b/src/Appwrite/Utopia/Response/Model/UsageProject.php index 1006276b56..6f74ec561b 100644 --- a/src/Appwrite/Utopia/Response/Model/UsageProject.php +++ b/src/Appwrite/Utopia/Response/Model/UsageProject.php @@ -197,6 +197,13 @@ class UsageProject extends Model 'example' => [], 'array' => true ]) + ->addRule('imageTransformations', [ + 'type' => Response::MODEL_METRIC, + 'description' => 'An array of aggregated number of image transformations.', + 'default' => [], + 'example' => [], + 'array' => true + ]) ; } From d5cfdc570f25dbab1a591df1f8e32e800fe216fa Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Fri, 28 Feb 2025 08:41:53 +0000 Subject: [PATCH 065/143] chore: updated specs --- app/config/specs/open-api3-latest-client.json | 10 +- .../specs/open-api3-latest-console.json | 211 ++++++++++------- app/config/specs/open-api3-latest-server.json | 178 ++++++++------- app/config/specs/swagger2-latest-client.json | 10 +- app/config/specs/swagger2-latest-console.json | 215 +++++++++++------- app/config/specs/swagger2-latest-server.json | 180 ++++++++------- 6 files changed, 460 insertions(+), 344 deletions(-) diff --git a/app/config/specs/open-api3-latest-client.json b/app/config/specs/open-api3-latest-client.json index 820b1f55e0..316fe13116 100644 --- a/app/config/specs/open-api3-latest-client.json +++ b/app/config/specs/open-api3-latest-client.json @@ -3383,7 +3383,7 @@ "parameters": [ { "name": "code", - "description": "Credit Card Code. Possible values: amex, argencard, cabal, cencosud, diners, discover, elo, hipercard, jcb, mastercard, naranja, targeta-shopping, union-china-pay, visa, mir, maestro.", + "description": "Credit Card Code. Possible values: amex, argencard, cabal, cencosud, diners, discover, elo, hipercard, jcb, mastercard, naranja, targeta-shopping, union-china-pay, visa, mir, maestro, rupay.", "required": true, "schema": { "type": "string", @@ -3404,7 +3404,8 @@ "union-china-pay", "visa", "mir", - "maestro" + "maestro", + "rupay" ], "x-enum-name": "CreditCard", "x-enum-keys": [ @@ -3423,7 +3424,8 @@ "Union China Pay", "Visa", "MIR", - "Maestro" + "Maestro", + "Rupay" ] }, "in": "path" @@ -4365,7 +4367,7 @@ "tags": [ "databases" ], - "description": "Create a new Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console.", + "description": "Create a new Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console.\n", "responses": { "201": { "description": "Document", diff --git a/app/config/specs/open-api3-latest-console.json b/app/config/specs/open-api3-latest-console.json index 7f57dfc437..5b1d6f0d1e 100644 --- a/app/config/specs/open-api3-latest-console.json +++ b/app/config/specs/open-api3-latest-console.json @@ -3387,7 +3387,7 @@ "parameters": [ { "name": "code", - "description": "Credit Card Code. Possible values: amex, argencard, cabal, cencosud, diners, discover, elo, hipercard, jcb, mastercard, naranja, targeta-shopping, union-china-pay, visa, mir, maestro.", + "description": "Credit Card Code. Possible values: amex, argencard, cabal, cencosud, diners, discover, elo, hipercard, jcb, mastercard, naranja, targeta-shopping, union-china-pay, visa, mir, maestro, rupay.", "required": true, "schema": { "type": "string", @@ -3408,7 +3408,8 @@ "union-china-pay", "visa", "mir", - "maestro" + "maestro", + "rupay" ], "x-enum-name": "CreditCard", "x-enum-keys": [ @@ -3427,7 +3428,8 @@ "Union China Pay", "Visa", "MIR", - "Maestro" + "Maestro", + "Rupay" ] }, "in": "path" @@ -7823,7 +7825,7 @@ "tags": [ "databases" ], - "description": "Create a new Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console.", + "description": "Create a new Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console.\n", "responses": { "201": { "description": "Document", @@ -11585,7 +11587,7 @@ }, "x-appwrite": { "method": "getCertificate", - "weight": 134, + "weight": 133, "cookies": false, "type": "", "deprecated": false, @@ -11692,7 +11694,7 @@ }, "x-appwrite": { "method": "getPubSub", - "weight": 130, + "weight": 129, "cookies": false, "type": "", "deprecated": false, @@ -11718,54 +11720,6 @@ ] } }, - "\/health\/queue": { - "get": { - "summary": "Get queue", - "operationId": "healthGetQueue", - "tags": [ - "health" - ], - "description": "Check the Appwrite queue messaging servers are up and connection is successful.", - "responses": { - "200": { - "description": "Health Status", - "content": { - "application\/json": { - "schema": { - "$ref": "#\/components\/schemas\/healthStatus" - } - } - } - } - }, - "x-appwrite": { - "method": "getQueue", - "weight": 129, - "cookies": false, - "type": "", - "deprecated": false, - "demo": "health\/get-queue.md", - "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue.md", - "rate-limit": 0, - "rate-time": 3600, - "rate-key": "url:{url},ip:{ip}", - "scope": "health.read", - "platforms": [ - "server" - ], - "packaging": false, - "auth": { - "Project": [] - } - }, - "security": [ - { - "Project": [], - "Key": [] - } - ] - } - }, "\/health\/queue\/builds": { "get": { "summary": "Get builds queue", @@ -11788,7 +11742,7 @@ }, "x-appwrite": { "method": "getQueueBuilds", - "weight": 136, + "weight": 135, "cookies": false, "type": "", "deprecated": false, @@ -11849,7 +11803,7 @@ }, "x-appwrite": { "method": "getQueueCertificates", - "weight": 135, + "weight": 134, "cookies": false, "type": "", "deprecated": false, @@ -11910,7 +11864,7 @@ }, "x-appwrite": { "method": "getQueueDatabases", - "weight": 137, + "weight": 136, "cookies": false, "type": "", "deprecated": false, @@ -11982,7 +11936,7 @@ }, "x-appwrite": { "method": "getQueueDeletes", - "weight": 138, + "weight": 137, "cookies": false, "type": "", "deprecated": false, @@ -12081,8 +12035,9 @@ "v1-audits", "v1-mails", "v1-functions", - "v1-usage", - "v1-usage-dump", + "v1-stats-resources", + "v1-stats-usage", + "v1-stats-usage-dump", "v1-webhooks", "v1-certificates", "v1-builds", @@ -12130,7 +12085,7 @@ }, "x-appwrite": { "method": "getQueueFunctions", - "weight": 142, + "weight": 141, "cookies": false, "type": "", "deprecated": false, @@ -12191,7 +12146,7 @@ }, "x-appwrite": { "method": "getQueueLogs", - "weight": 133, + "weight": 132, "cookies": false, "type": "", "deprecated": false, @@ -12252,7 +12207,7 @@ }, "x-appwrite": { "method": "getQueueMails", - "weight": 139, + "weight": 138, "cookies": false, "type": "", "deprecated": false, @@ -12313,7 +12268,7 @@ }, "x-appwrite": { "method": "getQueueMessaging", - "weight": 140, + "weight": 139, "cookies": false, "type": "", "deprecated": false, @@ -12374,7 +12329,7 @@ }, "x-appwrite": { "method": "getQueueMigrations", - "weight": 141, + "weight": 140, "cookies": false, "type": "", "deprecated": false, @@ -12413,14 +12368,14 @@ ] } }, - "\/health\/queue\/usage": { + "\/health\/queue\/stats-resources": { "get": { - "summary": "Get usage queue", - "operationId": "healthGetQueueUsage", + "summary": "Get stats resources queue", + "operationId": "healthGetQueueStatsResources", "tags": [ "health" ], - "description": "Get the number of metrics that are waiting to be processed in the Appwrite internal queue server.", + "description": "Get the number of metrics that are waiting to be processed in the Appwrite stats resources queue.", "responses": { "200": { "description": "Health Queue", @@ -12434,13 +12389,13 @@ } }, "x-appwrite": { - "method": "getQueueUsage", - "weight": 143, + "method": "getQueueStatsResources", + "weight": 142, "cookies": false, "type": "", "deprecated": false, - "demo": "health\/get-queue-usage.md", - "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-usage.md", + "demo": "health\/get-queue-stats-resources.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-stats-resources.md", "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", @@ -12474,10 +12429,71 @@ ] } }, - "\/health\/queue\/usage-dump": { + "\/health\/queue\/stats-usage": { + "get": { + "summary": "Get stats usage queue", + "operationId": "healthGetQueueUsage", + "tags": [ + "health" + ], + "description": "Get the number of metrics that are waiting to be processed in the Appwrite internal queue server.", + "responses": { + "200": { + "description": "Health Queue", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/healthQueue" + } + } + } + } + }, + "x-appwrite": { + "method": "getQueueUsage", + "weight": 143, + "cookies": false, + "type": "", + "deprecated": false, + "demo": "health\/get-queue-usage.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-stats-usage.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "health.read", + "platforms": [ + "server" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Key": [] + } + ], + "parameters": [ + { + "name": "threshold", + "description": "Queue size threshold. When hit (equal or higher), endpoint returns server error. Default value is 5000.", + "required": false, + "schema": { + "type": "integer", + "format": "int32", + "default": 5000 + }, + "in": "query" + } + ] + } + }, + "\/health\/queue\/stats-usage-dump": { "get": { "summary": "Get usage dump queue", - "operationId": "healthGetQueueUsageDump", + "operationId": "healthGetQueueStatsUsageDump", "tags": [ "health" ], @@ -12495,13 +12511,13 @@ } }, "x-appwrite": { - "method": "getQueueUsageDump", + "method": "getQueueStatsUsageDump", "weight": 144, "cookies": false, "type": "", "deprecated": false, - "demo": "health\/get-queue-usage-dump.md", - "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-usage-dump.md", + "demo": "health\/get-queue-stats-usage-dump.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-stats-usage-dump.md", "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", @@ -12557,7 +12573,7 @@ }, "x-appwrite": { "method": "getQueueWebhooks", - "weight": 132, + "weight": 131, "cookies": false, "type": "", "deprecated": false, @@ -12714,7 +12730,7 @@ }, "x-appwrite": { "method": "getTime", - "weight": 131, + "weight": 130, "cookies": false, "type": "", "deprecated": false, @@ -17609,17 +17625,17 @@ }, "endpoint": { "type": "string", - "description": "Source's Appwrite Endpoint", + "description": "Source Appwrite endpoint", "x-example": "https:\/\/example.com" }, "projectId": { "type": "string", - "description": "Source's Project ID", + "description": "Source Project ID", "x-example": "" }, "apiKey": { "type": "string", - "description": "Source's API Key", + "description": "Source API Key", "x-example": "" } }, @@ -36052,6 +36068,20 @@ "$ref": "#\/components\/schemas\/metric" }, "x-example": [] + }, + "imageTransformations": { + "type": "array", + "description": "Aggregated number of files transformations per period.", + "items": { + "$ref": "#\/components\/schemas\/metric" + }, + "x-example": [] + }, + "imageTransformationsTotal": { + "type": "integer", + "description": "Total aggregated number of files transformations.", + "x-example": 0, + "format": "int32" } }, "required": [ @@ -36059,7 +36089,9 @@ "filesTotal", "filesStorageTotal", "files", - "storage" + "storage", + "imageTransformations", + "imageTransformationsTotal" ] }, "usageFunctions": { @@ -36597,6 +36629,14 @@ "$ref": "#\/components\/schemas\/metric" }, "x-example": [] + }, + "imageTransformations": { + "type": "array", + "description": "An array of aggregated number of image transformations.", + "items": { + "$ref": "#\/components\/schemas\/metric" + }, + "x-example": [] } }, "required": [ @@ -36628,7 +36668,8 @@ "authPhoneEstimate", "authPhoneCountryBreakdown", "databasesReads", - "databasesWrites" + "databasesWrites", + "imageTransformations" ] }, "headers": { diff --git a/app/config/specs/open-api3-latest-server.json b/app/config/specs/open-api3-latest-server.json index 68d408762a..280c080514 100644 --- a/app/config/specs/open-api3-latest-server.json +++ b/app/config/specs/open-api3-latest-server.json @@ -3077,7 +3077,7 @@ "parameters": [ { "name": "code", - "description": "Credit Card Code. Possible values: amex, argencard, cabal, cencosud, diners, discover, elo, hipercard, jcb, mastercard, naranja, targeta-shopping, union-china-pay, visa, mir, maestro.", + "description": "Credit Card Code. Possible values: amex, argencard, cabal, cencosud, diners, discover, elo, hipercard, jcb, mastercard, naranja, targeta-shopping, union-china-pay, visa, mir, maestro, rupay.", "required": true, "schema": { "type": "string", @@ -3098,7 +3098,8 @@ "union-china-pay", "visa", "mir", - "maestro" + "maestro", + "rupay" ], "x-enum-name": "CreditCard", "x-enum-keys": [ @@ -3117,7 +3118,8 @@ "Union China Pay", "Visa", "MIR", - "Maestro" + "Maestro", + "Rupay" ] }, "in": "path" @@ -7381,7 +7383,7 @@ "tags": [ "databases" ], - "description": "Create a new Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console.", + "description": "Create a new Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console.\n", "responses": { "201": { "description": "Document", @@ -10461,7 +10463,7 @@ }, "x-appwrite": { "method": "getCertificate", - "weight": 134, + "weight": 133, "cookies": false, "type": "", "deprecated": false, @@ -10570,7 +10572,7 @@ }, "x-appwrite": { "method": "getPubSub", - "weight": 130, + "weight": 129, "cookies": false, "type": "", "deprecated": false, @@ -10597,55 +10599,6 @@ ] } }, - "\/health\/queue": { - "get": { - "summary": "Get queue", - "operationId": "healthGetQueue", - "tags": [ - "health" - ], - "description": "Check the Appwrite queue messaging servers are up and connection is successful.", - "responses": { - "200": { - "description": "Health Status", - "content": { - "application\/json": { - "schema": { - "$ref": "#\/components\/schemas\/healthStatus" - } - } - } - } - }, - "x-appwrite": { - "method": "getQueue", - "weight": 129, - "cookies": false, - "type": "", - "deprecated": false, - "demo": "health\/get-queue.md", - "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue.md", - "rate-limit": 0, - "rate-time": 3600, - "rate-key": "url:{url},ip:{ip}", - "scope": "health.read", - "platforms": [ - "server" - ], - "packaging": false, - "auth": { - "Project": [], - "Key": [] - } - }, - "security": [ - { - "Project": [], - "Key": [] - } - ] - } - }, "\/health\/queue\/builds": { "get": { "summary": "Get builds queue", @@ -10668,7 +10621,7 @@ }, "x-appwrite": { "method": "getQueueBuilds", - "weight": 136, + "weight": 135, "cookies": false, "type": "", "deprecated": false, @@ -10730,7 +10683,7 @@ }, "x-appwrite": { "method": "getQueueCertificates", - "weight": 135, + "weight": 134, "cookies": false, "type": "", "deprecated": false, @@ -10792,7 +10745,7 @@ }, "x-appwrite": { "method": "getQueueDatabases", - "weight": 137, + "weight": 136, "cookies": false, "type": "", "deprecated": false, @@ -10865,7 +10818,7 @@ }, "x-appwrite": { "method": "getQueueDeletes", - "weight": 138, + "weight": 137, "cookies": false, "type": "", "deprecated": false, @@ -10966,8 +10919,9 @@ "v1-audits", "v1-mails", "v1-functions", - "v1-usage", - "v1-usage-dump", + "v1-stats-resources", + "v1-stats-usage", + "v1-stats-usage-dump", "v1-webhooks", "v1-certificates", "v1-builds", @@ -11015,7 +10969,7 @@ }, "x-appwrite": { "method": "getQueueFunctions", - "weight": 142, + "weight": 141, "cookies": false, "type": "", "deprecated": false, @@ -11077,7 +11031,7 @@ }, "x-appwrite": { "method": "getQueueLogs", - "weight": 133, + "weight": 132, "cookies": false, "type": "", "deprecated": false, @@ -11139,7 +11093,7 @@ }, "x-appwrite": { "method": "getQueueMails", - "weight": 139, + "weight": 138, "cookies": false, "type": "", "deprecated": false, @@ -11201,7 +11155,7 @@ }, "x-appwrite": { "method": "getQueueMessaging", - "weight": 140, + "weight": 139, "cookies": false, "type": "", "deprecated": false, @@ -11263,7 +11217,7 @@ }, "x-appwrite": { "method": "getQueueMigrations", - "weight": 141, + "weight": 140, "cookies": false, "type": "", "deprecated": false, @@ -11303,14 +11257,14 @@ ] } }, - "\/health\/queue\/usage": { + "\/health\/queue\/stats-resources": { "get": { - "summary": "Get usage queue", - "operationId": "healthGetQueueUsage", + "summary": "Get stats resources queue", + "operationId": "healthGetQueueStatsResources", "tags": [ "health" ], - "description": "Get the number of metrics that are waiting to be processed in the Appwrite internal queue server.", + "description": "Get the number of metrics that are waiting to be processed in the Appwrite stats resources queue.", "responses": { "200": { "description": "Health Queue", @@ -11324,13 +11278,13 @@ } }, "x-appwrite": { - "method": "getQueueUsage", - "weight": 143, + "method": "getQueueStatsResources", + "weight": 142, "cookies": false, "type": "", "deprecated": false, - "demo": "health\/get-queue-usage.md", - "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-usage.md", + "demo": "health\/get-queue-stats-resources.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-stats-resources.md", "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", @@ -11365,10 +11319,72 @@ ] } }, - "\/health\/queue\/usage-dump": { + "\/health\/queue\/stats-usage": { + "get": { + "summary": "Get stats usage queue", + "operationId": "healthGetQueueUsage", + "tags": [ + "health" + ], + "description": "Get the number of metrics that are waiting to be processed in the Appwrite internal queue server.", + "responses": { + "200": { + "description": "Health Queue", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/healthQueue" + } + } + } + } + }, + "x-appwrite": { + "method": "getQueueUsage", + "weight": 143, + "cookies": false, + "type": "", + "deprecated": false, + "demo": "health\/get-queue-usage.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-stats-usage.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "health.read", + "platforms": [ + "server" + ], + "packaging": false, + "auth": { + "Project": [], + "Key": [] + } + }, + "security": [ + { + "Project": [], + "Key": [] + } + ], + "parameters": [ + { + "name": "threshold", + "description": "Queue size threshold. When hit (equal or higher), endpoint returns server error. Default value is 5000.", + "required": false, + "schema": { + "type": "integer", + "format": "int32", + "default": 5000 + }, + "in": "query" + } + ] + } + }, + "\/health\/queue\/stats-usage-dump": { "get": { "summary": "Get usage dump queue", - "operationId": "healthGetQueueUsageDump", + "operationId": "healthGetQueueStatsUsageDump", "tags": [ "health" ], @@ -11386,13 +11402,13 @@ } }, "x-appwrite": { - "method": "getQueueUsageDump", + "method": "getQueueStatsUsageDump", "weight": 144, "cookies": false, "type": "", "deprecated": false, - "demo": "health\/get-queue-usage-dump.md", - "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-usage-dump.md", + "demo": "health\/get-queue-stats-usage-dump.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-stats-usage-dump.md", "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", @@ -11449,7 +11465,7 @@ }, "x-appwrite": { "method": "getQueueWebhooks", - "weight": 132, + "weight": 131, "cookies": false, "type": "", "deprecated": false, @@ -11609,7 +11625,7 @@ }, "x-appwrite": { "method": "getTime", - "weight": 131, + "weight": 130, "cookies": false, "type": "", "deprecated": false, diff --git a/app/config/specs/swagger2-latest-client.json b/app/config/specs/swagger2-latest-client.json index c0980c44ce..8960bfaa5c 100644 --- a/app/config/specs/swagger2-latest-client.json +++ b/app/config/specs/swagger2-latest-client.json @@ -3557,7 +3557,7 @@ "parameters": [ { "name": "code", - "description": "Credit Card Code. Possible values: amex, argencard, cabal, cencosud, diners, discover, elo, hipercard, jcb, mastercard, naranja, targeta-shopping, union-china-pay, visa, mir, maestro.", + "description": "Credit Card Code. Possible values: amex, argencard, cabal, cencosud, diners, discover, elo, hipercard, jcb, mastercard, naranja, targeta-shopping, union-china-pay, visa, mir, maestro, rupay.", "required": true, "type": "string", "x-example": "amex", @@ -3577,7 +3577,8 @@ "union-china-pay", "visa", "mir", - "maestro" + "maestro", + "rupay" ], "x-enum-name": "CreditCard", "x-enum-keys": [ @@ -3596,7 +3597,8 @@ "Union China Pay", "Visa", "MIR", - "Maestro" + "Maestro", + "Rupay" ], "in": "path" }, @@ -4547,7 +4549,7 @@ "tags": [ "databases" ], - "description": "Create a new Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console.", + "description": "Create a new Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console.\n", "responses": { "201": { "description": "Document", diff --git a/app/config/specs/swagger2-latest-console.json b/app/config/specs/swagger2-latest-console.json index 94b0d55199..f13db98150 100644 --- a/app/config/specs/swagger2-latest-console.json +++ b/app/config/specs/swagger2-latest-console.json @@ -3577,7 +3577,7 @@ "parameters": [ { "name": "code", - "description": "Credit Card Code. Possible values: amex, argencard, cabal, cencosud, diners, discover, elo, hipercard, jcb, mastercard, naranja, targeta-shopping, union-china-pay, visa, mir, maestro.", + "description": "Credit Card Code. Possible values: amex, argencard, cabal, cencosud, diners, discover, elo, hipercard, jcb, mastercard, naranja, targeta-shopping, union-china-pay, visa, mir, maestro, rupay.", "required": true, "type": "string", "x-example": "amex", @@ -3597,7 +3597,8 @@ "union-china-pay", "visa", "mir", - "maestro" + "maestro", + "rupay" ], "x-enum-name": "CreditCard", "x-enum-keys": [ @@ -3616,7 +3617,8 @@ "Union China Pay", "Visa", "MIR", - "Maestro" + "Maestro", + "Rupay" ], "in": "path" }, @@ -8012,7 +8014,7 @@ "tags": [ "databases" ], - "description": "Create a new Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console.", + "description": "Create a new Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console.\n", "responses": { "201": { "description": "Document", @@ -11810,7 +11812,7 @@ }, "x-appwrite": { "method": "getCertificate", - "weight": 134, + "weight": 133, "cookies": false, "type": "", "deprecated": false, @@ -11919,7 +11921,7 @@ }, "x-appwrite": { "method": "getPubSub", - "weight": 130, + "weight": 129, "cookies": false, "type": "", "deprecated": false, @@ -11945,56 +11947,6 @@ ] } }, - "\/health\/queue": { - "get": { - "summary": "Get queue", - "operationId": "healthGetQueue", - "consumes": [ - "application\/json" - ], - "produces": [ - "application\/json" - ], - "tags": [ - "health" - ], - "description": "Check the Appwrite queue messaging servers are up and connection is successful.", - "responses": { - "200": { - "description": "Health Status", - "schema": { - "$ref": "#\/definitions\/healthStatus" - } - } - }, - "x-appwrite": { - "method": "getQueue", - "weight": 129, - "cookies": false, - "type": "", - "deprecated": false, - "demo": "health\/get-queue.md", - "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue.md", - "rate-limit": 0, - "rate-time": 3600, - "rate-key": "url:{url},ip:{ip}", - "scope": "health.read", - "platforms": [ - "server" - ], - "packaging": false, - "auth": { - "Project": [] - } - }, - "security": [ - { - "Project": [], - "Key": [] - } - ] - } - }, "\/health\/queue\/builds": { "get": { "summary": "Get builds queue", @@ -12019,7 +11971,7 @@ }, "x-appwrite": { "method": "getQueueBuilds", - "weight": 136, + "weight": 135, "cookies": false, "type": "", "deprecated": false, @@ -12080,7 +12032,7 @@ }, "x-appwrite": { "method": "getQueueCertificates", - "weight": 135, + "weight": 134, "cookies": false, "type": "", "deprecated": false, @@ -12141,7 +12093,7 @@ }, "x-appwrite": { "method": "getQueueDatabases", - "weight": 137, + "weight": 136, "cookies": false, "type": "", "deprecated": false, @@ -12211,7 +12163,7 @@ }, "x-appwrite": { "method": "getQueueDeletes", - "weight": 138, + "weight": 137, "cookies": false, "type": "", "deprecated": false, @@ -12309,8 +12261,9 @@ "v1-audits", "v1-mails", "v1-functions", - "v1-usage", - "v1-usage-dump", + "v1-stats-resources", + "v1-stats-usage", + "v1-stats-usage-dump", "v1-webhooks", "v1-certificates", "v1-builds", @@ -12357,7 +12310,7 @@ }, "x-appwrite": { "method": "getQueueFunctions", - "weight": 142, + "weight": 141, "cookies": false, "type": "", "deprecated": false, @@ -12418,7 +12371,7 @@ }, "x-appwrite": { "method": "getQueueLogs", - "weight": 133, + "weight": 132, "cookies": false, "type": "", "deprecated": false, @@ -12479,7 +12432,7 @@ }, "x-appwrite": { "method": "getQueueMails", - "weight": 139, + "weight": 138, "cookies": false, "type": "", "deprecated": false, @@ -12540,7 +12493,7 @@ }, "x-appwrite": { "method": "getQueueMessaging", - "weight": 140, + "weight": 139, "cookies": false, "type": "", "deprecated": false, @@ -12601,7 +12554,7 @@ }, "x-appwrite": { "method": "getQueueMigrations", - "weight": 141, + "weight": 140, "cookies": false, "type": "", "deprecated": false, @@ -12638,10 +12591,10 @@ ] } }, - "\/health\/queue\/usage": { + "\/health\/queue\/stats-resources": { "get": { - "summary": "Get usage queue", - "operationId": "healthGetQueueUsage", + "summary": "Get stats resources queue", + "operationId": "healthGetQueueStatsResources", "consumes": [ "application\/json" ], @@ -12651,7 +12604,7 @@ "tags": [ "health" ], - "description": "Get the number of metrics that are waiting to be processed in the Appwrite internal queue server.", + "description": "Get the number of metrics that are waiting to be processed in the Appwrite stats resources queue.", "responses": { "200": { "description": "Health Queue", @@ -12661,13 +12614,13 @@ } }, "x-appwrite": { - "method": "getQueueUsage", - "weight": 143, + "method": "getQueueStatsResources", + "weight": 142, "cookies": false, "type": "", "deprecated": false, - "demo": "health\/get-queue-usage.md", - "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-usage.md", + "demo": "health\/get-queue-stats-resources.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-stats-resources.md", "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", @@ -12699,10 +12652,71 @@ ] } }, - "\/health\/queue\/usage-dump": { + "\/health\/queue\/stats-usage": { + "get": { + "summary": "Get stats usage queue", + "operationId": "healthGetQueueUsage", + "consumes": [ + "application\/json" + ], + "produces": [ + "application\/json" + ], + "tags": [ + "health" + ], + "description": "Get the number of metrics that are waiting to be processed in the Appwrite internal queue server.", + "responses": { + "200": { + "description": "Health Queue", + "schema": { + "$ref": "#\/definitions\/healthQueue" + } + } + }, + "x-appwrite": { + "method": "getQueueUsage", + "weight": 143, + "cookies": false, + "type": "", + "deprecated": false, + "demo": "health\/get-queue-usage.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-stats-usage.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "health.read", + "platforms": [ + "server" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Key": [] + } + ], + "parameters": [ + { + "name": "threshold", + "description": "Queue size threshold. When hit (equal or higher), endpoint returns server error. Default value is 5000.", + "required": false, + "type": "integer", + "format": "int32", + "default": 5000, + "in": "query" + } + ] + } + }, + "\/health\/queue\/stats-usage-dump": { "get": { "summary": "Get usage dump queue", - "operationId": "healthGetQueueUsageDump", + "operationId": "healthGetQueueStatsUsageDump", "consumes": [ "application\/json" ], @@ -12722,13 +12736,13 @@ } }, "x-appwrite": { - "method": "getQueueUsageDump", + "method": "getQueueStatsUsageDump", "weight": 144, "cookies": false, "type": "", "deprecated": false, - "demo": "health\/get-queue-usage-dump.md", - "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-usage-dump.md", + "demo": "health\/get-queue-stats-usage-dump.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-stats-usage-dump.md", "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", @@ -12784,7 +12798,7 @@ }, "x-appwrite": { "method": "getQueueWebhooks", - "weight": 132, + "weight": 131, "cookies": false, "type": "", "deprecated": false, @@ -12945,7 +12959,7 @@ }, "x-appwrite": { "method": "getTime", - "weight": 131, + "weight": 130, "cookies": false, "type": "", "deprecated": false, @@ -18070,19 +18084,19 @@ }, "endpoint": { "type": "string", - "description": "Source's Appwrite Endpoint", + "description": "Source Appwrite endpoint", "default": null, "x-example": "https:\/\/example.com" }, "projectId": { "type": "string", - "description": "Source's Project ID", + "description": "Source Project ID", "default": null, "x-example": "" }, "apiKey": { "type": "string", - "description": "Source's API Key", + "description": "Source API Key", "default": null, "x-example": "" } @@ -36608,6 +36622,21 @@ "$ref": "#\/definitions\/metric" }, "x-example": [] + }, + "imageTransformations": { + "type": "array", + "description": "Aggregated number of files transformations per period.", + "items": { + "type": "object", + "$ref": "#\/definitions\/metric" + }, + "x-example": [] + }, + "imageTransformationsTotal": { + "type": "integer", + "description": "Total aggregated number of files transformations.", + "x-example": 0, + "format": "int32" } }, "required": [ @@ -36615,7 +36644,9 @@ "filesTotal", "filesStorageTotal", "files", - "storage" + "storage", + "imageTransformations", + "imageTransformationsTotal" ] }, "usageFunctions": { @@ -37185,6 +37216,15 @@ "$ref": "#\/definitions\/metric" }, "x-example": [] + }, + "imageTransformations": { + "type": "array", + "description": "An array of aggregated number of image transformations.", + "items": { + "type": "object", + "$ref": "#\/definitions\/metric" + }, + "x-example": [] } }, "required": [ @@ -37216,7 +37256,8 @@ "authPhoneEstimate", "authPhoneCountryBreakdown", "databasesReads", - "databasesWrites" + "databasesWrites", + "imageTransformations" ] }, "headers": { diff --git a/app/config/specs/swagger2-latest-server.json b/app/config/specs/swagger2-latest-server.json index e38495629c..749f87553b 100644 --- a/app/config/specs/swagger2-latest-server.json +++ b/app/config/specs/swagger2-latest-server.json @@ -3261,7 +3261,7 @@ "parameters": [ { "name": "code", - "description": "Credit Card Code. Possible values: amex, argencard, cabal, cencosud, diners, discover, elo, hipercard, jcb, mastercard, naranja, targeta-shopping, union-china-pay, visa, mir, maestro.", + "description": "Credit Card Code. Possible values: amex, argencard, cabal, cencosud, diners, discover, elo, hipercard, jcb, mastercard, naranja, targeta-shopping, union-china-pay, visa, mir, maestro, rupay.", "required": true, "type": "string", "x-example": "amex", @@ -3281,7 +3281,8 @@ "union-china-pay", "visa", "mir", - "maestro" + "maestro", + "rupay" ], "x-enum-name": "CreditCard", "x-enum-keys": [ @@ -3300,7 +3301,8 @@ "Union China Pay", "Visa", "MIR", - "Maestro" + "Maestro", + "Rupay" ], "in": "path" }, @@ -7552,7 +7554,7 @@ "tags": [ "databases" ], - "description": "Create a new Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console.", + "description": "Create a new Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console.\n", "responses": { "201": { "description": "Document", @@ -10689,7 +10691,7 @@ }, "x-appwrite": { "method": "getCertificate", - "weight": 134, + "weight": 133, "cookies": false, "type": "", "deprecated": false, @@ -10800,7 +10802,7 @@ }, "x-appwrite": { "method": "getPubSub", - "weight": 130, + "weight": 129, "cookies": false, "type": "", "deprecated": false, @@ -10827,57 +10829,6 @@ ] } }, - "\/health\/queue": { - "get": { - "summary": "Get queue", - "operationId": "healthGetQueue", - "consumes": [ - "application\/json" - ], - "produces": [ - "application\/json" - ], - "tags": [ - "health" - ], - "description": "Check the Appwrite queue messaging servers are up and connection is successful.", - "responses": { - "200": { - "description": "Health Status", - "schema": { - "$ref": "#\/definitions\/healthStatus" - } - } - }, - "x-appwrite": { - "method": "getQueue", - "weight": 129, - "cookies": false, - "type": "", - "deprecated": false, - "demo": "health\/get-queue.md", - "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue.md", - "rate-limit": 0, - "rate-time": 3600, - "rate-key": "url:{url},ip:{ip}", - "scope": "health.read", - "platforms": [ - "server" - ], - "packaging": false, - "auth": { - "Project": [], - "Key": [] - } - }, - "security": [ - { - "Project": [], - "Key": [] - } - ] - } - }, "\/health\/queue\/builds": { "get": { "summary": "Get builds queue", @@ -10902,7 +10853,7 @@ }, "x-appwrite": { "method": "getQueueBuilds", - "weight": 136, + "weight": 135, "cookies": false, "type": "", "deprecated": false, @@ -10964,7 +10915,7 @@ }, "x-appwrite": { "method": "getQueueCertificates", - "weight": 135, + "weight": 134, "cookies": false, "type": "", "deprecated": false, @@ -11026,7 +10977,7 @@ }, "x-appwrite": { "method": "getQueueDatabases", - "weight": 137, + "weight": 136, "cookies": false, "type": "", "deprecated": false, @@ -11097,7 +11048,7 @@ }, "x-appwrite": { "method": "getQueueDeletes", - "weight": 138, + "weight": 137, "cookies": false, "type": "", "deprecated": false, @@ -11197,8 +11148,9 @@ "v1-audits", "v1-mails", "v1-functions", - "v1-usage", - "v1-usage-dump", + "v1-stats-resources", + "v1-stats-usage", + "v1-stats-usage-dump", "v1-webhooks", "v1-certificates", "v1-builds", @@ -11245,7 +11197,7 @@ }, "x-appwrite": { "method": "getQueueFunctions", - "weight": 142, + "weight": 141, "cookies": false, "type": "", "deprecated": false, @@ -11307,7 +11259,7 @@ }, "x-appwrite": { "method": "getQueueLogs", - "weight": 133, + "weight": 132, "cookies": false, "type": "", "deprecated": false, @@ -11369,7 +11321,7 @@ }, "x-appwrite": { "method": "getQueueMails", - "weight": 139, + "weight": 138, "cookies": false, "type": "", "deprecated": false, @@ -11431,7 +11383,7 @@ }, "x-appwrite": { "method": "getQueueMessaging", - "weight": 140, + "weight": 139, "cookies": false, "type": "", "deprecated": false, @@ -11493,7 +11445,7 @@ }, "x-appwrite": { "method": "getQueueMigrations", - "weight": 141, + "weight": 140, "cookies": false, "type": "", "deprecated": false, @@ -11531,10 +11483,10 @@ ] } }, - "\/health\/queue\/usage": { + "\/health\/queue\/stats-resources": { "get": { - "summary": "Get usage queue", - "operationId": "healthGetQueueUsage", + "summary": "Get stats resources queue", + "operationId": "healthGetQueueStatsResources", "consumes": [ "application\/json" ], @@ -11544,7 +11496,7 @@ "tags": [ "health" ], - "description": "Get the number of metrics that are waiting to be processed in the Appwrite internal queue server.", + "description": "Get the number of metrics that are waiting to be processed in the Appwrite stats resources queue.", "responses": { "200": { "description": "Health Queue", @@ -11554,13 +11506,13 @@ } }, "x-appwrite": { - "method": "getQueueUsage", - "weight": 143, + "method": "getQueueStatsResources", + "weight": 142, "cookies": false, "type": "", "deprecated": false, - "demo": "health\/get-queue-usage.md", - "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-usage.md", + "demo": "health\/get-queue-stats-resources.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-stats-resources.md", "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", @@ -11593,10 +11545,72 @@ ] } }, - "\/health\/queue\/usage-dump": { + "\/health\/queue\/stats-usage": { + "get": { + "summary": "Get stats usage queue", + "operationId": "healthGetQueueUsage", + "consumes": [ + "application\/json" + ], + "produces": [ + "application\/json" + ], + "tags": [ + "health" + ], + "description": "Get the number of metrics that are waiting to be processed in the Appwrite internal queue server.", + "responses": { + "200": { + "description": "Health Queue", + "schema": { + "$ref": "#\/definitions\/healthQueue" + } + } + }, + "x-appwrite": { + "method": "getQueueUsage", + "weight": 143, + "cookies": false, + "type": "", + "deprecated": false, + "demo": "health\/get-queue-usage.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-stats-usage.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "health.read", + "platforms": [ + "server" + ], + "packaging": false, + "auth": { + "Project": [], + "Key": [] + } + }, + "security": [ + { + "Project": [], + "Key": [] + } + ], + "parameters": [ + { + "name": "threshold", + "description": "Queue size threshold. When hit (equal or higher), endpoint returns server error. Default value is 5000.", + "required": false, + "type": "integer", + "format": "int32", + "default": 5000, + "in": "query" + } + ] + } + }, + "\/health\/queue\/stats-usage-dump": { "get": { "summary": "Get usage dump queue", - "operationId": "healthGetQueueUsageDump", + "operationId": "healthGetQueueStatsUsageDump", "consumes": [ "application\/json" ], @@ -11616,13 +11630,13 @@ } }, "x-appwrite": { - "method": "getQueueUsageDump", + "method": "getQueueStatsUsageDump", "weight": 144, "cookies": false, "type": "", "deprecated": false, - "demo": "health\/get-queue-usage-dump.md", - "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-usage-dump.md", + "demo": "health\/get-queue-stats-usage-dump.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-stats-usage-dump.md", "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", @@ -11679,7 +11693,7 @@ }, "x-appwrite": { "method": "getQueueWebhooks", - "weight": 132, + "weight": 131, "cookies": false, "type": "", "deprecated": false, @@ -11843,7 +11857,7 @@ }, "x-appwrite": { "method": "getTime", - "weight": 131, + "weight": 130, "cookies": false, "type": "", "deprecated": false, From df3746271684ad08c1b1379bf9d784122bdd5262 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Fri, 28 Feb 2025 13:20:55 +0000 Subject: [PATCH 066/143] chore: fix typing and tests --- app/config/specs/open-api3-latest-console.json | 10 ++++------ app/config/specs/swagger2-latest-console.json | 11 ++++------- src/Appwrite/Utopia/Response/Model/UsageProject.php | 9 ++++----- tests/e2e/General/UsageTest.php | 2 +- 4 files changed, 13 insertions(+), 19 deletions(-) diff --git a/app/config/specs/open-api3-latest-console.json b/app/config/specs/open-api3-latest-console.json index 5b1d6f0d1e..5b31a62c23 100644 --- a/app/config/specs/open-api3-latest-console.json +++ b/app/config/specs/open-api3-latest-console.json @@ -36631,12 +36631,10 @@ "x-example": [] }, "imageTransformations": { - "type": "array", - "description": "An array of aggregated number of image transformations.", - "items": { - "$ref": "#\/components\/schemas\/metric" - }, - "x-example": [] + "type": "integer", + "description": "Total aggregated number of image transformations.", + "x-example": 0, + "format": "int32" } }, "required": [ diff --git a/app/config/specs/swagger2-latest-console.json b/app/config/specs/swagger2-latest-console.json index f13db98150..5c2dabd318 100644 --- a/app/config/specs/swagger2-latest-console.json +++ b/app/config/specs/swagger2-latest-console.json @@ -37218,13 +37218,10 @@ "x-example": [] }, "imageTransformations": { - "type": "array", - "description": "An array of aggregated number of image transformations.", - "items": { - "type": "object", - "$ref": "#\/definitions\/metric" - }, - "x-example": [] + "type": "integer", + "description": "Total aggregated number of image transformations.", + "x-example": 0, + "format": "int32" } }, "required": [ diff --git a/src/Appwrite/Utopia/Response/Model/UsageProject.php b/src/Appwrite/Utopia/Response/Model/UsageProject.php index 6f74ec561b..bdb7d6a897 100644 --- a/src/Appwrite/Utopia/Response/Model/UsageProject.php +++ b/src/Appwrite/Utopia/Response/Model/UsageProject.php @@ -198,11 +198,10 @@ class UsageProject extends Model 'array' => true ]) ->addRule('imageTransformations', [ - 'type' => Response::MODEL_METRIC, - 'description' => 'An array of aggregated number of image transformations.', - 'default' => [], - 'example' => [], - 'array' => true + 'type' => self::TYPE_INTEGER, + 'description' => 'Total aggregated number of image transformations.', + 'default' => 0, + 'example' => 0, ]) ; } diff --git a/tests/e2e/General/UsageTest.php b/tests/e2e/General/UsageTest.php index c0d0c80eb1..9061f95a96 100644 --- a/tests/e2e/General/UsageTest.php +++ b/tests/e2e/General/UsageTest.php @@ -150,7 +150,7 @@ class UsageTest extends Scope ); $this->assertEquals(200, $response['headers']['status-code']); - $this->assertEquals(29, count($response['body'])); + $this->assertEquals(30, count($response['body'])); $this->validateDates($response['body']['network']); $this->validateDates($response['body']['requests']); $this->validateDates($response['body']['users']); From 02de0d2432750728768fe6efb0a81cd21eff54d9 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Fri, 28 Feb 2025 13:29:32 +0000 Subject: [PATCH 067/143] chore: fix stats count --- tests/e2e/General/UsageTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/e2e/General/UsageTest.php b/tests/e2e/General/UsageTest.php index 9061f95a96..2e13dcdb98 100644 --- a/tests/e2e/General/UsageTest.php +++ b/tests/e2e/General/UsageTest.php @@ -331,7 +331,7 @@ class UsageTest extends Scope ] ); - $this->assertEquals(29, count($response['body'])); + $this->assertEquals(30, count($response['body'])); $this->assertEquals(1, count($response['body']['requests'])); $this->assertEquals($requestsTotal, $response['body']['requests'][array_key_last($response['body']['requests'])]['value']); $this->validateDates($response['body']['requests']); @@ -552,7 +552,7 @@ class UsageTest extends Scope ] ); - $this->assertEquals(29, count($response['body'])); + $this->assertEquals(30, count($response['body'])); $this->assertEquals(1, count($response['body']['requests'])); $this->assertEquals(1, count($response['body']['network'])); $this->assertEquals($requestsTotal, $response['body']['requests'][array_key_last($response['body']['requests'])]['value']); From 712c33064a3bd4d7af83278dab9ad13a17ff8759 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Sat, 1 Mar 2025 17:18:44 +0000 Subject: [PATCH 068/143] chore: fix sending of transformation stats --- app/controllers/api/project.php | 4 +++- src/Appwrite/Utopia/Response/Model/UsageProject.php | 6 ++++++ tests/e2e/General/UsageTest.php | 6 +++--- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/app/controllers/api/project.php b/app/controllers/api/project.php index cf2a128939..986264e3c5 100644 --- a/app/controllers/api/project.php +++ b/app/controllers/api/project.php @@ -76,6 +76,7 @@ App::get('/v1/project/usage') METRIC_BUILDS_MB_SECONDS, METRIC_DATABASES_OPERATIONS_READS, METRIC_DATABASES_OPERATIONS_WRITES, + METRIC_FILES_IMAGES_TRANSFORMED, ] ]; @@ -364,7 +365,8 @@ App::get('/v1/project/usage') 'authPhoneTotal' => $authPhoneTotal, 'authPhoneEstimate' => $authPhoneEstimate, 'authPhoneCountryBreakdown' => $authPhoneCountryBreakdown, - 'imageTransformations' => $total[METRIC_FILES_IMAGES_TRANSFORMED], + 'imageTransformations' => $usage[METRIC_FILES_IMAGES_TRANSFORMED], + 'imageTransformationsTotal' => $total[METRIC_FILES_IMAGES_TRANSFORMED], ]), Response::MODEL_USAGE_PROJECT); }); diff --git a/src/Appwrite/Utopia/Response/Model/UsageProject.php b/src/Appwrite/Utopia/Response/Model/UsageProject.php index bdb7d6a897..6ac8830ac5 100644 --- a/src/Appwrite/Utopia/Response/Model/UsageProject.php +++ b/src/Appwrite/Utopia/Response/Model/UsageProject.php @@ -197,6 +197,12 @@ class UsageProject extends Model 'example' => [], 'array' => true ]) + ->addRule('imageTransformationsTotal', [ + 'type' => self::TYPE_INTEGER, + 'description' => 'An array of aggregated number of image transformations.', + 'default' => 0, + 'example' => 0, + ]) ->addRule('imageTransformations', [ 'type' => self::TYPE_INTEGER, 'description' => 'Total aggregated number of image transformations.', diff --git a/tests/e2e/General/UsageTest.php b/tests/e2e/General/UsageTest.php index 2e13dcdb98..33a1408704 100644 --- a/tests/e2e/General/UsageTest.php +++ b/tests/e2e/General/UsageTest.php @@ -150,7 +150,7 @@ class UsageTest extends Scope ); $this->assertEquals(200, $response['headers']['status-code']); - $this->assertEquals(30, count($response['body'])); + $this->assertEquals(31, count($response['body'])); $this->validateDates($response['body']['network']); $this->validateDates($response['body']['requests']); $this->validateDates($response['body']['users']); @@ -331,7 +331,7 @@ class UsageTest extends Scope ] ); - $this->assertEquals(30, count($response['body'])); + $this->assertEquals(31, count($response['body'])); $this->assertEquals(1, count($response['body']['requests'])); $this->assertEquals($requestsTotal, $response['body']['requests'][array_key_last($response['body']['requests'])]['value']); $this->validateDates($response['body']['requests']); @@ -552,7 +552,7 @@ class UsageTest extends Scope ] ); - $this->assertEquals(30, count($response['body'])); + $this->assertEquals(31, count($response['body'])); $this->assertEquals(1, count($response['body']['requests'])); $this->assertEquals(1, count($response['body']['network'])); $this->assertEquals($requestsTotal, $response['body']['requests'][array_key_last($response['body']['requests'])]['value']); From aaee99e08b950affc760ee193776e0ec4d8df767 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Sat, 1 Mar 2025 17:19:55 +0000 Subject: [PATCH 069/143] chore: generate specs --- app/config/specs/open-api3-latest-console.json | 7 +++++++ app/config/specs/swagger2-latest-console.json | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/app/config/specs/open-api3-latest-console.json b/app/config/specs/open-api3-latest-console.json index 5b31a62c23..bc216226fb 100644 --- a/app/config/specs/open-api3-latest-console.json +++ b/app/config/specs/open-api3-latest-console.json @@ -36630,6 +36630,12 @@ }, "x-example": [] }, + "imageTransformationsTotal": { + "type": "integer", + "description": "An array of aggregated number of image transformations.", + "x-example": 0, + "format": "int32" + }, "imageTransformations": { "type": "integer", "description": "Total aggregated number of image transformations.", @@ -36667,6 +36673,7 @@ "authPhoneCountryBreakdown", "databasesReads", "databasesWrites", + "imageTransformationsTotal", "imageTransformations" ] }, diff --git a/app/config/specs/swagger2-latest-console.json b/app/config/specs/swagger2-latest-console.json index 5c2dabd318..2dddc72802 100644 --- a/app/config/specs/swagger2-latest-console.json +++ b/app/config/specs/swagger2-latest-console.json @@ -37217,6 +37217,12 @@ }, "x-example": [] }, + "imageTransformationsTotal": { + "type": "integer", + "description": "An array of aggregated number of image transformations.", + "x-example": 0, + "format": "int32" + }, "imageTransformations": { "type": "integer", "description": "Total aggregated number of image transformations.", @@ -37254,6 +37260,7 @@ "authPhoneCountryBreakdown", "databasesReads", "databasesWrites", + "imageTransformationsTotal", "imageTransformations" ] }, From 6900b717da261b71bfbb928c96e8fa264f6d5ea4 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Sat, 1 Mar 2025 17:45:20 +0000 Subject: [PATCH 070/143] chore: updated to use logsdb --- app/controllers/api/project.php | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/app/controllers/api/project.php b/app/controllers/api/project.php index 986264e3c5..b267e8e51e 100644 --- a/app/controllers/api/project.php +++ b/app/controllers/api/project.php @@ -40,14 +40,18 @@ App::get('/v1/project/usage') ->param('endDate', '', new DateTimeValidator(), 'End date for the usage') ->param('period', '1d', new WhiteList(['1h', '1d']), 'Period used', true) ->inject('response') + ->inject('project') ->inject('dbForProject') + ->inject('getLogsDB') ->inject('smsRates') - ->action(function (string $startDate, string $endDate, string $period, Response $response, Database $dbForProject, array $smsRates) { + ->action(function (string $startDate, string $endDate, string $period, Response $response, Document $project, Database $dbForProject, callable $getLogsDB, array $smsRates) { $stats = $total = $usage = []; $format = 'Y-m-d 00:00:00'; $firstDay = (new DateTime($startDate))->format($format); $lastDay = (new DateTime($endDate))->format($format); + $dbForLogs = call_user_func($getLogsDB, $project); + $metrics = [ 'total' => [ METRIC_EXECUTIONS, @@ -95,9 +99,11 @@ App::get('/v1/project/usage') '1d' => 'Y-m-d\T00:00:00.000P', }; - Authorization::skip(function () use ($dbForProject, $firstDay, $lastDay, $period, $metrics, $limit, &$total, &$stats) { + Authorization::skip(function () use ($dbForProject, $dbForLogs, $firstDay, $lastDay, $period, $metrics, $limit, &$total, &$stats) { foreach ($metrics['total'] as $metric) { - $result = $dbForProject->findOne('stats', [ + $db = ($metric === METRIC_FILES_IMAGES_TRANSFORMED) ? $dbForLogs : $dbForProject; + + $result = $db->findOne('stats', [ Query::equal('metric', [$metric]), Query::equal('period', ['inf']) ]); @@ -105,7 +111,9 @@ App::get('/v1/project/usage') } foreach ($metrics['period'] as $metric) { - $results = $dbForProject->find('stats', [ + $db = ($metric === METRIC_FILES_IMAGES_TRANSFORMED) ? $dbForLogs : $dbForProject; + + $results = $db->find('stats', [ Query::equal('metric', [$metric]), Query::equal('period', [$period]), Query::greaterThanEqual('time', $firstDay), From 5becd0a5dc9553fb0e120ad5ee52e0785f55a28f Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Mon, 3 Mar 2025 16:04:31 +0545 Subject: [PATCH 071/143] Revert "Fix: stats resources only queue projects accessed in last 3 hours" --- src/Appwrite/Platform/Tasks/StatsResources.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Appwrite/Platform/Tasks/StatsResources.php b/src/Appwrite/Platform/Tasks/StatsResources.php index c318f92cf1..ac3b9ead73 100644 --- a/src/Appwrite/Platform/Tasks/StatsResources.php +++ b/src/Appwrite/Platform/Tasks/StatsResources.php @@ -62,7 +62,7 @@ class StatsResources extends Action Authorization::disable(); Authorization::setDefaultStatus(false); - $last24Hours = (new \DateTime())->sub(\DateInterval::createFromDateString('3 hours')); + $last24Hours = (new \DateTime())->sub(\DateInterval::createFromDateString('24 hours')); /** * For each project that were accessed in last 24 hours */ From b314ae82084fea574ac63d0a9f18bb2990ef88b1 Mon Sep 17 00:00:00 2001 From: shimon Date: Mon, 3 Mar 2025 15:19:49 +0200 Subject: [PATCH 072/143] disable transformedAt update for console users --- app/controllers/api/storage.php | 20 ++++++++------------ app/controllers/shared/api.php | 14 ++++++++------ 2 files changed, 16 insertions(+), 18 deletions(-) diff --git a/app/controllers/api/storage.php b/app/controllers/api/storage.php index f180c22acf..5bf52f995f 100644 --- a/app/controllers/api/storage.php +++ b/app/controllers/api/storage.php @@ -6,7 +6,6 @@ use Appwrite\Auth\Auth; use Appwrite\ClamAV\Network; use Appwrite\Event\Delete; use Appwrite\Event\Event; -use Appwrite\Event\StatsUsage; use Appwrite\Extend\Exception; use Appwrite\OpenSSL\OpenSSL; use Appwrite\SDK\AuthType; @@ -942,8 +941,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview') ->inject('mode') ->inject('deviceForFiles') ->inject('deviceForLocal') - ->inject('queueForStatsUsage') - ->action(function (string $bucketId, string $fileId, int $width, int $height, string $gravity, int $quality, int $borderWidth, string $borderColor, int $borderRadius, float $opacity, int $rotation, string $background, string $output, Request $request, Response $response, Document $project, Database $dbForProject, string $mode, Device $deviceForFiles, Device $deviceForLocal, StatsUsage $queueForStatsUsage) { + ->action(function (string $bucketId, string $fileId, int $width, int $height, string $gravity, int $quality, int $borderWidth, string $borderColor, int $borderRadius, float $opacity, int $rotation, string $background, string $output, Request $request, Response $response, Document $project, Database $dbForProject, string $mode, Device $deviceForFiles, Device $deviceForLocal) { if (!\extension_loaded('imagick')) { throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Imagick extension is missing'); @@ -1071,15 +1069,13 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview') $contentType = (\array_key_exists($output, $outputs)) ? $outputs[$output] : $outputs['jpg']; - $queueForStatsUsage - ->addMetric(METRIC_FILES_TRANSFORMATIONS, 1) - ->addMetric(str_replace('{bucketInternalId}', $bucket->getInternalId(), METRIC_BUCKET_ID_FILES_TRANSFORMATIONS), 1) - ; - - $transformedAt = $file->getAttribute('transformedAt', ''); - if (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_PROJECT_ACCESS)) > $transformedAt) { - $file->setAttribute('transformedAt', DateTime::now()); - Authorization::skip(fn () => $dbForProject->updateDocument('bucket_' . $file->getAttribute('bucketInternalId'), $file->getId(), $file)); + //Do not update transformedAt if it's a console user + if (!Auth::isPrivilegedUser(Authorization::getRoles())) { + $transformedAt = $file->getAttribute('transformedAt', ''); + if (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_PROJECT_ACCESS)) > $transformedAt) { + $file->setAttribute('transformedAt', DateTime::now()); + Authorization::skip(fn () => $dbForProject->updateDocument('bucket_' . $file->getAttribute('bucketInternalId'), $file->getId(), $file)); + } } $response diff --git a/app/controllers/shared/api.php b/app/controllers/shared/api.php index 7f7b73ab0c..1015400a12 100644 --- a/app/controllers/shared/api.php +++ b/app/controllers/shared/api.php @@ -534,7 +534,7 @@ App::init() if ($type === 'bucket') { $bucketId = $parts[1] ?? null; - $bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); + $bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAppUser && !$isPrivilegedUser)) { throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); @@ -560,11 +560,13 @@ App::init() if ($file->isEmpty()) { throw new Exception(Exception::STORAGE_FILE_NOT_FOUND); } - - $transformedAt = $file->getAttribute('transformedAt', ''); - if (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_PROJECT_ACCESS)) > $transformedAt) { - $file->setAttribute('transformedAt', DateTime::now()); - Authorization::skip(fn () => $dbForProject->updateDocument('bucket_' . $file->getAttribute('bucketInternalId'), $file->getId(), $file)); + //Do not update transformedAt if it's a console user + if (!Auth::isPrivilegedUser(Authorization::getRoles())) { + $transformedAt = $file->getAttribute('transformedAt', ''); + if (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_PROJECT_ACCESS)) > $transformedAt) { + $file->setAttribute('transformedAt', DateTime::now()); + Authorization::skip(fn () => $dbForProject->updateDocument('bucket_' . $file->getAttribute('bucketInternalId'), $file->getId(), $file)); + } } } From c6b8e649fef5108bcbe70bc54270b01d851cd09b Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Mon, 3 Mar 2025 14:34:10 +0000 Subject: [PATCH 073/143] chore: added maxtimeout to testChannelExecutions --- tests/e2e/Services/Realtime/RealtimeCustomClientTest.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/e2e/Services/Realtime/RealtimeCustomClientTest.php b/tests/e2e/Services/Realtime/RealtimeCustomClientTest.php index dda524fc7c..c72dbf85ec 100644 --- a/tests/e2e/Services/Realtime/RealtimeCustomClientTest.php +++ b/tests/e2e/Services/Realtime/RealtimeCustomClientTest.php @@ -1311,6 +1311,8 @@ class RealtimeCustomClientTest extends Scope $this->assertEquals($deployment['headers']['status-code'], 202); $this->assertNotEmpty($deployment['body']['$id']); + $maxTimeSeconds = 10; + $startTime = time(); // Poll until deployment is built while (true) { $deployment = $this->client->call(Client::METHOD_GET, '/functions/' . $function['body']['$id'] . '/deployments/' . $deploymentId, [ @@ -1326,6 +1328,10 @@ class RealtimeCustomClientTest extends Scope break; } + if (time() - $startTime >= $maxTimeSeconds) { + throw new \Exception('Deployment timed out after ' . $maxTimeSeconds . ' seconds'); + } + \sleep(1); } From e865ca74d775393d12af377b4f120fd087a4d32b Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Mon, 3 Mar 2025 14:41:11 +0000 Subject: [PATCH 074/143] chore: added timeout to webhooks base test --- tests/e2e/Services/Webhooks/WebhooksBase.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/e2e/Services/Webhooks/WebhooksBase.php b/tests/e2e/Services/Webhooks/WebhooksBase.php index 2ef41003ee..ffcb1ce980 100644 --- a/tests/e2e/Services/Webhooks/WebhooksBase.php +++ b/tests/e2e/Services/Webhooks/WebhooksBase.php @@ -14,6 +14,8 @@ trait WebhooksBase { protected function awaitDeploymentIsBuilt($functionId, $deploymentId, $checkForSuccess = true): void { + $maxTimeSeconds = 10; + $startTime = time(); while (true) { $deployment = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/deployments/' . $deploymentId, [ 'content-type' => 'application/json', @@ -28,6 +30,10 @@ trait WebhooksBase break; } + if (time() - $startTime >= $maxTimeSeconds) { + throw new \Exception('Deployment timed out after ' . $maxTimeSeconds . ' seconds'); + } + \sleep(1); } From fc05a4f1e30e164c54e1330ef2cdcb77cd3cc36b Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Mon, 3 Mar 2025 16:07:33 +0000 Subject: [PATCH 075/143] chore: updated to use assertEventually --- .../Realtime/RealtimeCustomClientTest.php | 19 ++----------- tests/e2e/Services/Webhooks/WebhooksBase.php | 28 ++++++------------- 2 files changed, 11 insertions(+), 36 deletions(-) diff --git a/tests/e2e/Services/Realtime/RealtimeCustomClientTest.php b/tests/e2e/Services/Realtime/RealtimeCustomClientTest.php index c72dbf85ec..e356397408 100644 --- a/tests/e2e/Services/Realtime/RealtimeCustomClientTest.php +++ b/tests/e2e/Services/Realtime/RealtimeCustomClientTest.php @@ -1311,29 +1311,16 @@ class RealtimeCustomClientTest extends Scope $this->assertEquals($deployment['headers']['status-code'], 202); $this->assertNotEmpty($deployment['body']['$id']); - $maxTimeSeconds = 10; - $startTime = time(); // Poll until deployment is built - while (true) { + $this->assertEventually(function () use ($function, $deploymentId) { $deployment = $this->client->call(Client::METHOD_GET, '/functions/' . $function['body']['$id'] . '/deployments/' . $deploymentId, [ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'], ]); - if ( - $deployment['headers']['status-code'] >= 400 - || \in_array($deployment['body']['status'], ['ready', 'failed']) - ) { - break; - } - - if (time() - $startTime >= $maxTimeSeconds) { - throw new \Exception('Deployment timed out after ' . $maxTimeSeconds . ' seconds'); - } - - \sleep(1); - } + $this->assertEquals('ready', $deployment['body']['status'], \json_encode($deployment['body'])); + }); $response = $this->client->call(Client::METHOD_PATCH, '/functions/' . $functionId . '/deployments/' . $deploymentId, array_merge([ 'content-type' => 'application/json', diff --git a/tests/e2e/Services/Webhooks/WebhooksBase.php b/tests/e2e/Services/Webhooks/WebhooksBase.php index ffcb1ce980..b7985b5be1 100644 --- a/tests/e2e/Services/Webhooks/WebhooksBase.php +++ b/tests/e2e/Services/Webhooks/WebhooksBase.php @@ -2,6 +2,7 @@ namespace Tests\E2E\Services\Webhooks; +use Appwrite\Tests\Async; use Appwrite\Tests\Retry; use CURLFile; use Tests\E2E\Client; @@ -12,35 +13,22 @@ use Utopia\Database\Validator\Datetime as DatetimeValidator; trait WebhooksBase { + use Async; + protected function awaitDeploymentIsBuilt($functionId, $deploymentId, $checkForSuccess = true): void { - $maxTimeSeconds = 10; - $startTime = time(); - while (true) { + $this->assertEventually(function () use ($functionId, $deploymentId, $checkForSuccess) { $deployment = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/deployments/' . $deploymentId, [ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'], ]); - if ( - $deployment['headers']['status-code'] >= 400 - || \in_array($deployment['body']['status'], ['ready', 'failed']) - ) { - break; + if ($checkForSuccess) { + $this->assertEquals(200, $deployment['headers']['status-code']); + $this->assertEquals('ready', $deployment['body']['status'], \json_encode($deployment['body'])); } - - if (time() - $startTime >= $maxTimeSeconds) { - throw new \Exception('Deployment timed out after ' . $maxTimeSeconds . ' seconds'); - } - - \sleep(1); - } - - if ($checkForSuccess) { - $this->assertEquals(200, $deployment['headers']['status-code']); - $this->assertEquals('ready', $deployment['body']['status'], \json_encode($deployment['body'])); - } + }); } public static function getWebhookSignature(array $webhook, string $signatureKey): string From fe7aeefabf9f2f1523df07ebfa3a060099688d70 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Mon, 3 Mar 2025 16:20:32 +0000 Subject: [PATCH 076/143] chore: remove redundant param --- tests/e2e/Services/Webhooks/WebhooksBase.php | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/tests/e2e/Services/Webhooks/WebhooksBase.php b/tests/e2e/Services/Webhooks/WebhooksBase.php index b7985b5be1..c743810feb 100644 --- a/tests/e2e/Services/Webhooks/WebhooksBase.php +++ b/tests/e2e/Services/Webhooks/WebhooksBase.php @@ -15,19 +15,17 @@ trait WebhooksBase { use Async; - protected function awaitDeploymentIsBuilt($functionId, $deploymentId, $checkForSuccess = true): void + protected function awaitDeploymentIsBuilt($functionId, $deploymentId): void { - $this->assertEventually(function () use ($functionId, $deploymentId, $checkForSuccess) { + $this->assertEventually(function () use ($functionId, $deploymentId) { $deployment = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/deployments/' . $deploymentId, [ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'], ]); - if ($checkForSuccess) { - $this->assertEquals(200, $deployment['headers']['status-code']); - $this->assertEquals('ready', $deployment['body']['status'], \json_encode($deployment['body'])); - } + $this->assertEquals(200, $deployment['headers']['status-code']); + $this->assertEquals('ready', $deployment['body']['status'], \json_encode($deployment['body'])); }); } From 9a6de87f7dcbdeff0d6c70f18cb1923ed87c2a6b Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Mon, 3 Mar 2025 16:31:41 +0000 Subject: [PATCH 077/143] chore: usagetest and migrationsbase --- tests/e2e/General/UsageTest.php | 67 ++++++++----------- .../Services/Migrations/MigrationsBase.php | 23 +++---- 2 files changed, 37 insertions(+), 53 deletions(-) diff --git a/tests/e2e/General/UsageTest.php b/tests/e2e/General/UsageTest.php index df780d8f47..27d521aa6a 100644 --- a/tests/e2e/General/UsageTest.php +++ b/tests/e2e/General/UsageTest.php @@ -1142,49 +1142,38 @@ class UsageTest extends Scope $tries = 0; - while (true) { - try { - // Compare new values with old values - $response = $this->client->call( - Client::METHOD_GET, - '/functions/' . $functionId . '/usage?range=30d', - $this->getConsoleHeaders() - ); + $this->assertEventually(function () use ($functionId, $functionsMetrics, $projectMetrics) { + // Compare new values with old values + $response = $this->client->call( + Client::METHOD_GET, + '/functions/' . $functionId . '/usage?range=30d', + $this->getConsoleHeaders() + ); - $this->assertEquals(200, $response['headers']['status-code']); - $this->assertEquals(19, count($response['body'])); - $this->assertEquals('30d', $response['body']['range']); + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals(19, count($response['body'])); + $this->assertEquals('30d', $response['body']['range']); - // Check if the new values are greater than the old values - $this->assertEquals($functionsMetrics['executionsTotal'] + 1, $response['body']['executionsTotal']); - $this->assertGreaterThan($functionsMetrics['executionsTimeTotal'], $response['body']['executionsTimeTotal']); - $this->assertGreaterThan($functionsMetrics['executionsMbSecondsTotal'], $response['body']['executionsMbSecondsTotal']); + // Check if the new values are greater than the old values + $this->assertEquals($functionsMetrics['executionsTotal'] + 1, $response['body']['executionsTotal']); + $this->assertGreaterThan($functionsMetrics['executionsTimeTotal'], $response['body']['executionsTimeTotal']); + $this->assertGreaterThan($functionsMetrics['executionsMbSecondsTotal'], $response['body']['executionsMbSecondsTotal']); - $response = $this->client->call( - Client::METHOD_GET, - '/project/usage', - $this->getConsoleHeaders(), - [ - 'period' => '1h', - 'startDate' => self::getToday(), - 'endDate' => self::getTomorrow(), - ] - ); + $response = $this->client->call( + Client::METHOD_GET, + '/project/usage', + $this->getConsoleHeaders(), + [ + 'period' => '1h', + 'startDate' => self::getToday(), + 'endDate' => self::getTomorrow(), + ] + ); - $this->assertEquals(200, $response['headers']['status-code']); - $this->assertEquals($projectMetrics['executionsTotal'] + 1, $response['body']['executionsTotal']); - $this->assertGreaterThan($projectMetrics['executionsMbSecondsTotal'], $response['body']['executionsMbSecondsTotal']); - - break; - } catch (ExpectationFailedException $th) { - if ($tries >= 5) { - throw $th; - } else { - $tries++; - sleep(5); - } - } - } + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals($projectMetrics['executionsTotal'] + 1, $response['body']['executionsTotal']); + $this->assertGreaterThan($projectMetrics['executionsMbSecondsTotal'], $response['body']['executionsMbSecondsTotal']); + }); } public function tearDown(): void diff --git a/tests/e2e/Services/Migrations/MigrationsBase.php b/tests/e2e/Services/Migrations/MigrationsBase.php index 381706f5ee..6c468ee730 100644 --- a/tests/e2e/Services/Migrations/MigrationsBase.php +++ b/tests/e2e/Services/Migrations/MigrationsBase.php @@ -53,8 +53,9 @@ trait MigrationsBase $this->assertNotEmpty($migration['body']); $this->assertNotEmpty($migration['body']['$id']); - $attempts = 0; - while ($attempts < 5) { + $migrationResult = []; + + $this->assertEventually(function () use ($migration, &$migrationResult) { $response = $this->client->call(Client::METHOD_GET, '/migrations/' . $migration['body']['$id'], [ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getDestinationProject()['$id'], @@ -66,24 +67,18 @@ trait MigrationsBase $this->assertNotEmpty($response['body']['$id']); if ($response['body']['status'] === 'failed') { - $this->fail('Migration failed', json_encode($response['body'], JSON_PRETTY_PRINT)); + $this->fail('Migration failed' . json_encode($response['body'], JSON_PRETTY_PRINT)); } $this->assertNotEquals('failed', $response['body']['status']); + $this->assertEquals('completed', $response['body']['status']); - if ($response['body']['status'] === 'completed') { - return $response['body']; - } + $migrationResult = $response['body']; - if ($attempts === 4) { - $this->assertEquals('completed', $response['body']['status']); - } + return true; + }); - $attempts++; - sleep(5); - } - - return []; + return $migrationResult; } /** From 5d6154444462ca8b49f0c8b1f97ec3e15e1a1e1b Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Tue, 4 Mar 2025 05:31:35 +0000 Subject: [PATCH 078/143] Fix: use receivedAt date when available --- src/Appwrite/Platform/Workers/StatsUsageDump.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Appwrite/Platform/Workers/StatsUsageDump.php b/src/Appwrite/Platform/Workers/StatsUsageDump.php index 5d7240f4b5..292fdffc00 100644 --- a/src/Appwrite/Platform/Workers/StatsUsageDump.php +++ b/src/Appwrite/Platform/Workers/StatsUsageDump.php @@ -142,7 +142,11 @@ class StatsUsageDump extends Action } foreach ($this->periods as $period => $format) { - $time = 'inf' === $period ? null : date($format, time()); + $time = null; + + if ($period !== 'inf') { + $time = $receivedAt !== 'NONE' ? (new \DateTime($receivedAt))->format($format) : date($format, time()); + } $id = \md5("{$time}_{$period}_{$key}"); $document = new Document([ From dd5dd4cc43c26404a8e73a7e78e7063bac3464c2 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Tue, 4 Mar 2025 05:34:02 +0000 Subject: [PATCH 079/143] Same for database metrics --- src/Appwrite/Platform/Workers/StatsUsageDump.php | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/Appwrite/Platform/Workers/StatsUsageDump.php b/src/Appwrite/Platform/Workers/StatsUsageDump.php index 292fdffc00..294269d586 100644 --- a/src/Appwrite/Platform/Workers/StatsUsageDump.php +++ b/src/Appwrite/Platform/Workers/StatsUsageDump.php @@ -134,7 +134,7 @@ class StatsUsageDump extends Action if (str_contains($key, METRIC_DATABASES_STORAGE)) { try { - $this->handleDatabaseStorage($key, $dbForProject, $project); + $this->handleDatabaseStorage($key, $dbForProject, $project, $receivedAt); } catch (\Exception $e) { console::error('[' . DateTime::now() . '] failed to calculate database storage for key [' . $key . '] ' . $e->getMessage()); } @@ -175,12 +175,12 @@ class StatsUsageDump extends Action } } - private function handleDatabaseStorage(string $key, Database $dbForProject, Document $project): void + private function handleDatabaseStorage(string $key, Database $dbForProject, Document $project, string $receivedAt): void { $data = explode('.', $key); $start = microtime(true); - $updateMetric = function (Database $dbForProject, Document $project, int $value, string $key, string $period, string|null $time) { + $updateMetric = function (Database $dbForProject, Document $project, int $value, string $key, string $period, string|null $time) use ($receivedAt) { $id = \md5("{$time}_{$period}_{$key}"); $document = new Document([ @@ -201,7 +201,11 @@ class StatsUsageDump extends Action }; foreach ($this->periods as $period => $format) { - $time = 'inf' === $period ? null : date($format, time()); + $time = null; + + if ($period !== 'inf') { + $time = $receivedAt !== 'NONE' ? (new \DateTime($receivedAt))->format($format) : date($format, time()); + } $id = \md5("{$time}_{$period}_{$key}"); $value = 0; From 4d5d30ba7bbab63508467788ecf5fd7a04df222e Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Tue, 4 Mar 2025 05:50:21 +0000 Subject: [PATCH 080/143] Fix default value --- src/Appwrite/Platform/Workers/StatsUsageDump.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Appwrite/Platform/Workers/StatsUsageDump.php b/src/Appwrite/Platform/Workers/StatsUsageDump.php index 294269d586..e03bb86b58 100644 --- a/src/Appwrite/Platform/Workers/StatsUsageDump.php +++ b/src/Appwrite/Platform/Workers/StatsUsageDump.php @@ -117,7 +117,7 @@ class StatsUsageDump extends Action $project = new Document($stats['project'] ?? []); $numberOfKeys = !empty($stats['keys']) ? count($stats['keys']) : 0; - $receivedAt = $stats['receivedAt'] ?? 'NONE'; + $receivedAt = $stats['receivedAt'] ?? null; if ($numberOfKeys === 0) { continue; } @@ -145,7 +145,7 @@ class StatsUsageDump extends Action $time = null; if ($period !== 'inf') { - $time = $receivedAt !== 'NONE' ? (new \DateTime($receivedAt))->format($format) : date($format, time()); + $time = !empty($receivedAt) ? (new \DateTime($receivedAt))->format($format) : date($format, time()); } $id = \md5("{$time}_{$period}_{$key}"); @@ -204,7 +204,7 @@ class StatsUsageDump extends Action $time = null; if ($period !== 'inf') { - $time = $receivedAt !== 'NONE' ? (new \DateTime($receivedAt))->format($format) : date($format, time()); + $time = !empty($receivedAt) ? (new \DateTime($receivedAt))->format($format) : date($format, time()); } $id = \md5("{$time}_{$period}_{$key}"); From 0659dce9357584dcf3e90283b739303a2fa6fdee Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Tue, 4 Mar 2025 06:22:58 +0000 Subject: [PATCH 081/143] Fix: disable dual writing --- src/Appwrite/Platform/Workers/StatsUsageDump.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Appwrite/Platform/Workers/StatsUsageDump.php b/src/Appwrite/Platform/Workers/StatsUsageDump.php index e03bb86b58..81acd4e4b0 100644 --- a/src/Appwrite/Platform/Workers/StatsUsageDump.php +++ b/src/Appwrite/Platform/Workers/StatsUsageDump.php @@ -328,7 +328,7 @@ class StatsUsageDump extends Action protected function writeToLogsDB(Document $project, Document $document): void { - if (!System::getEnv('_APP_STATS_USAGE_DUAL_WRITING', false)) { + if (System::getEnv('_APP_STATS_USAGE_DUAL_WRITING', 'disabled') === 'disabled') { Console::log('Dual Writing is disabled. Skipping...'); return; } From 8b4bc7e4a6b1a39af6b171bc3362bf7b5e1cafca Mon Sep 17 00:00:00 2001 From: shimon Date: Tue, 4 Mar 2025 18:19:23 +0200 Subject: [PATCH 082/143] updating audits workers var scope --- src/Appwrite/Platform/Workers/Audits.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Appwrite/Platform/Workers/Audits.php b/src/Appwrite/Platform/Workers/Audits.php index 977aff3cfb..2d48750e9d 100644 --- a/src/Appwrite/Platform/Workers/Audits.php +++ b/src/Appwrite/Platform/Workers/Audits.php @@ -17,15 +17,15 @@ use Utopia\System\System; class Audits extends Action { - private const BATCH_SIZE_DEVELOPMENT = 1; // smaller batch size for development - private const BATCH_SIZE_PRODUCTION = 5_000; - private const BATCH_AGGREGATION_INTERVAL = 60; // in seconds + protected const BATCH_SIZE_DEVELOPMENT = 1; // smaller batch size for development + protected const BATCH_SIZE_PRODUCTION = 5_000; + protected const BATCH_AGGREGATION_INTERVAL = 60; // in seconds - private int $lastTriggeredTime = 0; + protected int $lastTriggeredTime = 0; - private array $logs = []; + protected array $logs = []; - private function getBatchSize(): int + protected function getBatchSize(): int { return System::getEnv('_APP_ENV', 'development') === 'development' ? self::BATCH_SIZE_DEVELOPMENT From f761829d6053580defc8eada81cb72061c42b07f Mon Sep 17 00:00:00 2001 From: shimon Date: Tue, 4 Mar 2025 18:49:31 +0200 Subject: [PATCH 083/143] updating audits workers var scope --- src/Appwrite/Platform/Workers/Audits.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Appwrite/Platform/Workers/Audits.php b/src/Appwrite/Platform/Workers/Audits.php index 2d48750e9d..2a43a9e24f 100644 --- a/src/Appwrite/Platform/Workers/Audits.php +++ b/src/Appwrite/Platform/Workers/Audits.php @@ -17,7 +17,7 @@ use Utopia\System\System; class Audits extends Action { - protected const BATCH_SIZE_DEVELOPMENT = 1; // smaller batch size for development + protected const BATCH_SIZE_DEVELOPMENT = 2; // smaller batch size for development protected const BATCH_SIZE_PRODUCTION = 5_000; protected const BATCH_AGGREGATION_INTERVAL = 60; // in seconds From 76dc8c29d5e5bf8c2f8149e4a186331753082acc Mon Sep 17 00:00:00 2001 From: shimon Date: Tue, 4 Mar 2025 19:07:45 +0200 Subject: [PATCH 084/143] updating audits workers var scope --- src/Appwrite/Platform/Workers/Audits.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Appwrite/Platform/Workers/Audits.php b/src/Appwrite/Platform/Workers/Audits.php index 2a43a9e24f..f22df17d31 100644 --- a/src/Appwrite/Platform/Workers/Audits.php +++ b/src/Appwrite/Platform/Workers/Audits.php @@ -21,7 +21,7 @@ class Audits extends Action protected const BATCH_SIZE_PRODUCTION = 5_000; protected const BATCH_AGGREGATION_INTERVAL = 60; // in seconds - protected int $lastTriggeredTime = 0; + private int $lastTriggeredTime = 0; protected array $logs = []; From ce054e7088db0966c3cbf0d7527b284b273af3c6 Mon Sep 17 00:00:00 2001 From: shimon Date: Tue, 4 Mar 2025 19:12:20 +0200 Subject: [PATCH 085/143] updating audits workers var scope --- src/Appwrite/Platform/Workers/Audits.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Appwrite/Platform/Workers/Audits.php b/src/Appwrite/Platform/Workers/Audits.php index f22df17d31..85fe18ce9e 100644 --- a/src/Appwrite/Platform/Workers/Audits.php +++ b/src/Appwrite/Platform/Workers/Audits.php @@ -23,7 +23,7 @@ class Audits extends Action private int $lastTriggeredTime = 0; - protected array $logs = []; + private array $logs = []; protected function getBatchSize(): int { From 09aa383cf338fe53053725c1ade509a249ed9f32 Mon Sep 17 00:00:00 2001 From: shimon Date: Tue, 4 Mar 2025 19:15:00 +0200 Subject: [PATCH 086/143] updating audits workers var scope --- src/Appwrite/Platform/Workers/Audits.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Appwrite/Platform/Workers/Audits.php b/src/Appwrite/Platform/Workers/Audits.php index 85fe18ce9e..3221010a07 100644 --- a/src/Appwrite/Platform/Workers/Audits.php +++ b/src/Appwrite/Platform/Workers/Audits.php @@ -25,6 +25,7 @@ class Audits extends Action private array $logs = []; + protected function getBatchSize(): int { return System::getEnv('_APP_ENV', 'development') === 'development' From 1838b9c4e879f2eca2fd775dcd32df9821e35db5 Mon Sep 17 00:00:00 2001 From: shimon Date: Tue, 4 Mar 2025 19:49:10 +0200 Subject: [PATCH 087/143] updating audits workers var scope --- src/Appwrite/Platform/Workers/Audits.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Appwrite/Platform/Workers/Audits.php b/src/Appwrite/Platform/Workers/Audits.php index 3221010a07..c663ffc6d4 100644 --- a/src/Appwrite/Platform/Workers/Audits.php +++ b/src/Appwrite/Platform/Workers/Audits.php @@ -17,9 +17,9 @@ use Utopia\System\System; class Audits extends Action { - protected const BATCH_SIZE_DEVELOPMENT = 2; // smaller batch size for development + protected const BATCH_SIZE_DEVELOPMENT = 26; // smaller batch size for development protected const BATCH_SIZE_PRODUCTION = 5_000; - protected const BATCH_AGGREGATION_INTERVAL = 60; // in seconds + protected const BATCH_AGGREGATION_INTERVAL = 300; // in seconds private int $lastTriggeredTime = 0; From c443c8fdab744b6c532196145ee046ac98612b1c Mon Sep 17 00:00:00 2001 From: shimon Date: Tue, 4 Mar 2025 19:49:37 +0200 Subject: [PATCH 088/143] updating audits workers var scope --- src/Appwrite/Platform/Workers/Audits.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Appwrite/Platform/Workers/Audits.php b/src/Appwrite/Platform/Workers/Audits.php index c663ffc6d4..3367bc1268 100644 --- a/src/Appwrite/Platform/Workers/Audits.php +++ b/src/Appwrite/Platform/Workers/Audits.php @@ -17,9 +17,9 @@ use Utopia\System\System; class Audits extends Action { - protected const BATCH_SIZE_DEVELOPMENT = 26; // smaller batch size for development + protected const BATCH_SIZE_DEVELOPMENT = 6; // smaller batch size for development protected const BATCH_SIZE_PRODUCTION = 5_000; - protected const BATCH_AGGREGATION_INTERVAL = 300; // in seconds + protected const BATCH_AGGREGATION_INTERVAL = 120; // in seconds private int $lastTriggeredTime = 0; From f2ad3a501ed4a538f655592df4aa0b31e3105399 Mon Sep 17 00:00:00 2001 From: shimon Date: Tue, 4 Mar 2025 20:34:56 +0200 Subject: [PATCH 089/143] updating audits workers var scope --- src/Appwrite/Platform/Workers/Audits.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Appwrite/Platform/Workers/Audits.php b/src/Appwrite/Platform/Workers/Audits.php index 3367bc1268..269b29c4c8 100644 --- a/src/Appwrite/Platform/Workers/Audits.php +++ b/src/Appwrite/Platform/Workers/Audits.php @@ -17,9 +17,9 @@ use Utopia\System\System; class Audits extends Action { - protected const BATCH_SIZE_DEVELOPMENT = 6; // smaller batch size for development + protected const BATCH_SIZE_DEVELOPMENT = 1; // smaller batch size for development protected const BATCH_SIZE_PRODUCTION = 5_000; - protected const BATCH_AGGREGATION_INTERVAL = 120; // in seconds + protected const BATCH_AGGREGATION_INTERVAL = 60; // in seconds private int $lastTriggeredTime = 0; From 1d2883f47250b45837d87905c8ffcf0d4b480f5f Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Wed, 5 Mar 2025 04:46:06 +0000 Subject: [PATCH 090/143] chore: fix model for image transformations in usage project --- src/Appwrite/Utopia/Response/Model/UsageProject.php | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/Appwrite/Utopia/Response/Model/UsageProject.php b/src/Appwrite/Utopia/Response/Model/UsageProject.php index 6ac8830ac5..6737cbf6ef 100644 --- a/src/Appwrite/Utopia/Response/Model/UsageProject.php +++ b/src/Appwrite/Utopia/Response/Model/UsageProject.php @@ -197,18 +197,19 @@ class UsageProject extends Model 'example' => [], 'array' => true ]) + ->addRule('imageTransformations', [ + 'type' => Response::MODEL_METRIC, + 'description' => 'An array of aggregated number of image transformations.', + 'default' => [], + 'example' => [], + 'array' => true + ]) ->addRule('imageTransformationsTotal', [ 'type' => self::TYPE_INTEGER, 'description' => 'An array of aggregated number of image transformations.', 'default' => 0, 'example' => 0, ]) - ->addRule('imageTransformations', [ - 'type' => self::TYPE_INTEGER, - 'description' => 'Total aggregated number of image transformations.', - 'default' => 0, - 'example' => 0, - ]) ; } From 2c6f731a3820333a18fbcafbd43f478349f0a0bd Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Wed, 5 Mar 2025 04:49:34 +0000 Subject: [PATCH 091/143] fix: description --- src/Appwrite/Utopia/Response/Model/UsageProject.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Appwrite/Utopia/Response/Model/UsageProject.php b/src/Appwrite/Utopia/Response/Model/UsageProject.php index 6737cbf6ef..395b19b7cd 100644 --- a/src/Appwrite/Utopia/Response/Model/UsageProject.php +++ b/src/Appwrite/Utopia/Response/Model/UsageProject.php @@ -206,7 +206,7 @@ class UsageProject extends Model ]) ->addRule('imageTransformationsTotal', [ 'type' => self::TYPE_INTEGER, - 'description' => 'An array of aggregated number of image transformations.', + 'description' => 'Total aggregated number of image transformations.', 'default' => 0, 'example' => 0, ]) From 6024d01e7ba7413ce462f09064b9a2abfd502a88 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Wed, 5 Mar 2025 05:25:50 +0000 Subject: [PATCH 092/143] Feat: calculate database storage in stats-resources - Calculate database storage in stats-resources worker --- .../Platform/Workers/StatsResources.php | 30 +++++++++++++------ 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/src/Appwrite/Platform/Workers/StatsResources.php b/src/Appwrite/Platform/Workers/StatsResources.php index 62046bd186..969d43e895 100644 --- a/src/Appwrite/Platform/Workers/StatsResources.php +++ b/src/Appwrite/Platform/Workers/StatsResources.php @@ -182,7 +182,7 @@ class StatsResources extends Action } try { - $this->countForDatabase($dbForProject, $dbForLogs, $region); + $this->countForDatabase($dbForProject, $region); } catch (Throwable $th) { call_user_func_array($this->logError, [$th, "StatsResources", "count_for_database_{$project->getId()}"]); } @@ -242,42 +242,54 @@ class StatsResources extends Action $this->createStatsDocuments($region, METRIC_FILES_IMAGES_TRANSFORMED, $totalImageTransformations); } - protected function countForDatabase(Database $dbForProject, Database $dbForLogs, string $region) + protected function countForDatabase(Database $dbForProject, string $region) { $totalCollections = 0; $totalDocuments = 0; - $this->foreachDocument($dbForProject, 'databases', [], function ($database) use ($dbForProject, $dbForLogs, $region, &$totalCollections, &$totalDocuments) { + $totalDatabaseStorage = 0; + + $this->foreachDocument($dbForProject, 'databases', [], function ($database) use ($dbForProject, $region, &$totalCollections, &$totalDocuments, &$totalDatabaseStorage) { $collections = $dbForProject->count('database_' . $database->getInternalId()); $metric = str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_COLLECTIONS); $this->createStatsDocuments($region, $metric, $collections); - $documents = $this->countForCollections($dbForProject, $dbForLogs, $database, $region); + [$documents, $storage] = $this->countForCollections($dbForProject, $database, $region); + $totalDatabaseStorage += $storage; $totalDocuments += $documents; $totalCollections += $collections; }); $this->createStatsDocuments($region, METRIC_COLLECTIONS, $totalCollections); $this->createStatsDocuments($region, METRIC_DOCUMENTS, $totalDocuments); + $this->createStatsDocuments($region, METRIC_DATABASES_STORAGE, $totalDatabaseStorage); } - protected function countForCollections(Database $dbForProject, Database $dbForLogs, Document $database, string $region): int + protected function countForCollections(Database $dbForProject, Document $database, string $region): array { $databaseDocuments = 0; - $this->foreachDocument($dbForProject, 'database_' . $database->getInternalId(), [], function ($collection) use ($dbForProject, $dbForLogs, $database, $region, &$totalCollections, &$databaseDocuments) { + $databaseStorage = 0; + $this->foreachDocument($dbForProject, 'database_' . $database->getInternalId(), [], function ($collection) use ($dbForProject, $database, $region, &$databaseStorage, &$databaseDocuments) { $documents = $dbForProject->count('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId()); - $metric = str_replace(['{databaseInternalId}', '{collectionInternalId}'], [$database->getInternalId(), $collection->getInternalId()], METRIC_DATABASE_ID_COLLECTION_ID_DOCUMENTS); $this->createStatsDocuments($region, $metric, $documents); - $databaseDocuments += $documents; + + $collectionStorage = $dbForProject->getSizeOfCollection('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId()); + $metric = str_replace(['{databaseInternalId}', '{collectionInternalId}'], [$database->getInternalId(), $collection->getInternalId()], METRIC_DATABASE_ID_COLLECTION_ID_STORAGE); + $this->createStatsDocuments($region, $metric, $collectionStorage); + $databaseStorage += $collectionStorage; + }); $metric = str_replace(['{databaseInternalId}'], [$database->getInternalId()], METRIC_DATABASE_ID_DOCUMENTS); $this->createStatsDocuments($region, $metric, $databaseDocuments); - return $databaseDocuments; + $metric = str_replace(['{databaseInternalId}'], [$database->getInternalId()], METRIC_DATABASE_ID_STORAGE); + $this->createStatsDocuments($region, $metric, $databaseStorage); + + return [$databaseDocuments, $databaseStorage]; } protected function countForFunctions(Database $dbForProject, Database $dbForLogs, string $region) From 2afb8a14ac7f541eda336cd85c71e76e31e4b83d Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Wed, 5 Mar 2025 05:27:56 +0000 Subject: [PATCH 093/143] Skip in dual writing --- src/Appwrite/Platform/Workers/StatsUsageDump.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Appwrite/Platform/Workers/StatsUsageDump.php b/src/Appwrite/Platform/Workers/StatsUsageDump.php index 81acd4e4b0..119a9e7288 100644 --- a/src/Appwrite/Platform/Workers/StatsUsageDump.php +++ b/src/Appwrite/Platform/Workers/StatsUsageDump.php @@ -48,6 +48,7 @@ class StatsUsageDump extends Action METRIC_BUILDS => true, METRIC_COLLECTIONS => true, METRIC_DOCUMENTS => true, + METRIC_DATABASES_STORAGE => true, ]; /** @@ -63,6 +64,7 @@ class StatsUsageDump extends Action '.deployments.storage', '.builds', '.builds.storage', + '.databases.storage' ]; /** From 0ce4555f70312b0d9f1de93d91f96f2ed70d3192 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Wed, 5 Mar 2025 17:42:43 +0000 Subject: [PATCH 094/143] chore: added auth group to create phone token --- app/controllers/api/account.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index a634618e6e..20f64496ac 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -2400,7 +2400,7 @@ App::put('/v1/account/sessions/phone') App::post('/v1/account/tokens/phone') ->alias('/v1/account/sessions/phone') ->desc('Create phone token') - ->groups(['api', 'account']) + ->groups(['api', 'account', 'auth']) ->label('scope', 'sessions.write') ->label('auth.type', 'phone') ->label('audits.event', 'session.create') From 36b047529843dc23f3b94d2b23cb2fb6c2f0b36b Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Wed, 5 Mar 2025 18:40:41 +0000 Subject: [PATCH 095/143] chore: update tests --- .../Account/AccountCustomClientTest.php | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/tests/e2e/Services/Account/AccountCustomClientTest.php b/tests/e2e/Services/Account/AccountCustomClientTest.php index 439fa24fb6..daa5bcbff8 100644 --- a/tests/e2e/Services/Account/AccountCustomClientTest.php +++ b/tests/e2e/Services/Account/AccountCustomClientTest.php @@ -2419,6 +2419,33 @@ class AccountCustomClientTest extends Scope $message = $smsRequest['data']['message']; $token = substr($message, 0, 6); + /** + * Test for FAILURE + */ + + // disable phone sessions + $response = $this->client->call(Client::METHOD_PATCH, '/projects/' . $this->getProject()['$id'] . '/auth/phone', array_merge([ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => 'console', + 'cookie' => 'a_session_console=' . $this->getRoot()['session'], + ]), [ + 'status' => false, + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals(false, $response['body']['authPhone']); + + $response = $this->client->call(Client::METHOD_POST, '/account/verification/phone', array_merge([ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session, + ])); + + $this->assertEquals(501, $response['headers']['status-code']); + $this->assertEquals("Phone authentication is disabled for this project", $response['body']['message']); + return \array_merge($data, [ 'token' => \substr($smsRequest['data']['message'], 0, 6) ]); From a0605b14cb46b541cc62d9ba4920189cdf9102fd Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Fri, 7 Mar 2025 04:14:45 +0000 Subject: [PATCH 096/143] chore: regen specs and update vers --- app/config/platforms.php | 6 +- .../specs/open-api3-latest-console.json | 22 ++- app/config/specs/open-api3-latest-server.json | 4 - app/config/specs/swagger2-latest-console.json | 23 ++- app/config/specs/swagger2-latest-server.json | 4 - composer.lock | 137 ++++++++---------- .../examples/account/update-mfa-challenge.md | 2 +- .../users/delete-mfa-authenticator.md | 2 +- 8 files changed, 89 insertions(+), 111 deletions(-) diff --git a/app/config/platforms.php b/app/config/platforms.php index 4b6a0d2da6..4fd4decc5b 100644 --- a/app/config/platforms.php +++ b/app/config/platforms.php @@ -59,7 +59,7 @@ return [ [ 'key' => 'flutter', 'name' => 'Flutter', - 'version' => '14.0.1', + 'version' => '15.0.0', 'url' => 'https://github.com/appwrite/sdk-for-flutter', 'package' => 'https://pub.dev/packages/appwrite', 'enabled' => true, @@ -217,7 +217,7 @@ return [ [ 'key' => 'cli', 'name' => 'Command Line', - 'version' => '6.2.1', + 'version' => '6.2.2', 'url' => 'https://github.com/appwrite/sdk-for-cli', 'package' => 'https://www.npmjs.com/package/appwrite-cli', 'enabled' => true, @@ -371,7 +371,7 @@ return [ [ 'key' => 'dart', 'name' => 'Dart', - 'version' => '13.0.1', + 'version' => '14.0.0', 'url' => 'https://github.com/appwrite/sdk-for-dart', 'package' => 'https://pub.dev/packages/dart_appwrite', 'enabled' => true, diff --git a/app/config/specs/open-api3-latest-console.json b/app/config/specs/open-api3-latest-console.json index bc216226fb..54161c4262 100644 --- a/app/config/specs/open-api3-latest-console.json +++ b/app/config/specs/open-api3-latest-console.json @@ -6409,8 +6409,6 @@ }, "required": [ "required", - "min", - "max", "default" ] } @@ -6646,8 +6644,6 @@ }, "required": [ "required", - "min", - "max", "default" ] } @@ -36630,13 +36626,15 @@ }, "x-example": [] }, - "imageTransformationsTotal": { - "type": "integer", - "description": "An array of aggregated number of image transformations.", - "x-example": 0, - "format": "int32" - }, "imageTransformations": { + "type": "array", + "description": "An array of aggregated number of image transformations.", + "items": { + "$ref": "#\/components\/schemas\/metric" + }, + "x-example": [] + }, + "imageTransformationsTotal": { "type": "integer", "description": "Total aggregated number of image transformations.", "x-example": 0, @@ -36673,8 +36671,8 @@ "authPhoneCountryBreakdown", "databasesReads", "databasesWrites", - "imageTransformationsTotal", - "imageTransformations" + "imageTransformations", + "imageTransformationsTotal" ] }, "headers": { diff --git a/app/config/specs/open-api3-latest-server.json b/app/config/specs/open-api3-latest-server.json index 280c080514..3d32d3e978 100644 --- a/app/config/specs/open-api3-latest-server.json +++ b/app/config/specs/open-api3-latest-server.json @@ -5953,8 +5953,6 @@ }, "required": [ "required", - "min", - "max", "default" ] } @@ -6192,8 +6190,6 @@ }, "required": [ "required", - "min", - "max", "default" ] } diff --git a/app/config/specs/swagger2-latest-console.json b/app/config/specs/swagger2-latest-console.json index 2dddc72802..8fc7e7daf3 100644 --- a/app/config/specs/swagger2-latest-console.json +++ b/app/config/specs/swagger2-latest-console.json @@ -6609,8 +6609,6 @@ }, "required": [ "required", - "min", - "max", "default" ] } @@ -6847,8 +6845,6 @@ }, "required": [ "required", - "min", - "max", "default" ] } @@ -37217,13 +37213,16 @@ }, "x-example": [] }, - "imageTransformationsTotal": { - "type": "integer", - "description": "An array of aggregated number of image transformations.", - "x-example": 0, - "format": "int32" - }, "imageTransformations": { + "type": "array", + "description": "An array of aggregated number of image transformations.", + "items": { + "type": "object", + "$ref": "#\/definitions\/metric" + }, + "x-example": [] + }, + "imageTransformationsTotal": { "type": "integer", "description": "Total aggregated number of image transformations.", "x-example": 0, @@ -37260,8 +37259,8 @@ "authPhoneCountryBreakdown", "databasesReads", "databasesWrites", - "imageTransformationsTotal", - "imageTransformations" + "imageTransformations", + "imageTransformationsTotal" ] }, "headers": { diff --git a/app/config/specs/swagger2-latest-server.json b/app/config/specs/swagger2-latest-server.json index 749f87553b..83757c94f4 100644 --- a/app/config/specs/swagger2-latest-server.json +++ b/app/config/specs/swagger2-latest-server.json @@ -6135,8 +6135,6 @@ }, "required": [ "required", - "min", - "max", "default" ] } @@ -6375,8 +6373,6 @@ }, "required": [ "required", - "min", - "max", "default" ] } diff --git a/composer.lock b/composer.lock index e1d1ac16e3..8930e956a3 100644 --- a/composer.lock +++ b/composer.lock @@ -279,16 +279,16 @@ }, { "name": "brick/math", - "version": "0.12.2", + "version": "0.12.3", "source": { "type": "git", "url": "https://github.com/brick/math.git", - "reference": "901eddb1e45a8e0f689302e40af871c181ecbe40" + "reference": "866551da34e9a618e64a819ee1e01c20d8a588ba" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/brick/math/zipball/901eddb1e45a8e0f689302e40af871c181ecbe40", - "reference": "901eddb1e45a8e0f689302e40af871c181ecbe40", + "url": "https://api.github.com/repos/brick/math/zipball/866551da34e9a618e64a819ee1e01c20d8a588ba", + "reference": "866551da34e9a618e64a819ee1e01c20d8a588ba", "shasum": "" }, "require": { @@ -327,7 +327,7 @@ ], "support": { "issues": "https://github.com/brick/math/issues", - "source": "https://github.com/brick/math/tree/0.12.2" + "source": "https://github.com/brick/math/tree/0.12.3" }, "funding": [ { @@ -335,7 +335,7 @@ "type": "github" } ], - "time": "2025-02-26T10:21:45+00:00" + "time": "2025-02-28T13:11:00+00:00" }, { "name": "chillerlan/php-qrcode", @@ -709,16 +709,16 @@ }, { "name": "google/protobuf", - "version": "v4.29.3", + "version": "v4.30.0", "source": { "type": "git", "url": "https://github.com/protocolbuffers/protobuf-php.git", - "reference": "ab5077c2cfdd1f415f42d11fdbdf903ba8e3d9b7" + "reference": "e1d66682f6836aa87820400f0aa07d9eb566feb6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/protocolbuffers/protobuf-php/zipball/ab5077c2cfdd1f415f42d11fdbdf903ba8e3d9b7", - "reference": "ab5077c2cfdd1f415f42d11fdbdf903ba8e3d9b7", + "url": "https://api.github.com/repos/protocolbuffers/protobuf-php/zipball/e1d66682f6836aa87820400f0aa07d9eb566feb6", + "reference": "e1d66682f6836aa87820400f0aa07d9eb566feb6", "shasum": "" }, "require": { @@ -747,9 +747,9 @@ "proto" ], "support": { - "source": "https://github.com/protocolbuffers/protobuf-php/tree/v4.29.3" + "source": "https://github.com/protocolbuffers/protobuf-php/tree/v4.30.0" }, - "time": "2025-01-08T21:00:13+00:00" + "time": "2025-03-04T22:54:49+00:00" }, { "name": "jean85/pretty-package-versions", @@ -1237,16 +1237,16 @@ }, { "name": "open-telemetry/api", - "version": "1.2.2", + "version": "1.2.3", "source": { "type": "git", "url": "https://github.com/opentelemetry-php/api.git", - "reference": "8b925df3047628968bc5be722468db1b98b82d51" + "reference": "199d7ddda88f5f5619fa73463f1a5a7149ccd1f1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/opentelemetry-php/api/zipball/8b925df3047628968bc5be722468db1b98b82d51", - "reference": "8b925df3047628968bc5be722468db1b98b82d51", + "url": "https://api.github.com/repos/opentelemetry-php/api/zipball/199d7ddda88f5f5619fa73463f1a5a7149ccd1f1", + "reference": "199d7ddda88f5f5619fa73463f1a5a7149ccd1f1", "shasum": "" }, "require": { @@ -1303,7 +1303,7 @@ "issues": "https://github.com/open-telemetry/opentelemetry-php/issues", "source": "https://github.com/open-telemetry/opentelemetry-php" }, - "time": "2025-02-03T21:49:11+00:00" + "time": "2025-03-05T21:42:54+00:00" }, { "name": "open-telemetry/context", @@ -2371,16 +2371,16 @@ }, { "name": "ramsey/collection", - "version": "2.0.0", + "version": "2.1.0", "source": { "type": "git", "url": "https://github.com/ramsey/collection.git", - "reference": "a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5" + "reference": "3c5990b8a5e0b79cd1cf11c2dc1229e58e93f109" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ramsey/collection/zipball/a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5", - "reference": "a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5", + "url": "https://api.github.com/repos/ramsey/collection/zipball/3c5990b8a5e0b79cd1cf11c2dc1229e58e93f109", + "reference": "3c5990b8a5e0b79cd1cf11c2dc1229e58e93f109", "shasum": "" }, "require": { @@ -2388,25 +2388,22 @@ }, "require-dev": { "captainhook/plugin-composer": "^5.3", - "ergebnis/composer-normalize": "^2.28.3", - "fakerphp/faker": "^1.21", + "ergebnis/composer-normalize": "^2.45", + "fakerphp/faker": "^1.24", "hamcrest/hamcrest-php": "^2.0", - "jangregor/phpstan-prophecy": "^1.0", - "mockery/mockery": "^1.5", + "jangregor/phpstan-prophecy": "^2.1", + "mockery/mockery": "^1.6", "php-parallel-lint/php-console-highlighter": "^1.0", - "php-parallel-lint/php-parallel-lint": "^1.3", - "phpcsstandards/phpcsutils": "^1.0.0-rc1", - "phpspec/prophecy-phpunit": "^2.0", - "phpstan/extension-installer": "^1.2", - "phpstan/phpstan": "^1.9", - "phpstan/phpstan-mockery": "^1.1", - "phpstan/phpstan-phpunit": "^1.3", - "phpunit/phpunit": "^9.5", - "psalm/plugin-mockery": "^1.1", - "psalm/plugin-phpunit": "^0.18.4", - "ramsey/coding-standard": "^2.0.3", - "ramsey/conventional-commits": "^1.3", - "vimeo/psalm": "^5.4" + "php-parallel-lint/php-parallel-lint": "^1.4", + "phpspec/prophecy-phpunit": "^2.3", + "phpstan/extension-installer": "^1.4", + "phpstan/phpstan": "^2.1", + "phpstan/phpstan-mockery": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpunit/phpunit": "^10.5", + "ramsey/coding-standard": "^2.3", + "ramsey/conventional-commits": "^1.6", + "roave/security-advisories": "dev-latest" }, "type": "library", "extra": { @@ -2444,19 +2441,9 @@ ], "support": { "issues": "https://github.com/ramsey/collection/issues", - "source": "https://github.com/ramsey/collection/tree/2.0.0" + "source": "https://github.com/ramsey/collection/tree/2.1.0" }, - "funding": [ - { - "url": "https://github.com/ramsey", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/ramsey/collection", - "type": "tidelift" - } - ], - "time": "2022-12-31T21:50:55+00:00" + "time": "2025-03-02T04:48:29+00:00" }, { "name": "ramsey/uuid", @@ -3880,16 +3867,16 @@ }, { "name": "utopia-php/fetch", - "version": "0.3.0", + "version": "0.3.1", "source": { "type": "git", "url": "https://github.com/utopia-php/fetch.git", - "reference": "02b12c05aec13399dcc2da8d51f908e328ab63f4" + "reference": "524dd50afa8c64670c4fb18f1df4db9b5bb4b3d0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/fetch/zipball/02b12c05aec13399dcc2da8d51f908e328ab63f4", - "reference": "02b12c05aec13399dcc2da8d51f908e328ab63f4", + "url": "https://api.github.com/repos/utopia-php/fetch/zipball/524dd50afa8c64670c4fb18f1df4db9b5bb4b3d0", + "reference": "524dd50afa8c64670c4fb18f1df4db9b5bb4b3d0", "shasum": "" }, "require": { @@ -3913,22 +3900,22 @@ "description": "A simple library that provides an interface for making HTTP Requests.", "support": { "issues": "https://github.com/utopia-php/fetch/issues", - "source": "https://github.com/utopia-php/fetch/tree/0.3.0" + "source": "https://github.com/utopia-php/fetch/tree/0.3.1" }, - "time": "2025-01-17T06:11:10+00:00" + "time": "2025-03-05T18:08:55+00:00" }, { "name": "utopia-php/framework", - "version": "0.33.17", + "version": "0.33.19", "source": { "type": "git", "url": "https://github.com/utopia-php/http.git", - "reference": "73fac6fbce9f56282dba4e52a58cf836ec434644" + "reference": "64c7b7bb8a8595ffe875fa8d4b7705684dbf46c0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/http/zipball/73fac6fbce9f56282dba4e52a58cf836ec434644", - "reference": "73fac6fbce9f56282dba4e52a58cf836ec434644", + "url": "https://api.github.com/repos/utopia-php/http/zipball/64c7b7bb8a8595ffe875fa8d4b7705684dbf46c0", + "reference": "64c7b7bb8a8595ffe875fa8d4b7705684dbf46c0", "shasum": "" }, "require": { @@ -3960,9 +3947,9 @@ ], "support": { "issues": "https://github.com/utopia-php/http/issues", - "source": "https://github.com/utopia-php/http/tree/0.33.17" + "source": "https://github.com/utopia-php/http/tree/0.33.19" }, - "time": "2025-02-24T17:35:48+00:00" + "time": "2025-03-06T11:37:49+00:00" }, { "name": "utopia-php/image", @@ -4608,22 +4595,24 @@ }, { "name": "utopia-php/storage", - "version": "0.18.9", + "version": "0.18.10", "source": { "type": "git", "url": "https://github.com/utopia-php/storage.git", - "reference": "1cf455404e8700b3093fd73d74a38d41cdced90c" + "reference": "76f31158f4251abb207f7a9b16f7cb0bfdb3b39e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/storage/zipball/1cf455404e8700b3093fd73d74a38d41cdced90c", - "reference": "1cf455404e8700b3093fd73d74a38d41cdced90c", + "url": "https://api.github.com/repos/utopia-php/storage/zipball/76f31158f4251abb207f7a9b16f7cb0bfdb3b39e", + "reference": "76f31158f4251abb207f7a9b16f7cb0bfdb3b39e", "shasum": "" }, "require": { "ext-brotli": "*", + "ext-curl": "*", "ext-fileinfo": "*", "ext-lz4": "*", + "ext-simplexml": "*", "ext-snappy": "*", "ext-xz": "*", "ext-zlib": "*", @@ -4657,9 +4646,9 @@ ], "support": { "issues": "https://github.com/utopia-php/storage/issues", - "source": "https://github.com/utopia-php/storage/tree/0.18.9" + "source": "https://github.com/utopia-php/storage/tree/0.18.10" }, - "time": "2025-02-11T13:10:40+00:00" + "time": "2025-03-03T10:47:54+00:00" }, { "name": "utopia-php/swoole", @@ -5052,16 +5041,16 @@ "packages-dev": [ { "name": "appwrite/sdk-generator", - "version": "0.40.1", + "version": "0.40.2", "source": { "type": "git", "url": "https://github.com/appwrite/sdk-generator.git", - "reference": "df180676b6fbde7832ae1495af3e2f3e8f700837" + "reference": "56f09482d9e2f223911277ab887f197402708049" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/df180676b6fbde7832ae1495af3e2f3e8f700837", - "reference": "df180676b6fbde7832ae1495af3e2f3e8f700837", + "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/56f09482d9e2f223911277ab887f197402708049", + "reference": "56f09482d9e2f223911277ab887f197402708049", "shasum": "" }, "require": { @@ -5097,9 +5086,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/0.40.1" + "source": "https://github.com/appwrite/sdk-generator/tree/0.40.2" }, - "time": "2025-02-26T07:07:10+00:00" + "time": "2025-03-06T16:31:03+00:00" }, { "name": "doctrine/annotations", diff --git a/docs/examples/1.6.x/server-swift/examples/account/update-mfa-challenge.md b/docs/examples/1.6.x/server-swift/examples/account/update-mfa-challenge.md index eed3bfade7..fee76bf0dd 100644 --- a/docs/examples/1.6.x/server-swift/examples/account/update-mfa-challenge.md +++ b/docs/examples/1.6.x/server-swift/examples/account/update-mfa-challenge.md @@ -7,7 +7,7 @@ let client = Client() let account = Account(client) -let result = try await account.updateMfaChallenge( +let session = try await account.updateMfaChallenge( challengeId: "", otp: "" ) diff --git a/docs/examples/1.6.x/server-swift/examples/users/delete-mfa-authenticator.md b/docs/examples/1.6.x/server-swift/examples/users/delete-mfa-authenticator.md index 902d0c904c..5f1d6a0eeb 100644 --- a/docs/examples/1.6.x/server-swift/examples/users/delete-mfa-authenticator.md +++ b/docs/examples/1.6.x/server-swift/examples/users/delete-mfa-authenticator.md @@ -8,7 +8,7 @@ let client = Client() let users = Users(client) -let user = try await users.deleteMfaAuthenticator( +let result = try await users.deleteMfaAuthenticator( userId: "", type: .totp ) From b791e596721071413eff91ef0a9ec438c2ebdb68 Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Fri, 7 Mar 2025 10:59:16 +0000 Subject: [PATCH 097/143] chore: bump composer --- composer.lock | 159 +++++++++++++++++++++++--------------------------- 1 file changed, 74 insertions(+), 85 deletions(-) diff --git a/composer.lock b/composer.lock index 76f61e44d7..e89d6b53b5 100644 --- a/composer.lock +++ b/composer.lock @@ -279,16 +279,16 @@ }, { "name": "brick/math", - "version": "0.12.2", + "version": "0.12.3", "source": { "type": "git", "url": "https://github.com/brick/math.git", - "reference": "901eddb1e45a8e0f689302e40af871c181ecbe40" + "reference": "866551da34e9a618e64a819ee1e01c20d8a588ba" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/brick/math/zipball/901eddb1e45a8e0f689302e40af871c181ecbe40", - "reference": "901eddb1e45a8e0f689302e40af871c181ecbe40", + "url": "https://api.github.com/repos/brick/math/zipball/866551da34e9a618e64a819ee1e01c20d8a588ba", + "reference": "866551da34e9a618e64a819ee1e01c20d8a588ba", "shasum": "" }, "require": { @@ -327,7 +327,7 @@ ], "support": { "issues": "https://github.com/brick/math/issues", - "source": "https://github.com/brick/math/tree/0.12.2" + "source": "https://github.com/brick/math/tree/0.12.3" }, "funding": [ { @@ -335,7 +335,7 @@ "type": "github" } ], - "time": "2025-02-26T10:21:45+00:00" + "time": "2025-02-28T13:11:00+00:00" }, { "name": "chillerlan/php-qrcode", @@ -709,16 +709,16 @@ }, { "name": "google/protobuf", - "version": "v4.29.3", + "version": "v4.30.0", "source": { "type": "git", "url": "https://github.com/protocolbuffers/protobuf-php.git", - "reference": "ab5077c2cfdd1f415f42d11fdbdf903ba8e3d9b7" + "reference": "e1d66682f6836aa87820400f0aa07d9eb566feb6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/protocolbuffers/protobuf-php/zipball/ab5077c2cfdd1f415f42d11fdbdf903ba8e3d9b7", - "reference": "ab5077c2cfdd1f415f42d11fdbdf903ba8e3d9b7", + "url": "https://api.github.com/repos/protocolbuffers/protobuf-php/zipball/e1d66682f6836aa87820400f0aa07d9eb566feb6", + "reference": "e1d66682f6836aa87820400f0aa07d9eb566feb6", "shasum": "" }, "require": { @@ -747,9 +747,9 @@ "proto" ], "support": { - "source": "https://github.com/protocolbuffers/protobuf-php/tree/v4.29.3" + "source": "https://github.com/protocolbuffers/protobuf-php/tree/v4.30.0" }, - "time": "2025-01-08T21:00:13+00:00" + "time": "2025-03-04T22:54:49+00:00" }, { "name": "jean85/pretty-package-versions", @@ -1237,16 +1237,16 @@ }, { "name": "open-telemetry/api", - "version": "1.2.2", + "version": "1.2.3", "source": { "type": "git", "url": "https://github.com/opentelemetry-php/api.git", - "reference": "8b925df3047628968bc5be722468db1b98b82d51" + "reference": "199d7ddda88f5f5619fa73463f1a5a7149ccd1f1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/opentelemetry-php/api/zipball/8b925df3047628968bc5be722468db1b98b82d51", - "reference": "8b925df3047628968bc5be722468db1b98b82d51", + "url": "https://api.github.com/repos/opentelemetry-php/api/zipball/199d7ddda88f5f5619fa73463f1a5a7149ccd1f1", + "reference": "199d7ddda88f5f5619fa73463f1a5a7149ccd1f1", "shasum": "" }, "require": { @@ -1303,7 +1303,7 @@ "issues": "https://github.com/open-telemetry/opentelemetry-php/issues", "source": "https://github.com/open-telemetry/opentelemetry-php" }, - "time": "2025-02-03T21:49:11+00:00" + "time": "2025-03-05T21:42:54+00:00" }, { "name": "open-telemetry/context", @@ -1366,16 +1366,16 @@ }, { "name": "open-telemetry/exporter-otlp", - "version": "1.2.0", + "version": "1.2.1", "source": { "type": "git", "url": "https://github.com/opentelemetry-php/exporter-otlp.git", - "reference": "243d9657c44a06f740cf384f486afe954c2b725f" + "reference": "b7580440b7481a98da97aceabeb46e1b276c8747" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/opentelemetry-php/exporter-otlp/zipball/243d9657c44a06f740cf384f486afe954c2b725f", - "reference": "243d9657c44a06f740cf384f486afe954c2b725f", + "url": "https://api.github.com/repos/opentelemetry-php/exporter-otlp/zipball/b7580440b7481a98da97aceabeb46e1b276c8747", + "reference": "b7580440b7481a98da97aceabeb46e1b276c8747", "shasum": "" }, "require": { @@ -1426,7 +1426,7 @@ "issues": "https://github.com/open-telemetry/opentelemetry-php/issues", "source": "https://github.com/open-telemetry/opentelemetry-php" }, - "time": "2025-01-08T23:50:03+00:00" + "time": "2025-03-06T23:21:56+00:00" }, { "name": "open-telemetry/gen-otlp-protobuf", @@ -2371,16 +2371,16 @@ }, { "name": "ramsey/collection", - "version": "2.0.0", + "version": "2.1.0", "source": { "type": "git", "url": "https://github.com/ramsey/collection.git", - "reference": "a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5" + "reference": "3c5990b8a5e0b79cd1cf11c2dc1229e58e93f109" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ramsey/collection/zipball/a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5", - "reference": "a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5", + "url": "https://api.github.com/repos/ramsey/collection/zipball/3c5990b8a5e0b79cd1cf11c2dc1229e58e93f109", + "reference": "3c5990b8a5e0b79cd1cf11c2dc1229e58e93f109", "shasum": "" }, "require": { @@ -2388,25 +2388,22 @@ }, "require-dev": { "captainhook/plugin-composer": "^5.3", - "ergebnis/composer-normalize": "^2.28.3", - "fakerphp/faker": "^1.21", + "ergebnis/composer-normalize": "^2.45", + "fakerphp/faker": "^1.24", "hamcrest/hamcrest-php": "^2.0", - "jangregor/phpstan-prophecy": "^1.0", - "mockery/mockery": "^1.5", + "jangregor/phpstan-prophecy": "^2.1", + "mockery/mockery": "^1.6", "php-parallel-lint/php-console-highlighter": "^1.0", - "php-parallel-lint/php-parallel-lint": "^1.3", - "phpcsstandards/phpcsutils": "^1.0.0-rc1", - "phpspec/prophecy-phpunit": "^2.0", - "phpstan/extension-installer": "^1.2", - "phpstan/phpstan": "^1.9", - "phpstan/phpstan-mockery": "^1.1", - "phpstan/phpstan-phpunit": "^1.3", - "phpunit/phpunit": "^9.5", - "psalm/plugin-mockery": "^1.1", - "psalm/plugin-phpunit": "^0.18.4", - "ramsey/coding-standard": "^2.0.3", - "ramsey/conventional-commits": "^1.3", - "vimeo/psalm": "^5.4" + "php-parallel-lint/php-parallel-lint": "^1.4", + "phpspec/prophecy-phpunit": "^2.3", + "phpstan/extension-installer": "^1.4", + "phpstan/phpstan": "^2.1", + "phpstan/phpstan-mockery": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpunit/phpunit": "^10.5", + "ramsey/coding-standard": "^2.3", + "ramsey/conventional-commits": "^1.6", + "roave/security-advisories": "dev-latest" }, "type": "library", "extra": { @@ -2444,19 +2441,9 @@ ], "support": { "issues": "https://github.com/ramsey/collection/issues", - "source": "https://github.com/ramsey/collection/tree/2.0.0" + "source": "https://github.com/ramsey/collection/tree/2.1.0" }, - "funding": [ - { - "url": "https://github.com/ramsey", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/ramsey/collection", - "type": "tidelift" - } - ], - "time": "2022-12-31T21:50:55+00:00" + "time": "2025-03-02T04:48:29+00:00" }, { "name": "ramsey/uuid", @@ -3718,16 +3705,16 @@ }, { "name": "utopia-php/database", - "version": "0.60.4", + "version": "0.60.6", "source": { "type": "git", "url": "https://github.com/utopia-php/database.git", - "reference": "3a034f8eb275cd92d088f1e492a7cd00fd021427" + "reference": "f3c9aa964b39c6205069f038a26e709a15541406" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/database/zipball/3a034f8eb275cd92d088f1e492a7cd00fd021427", - "reference": "3a034f8eb275cd92d088f1e492a7cd00fd021427", + "url": "https://api.github.com/repos/utopia-php/database/zipball/f3c9aa964b39c6205069f038a26e709a15541406", + "reference": "f3c9aa964b39c6205069f038a26e709a15541406", "shasum": "" }, "require": { @@ -3768,9 +3755,9 @@ ], "support": { "issues": "https://github.com/utopia-php/database/issues", - "source": "https://github.com/utopia-php/database/tree/0.60.4" + "source": "https://github.com/utopia-php/database/tree/0.60.6" }, - "time": "2025-02-25T23:09:18+00:00" + "time": "2025-03-05T01:23:14+00:00" }, { "name": "utopia-php/domains", @@ -3881,16 +3868,16 @@ }, { "name": "utopia-php/fetch", - "version": "0.3.0", + "version": "0.3.1", "source": { "type": "git", "url": "https://github.com/utopia-php/fetch.git", - "reference": "02b12c05aec13399dcc2da8d51f908e328ab63f4" + "reference": "524dd50afa8c64670c4fb18f1df4db9b5bb4b3d0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/fetch/zipball/02b12c05aec13399dcc2da8d51f908e328ab63f4", - "reference": "02b12c05aec13399dcc2da8d51f908e328ab63f4", + "url": "https://api.github.com/repos/utopia-php/fetch/zipball/524dd50afa8c64670c4fb18f1df4db9b5bb4b3d0", + "reference": "524dd50afa8c64670c4fb18f1df4db9b5bb4b3d0", "shasum": "" }, "require": { @@ -3914,22 +3901,22 @@ "description": "A simple library that provides an interface for making HTTP Requests.", "support": { "issues": "https://github.com/utopia-php/fetch/issues", - "source": "https://github.com/utopia-php/fetch/tree/0.3.0" + "source": "https://github.com/utopia-php/fetch/tree/0.3.1" }, - "time": "2025-01-17T06:11:10+00:00" + "time": "2025-03-05T18:08:55+00:00" }, { "name": "utopia-php/framework", - "version": "0.33.17", + "version": "0.33.19", "source": { "type": "git", "url": "https://github.com/utopia-php/http.git", - "reference": "73fac6fbce9f56282dba4e52a58cf836ec434644" + "reference": "64c7b7bb8a8595ffe875fa8d4b7705684dbf46c0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/http/zipball/73fac6fbce9f56282dba4e52a58cf836ec434644", - "reference": "73fac6fbce9f56282dba4e52a58cf836ec434644", + "url": "https://api.github.com/repos/utopia-php/http/zipball/64c7b7bb8a8595ffe875fa8d4b7705684dbf46c0", + "reference": "64c7b7bb8a8595ffe875fa8d4b7705684dbf46c0", "shasum": "" }, "require": { @@ -3961,9 +3948,9 @@ ], "support": { "issues": "https://github.com/utopia-php/http/issues", - "source": "https://github.com/utopia-php/http/tree/0.33.17" + "source": "https://github.com/utopia-php/http/tree/0.33.19" }, - "time": "2025-02-24T17:35:48+00:00" + "time": "2025-03-06T11:37:49+00:00" }, { "name": "utopia-php/image", @@ -4609,22 +4596,24 @@ }, { "name": "utopia-php/storage", - "version": "0.18.9", + "version": "0.18.10", "source": { "type": "git", "url": "https://github.com/utopia-php/storage.git", - "reference": "1cf455404e8700b3093fd73d74a38d41cdced90c" + "reference": "76f31158f4251abb207f7a9b16f7cb0bfdb3b39e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/storage/zipball/1cf455404e8700b3093fd73d74a38d41cdced90c", - "reference": "1cf455404e8700b3093fd73d74a38d41cdced90c", + "url": "https://api.github.com/repos/utopia-php/storage/zipball/76f31158f4251abb207f7a9b16f7cb0bfdb3b39e", + "reference": "76f31158f4251abb207f7a9b16f7cb0bfdb3b39e", "shasum": "" }, "require": { "ext-brotli": "*", + "ext-curl": "*", "ext-fileinfo": "*", "ext-lz4": "*", + "ext-simplexml": "*", "ext-snappy": "*", "ext-xz": "*", "ext-zlib": "*", @@ -4658,9 +4647,9 @@ ], "support": { "issues": "https://github.com/utopia-php/storage/issues", - "source": "https://github.com/utopia-php/storage/tree/0.18.9" + "source": "https://github.com/utopia-php/storage/tree/0.18.10" }, - "time": "2025-02-11T13:10:40+00:00" + "time": "2025-03-03T10:47:54+00:00" }, { "name": "utopia-php/swoole", @@ -5054,16 +5043,16 @@ "packages-dev": [ { "name": "appwrite/sdk-generator", - "version": "0.40.1", + "version": "0.40.2", "source": { "type": "git", "url": "https://github.com/appwrite/sdk-generator.git", - "reference": "df180676b6fbde7832ae1495af3e2f3e8f700837" + "reference": "56f09482d9e2f223911277ab887f197402708049" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/df180676b6fbde7832ae1495af3e2f3e8f700837", - "reference": "df180676b6fbde7832ae1495af3e2f3e8f700837", + "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/56f09482d9e2f223911277ab887f197402708049", + "reference": "56f09482d9e2f223911277ab887f197402708049", "shasum": "" }, "require": { @@ -5099,9 +5088,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/0.40.1" + "source": "https://github.com/appwrite/sdk-generator/tree/0.40.2" }, - "time": "2025-02-26T07:07:10+00:00" + "time": "2025-03-06T16:31:03+00:00" }, { "name": "doctrine/annotations", From d9fc6b70ad3bbd501e56458befc7b0f2d4fbaaa6 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Fri, 7 Mar 2025 13:18:20 +0000 Subject: [PATCH 098/143] chore: bump react native --- app/config/platforms.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/config/platforms.php b/app/config/platforms.php index 4fd4decc5b..477d2a47b2 100644 --- a/app/config/platforms.php +++ b/app/config/platforms.php @@ -134,7 +134,7 @@ return [ [ 'key' => 'react-native', 'name' => 'React Native', - 'version' => '0.6.0', + 'version' => '0.7.1', 'url' => 'https://github.com/appwrite/sdk-for-react-native', 'package' => 'https://npmjs.com/package/react-native-appwrite', 'enabled' => true, From 493f536a6e228a45eb7f82989d767385e727bebb Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Fri, 7 Mar 2025 14:04:52 +0000 Subject: [PATCH 099/143] chore: added logsdb for deletes worker --- src/Appwrite/Platform/Tasks/Maintenance.php | 1 - src/Appwrite/Platform/Workers/Deletes.php | 30 +++++++++++++-------- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/src/Appwrite/Platform/Tasks/Maintenance.php b/src/Appwrite/Platform/Tasks/Maintenance.php index 2d37bdbf70..53aa4f1273 100644 --- a/src/Appwrite/Platform/Tasks/Maintenance.php +++ b/src/Appwrite/Platform/Tasks/Maintenance.php @@ -52,7 +52,6 @@ class Maintenance extends Action ->setProject($project) ->setUsageRetentionHourlyDateTime(DateTime::addSeconds(new \DateTime(), -1 * $usageStatsRetentionHourly)) ->trigger(); - }); $this->notifyDeleteConnections($queueForDeletes); diff --git a/src/Appwrite/Platform/Workers/Deletes.php b/src/Appwrite/Platform/Workers/Deletes.php index e11181d199..643fc4d010 100644 --- a/src/Appwrite/Platform/Workers/Deletes.php +++ b/src/Appwrite/Platform/Workers/Deletes.php @@ -47,7 +47,7 @@ class Deletes extends Action ->inject('project') ->inject('dbForPlatform') ->inject('getProjectDB') - ->inject('timelimit') + ->inject('getLogsDB') ->inject('deviceForFiles') ->inject('deviceForFunctions') ->inject('deviceForBuilds') @@ -57,8 +57,8 @@ class Deletes extends Action ->inject('auditRetention') ->inject('log') ->callback( - fn ($message, Document $project, Database $dbForPlatform, callable $getProjectDB, callable $timelimit, Device $deviceForFiles, Device $deviceForFunctions, Device $deviceForBuilds, Device $deviceForCache, CertificatesAdapter $certificates, string $executionRetention, string $auditRetention, Log $log) => - $this->action($message, $project, $dbForPlatform, $getProjectDB, $timelimit, $deviceForFiles, $deviceForFunctions, $deviceForBuilds, $deviceForCache, $certificates, $executionRetention, $auditRetention, $log) + fn ($message, Document $project, Database $dbForPlatform, callable $getProjectDB, callable $getLogsDB, Device $deviceForFiles, Device $deviceForFunctions, Device $deviceForBuilds, Device $deviceForCache, CertificatesAdapter $certificates, string $executionRetention, string $auditRetention, Log $log) => + $this->action($message, $project, $dbForPlatform, $getProjectDB, $getLogsDB, $deviceForFiles, $deviceForFunctions, $deviceForBuilds, $deviceForCache, $certificates, $executionRetention, $auditRetention, $log) ); } @@ -66,7 +66,7 @@ class Deletes extends Action * @throws Exception * @throws Throwable */ - public function action(Message $message, Document $project, Database $dbForPlatform, callable $getProjectDB, callable $timelimit, Device $deviceForFiles, Device $deviceForFunctions, Device $deviceForBuilds, Device $deviceForCache, CertificatesAdapter $certificates, string $executionRetention, string $auditRetention, Log $log): void + public function action(Message $message, Document $project, Database $dbForPlatform, callable $getProjectDB, callable $getLogsDB, Device $deviceForFiles, Device $deviceForFunctions, Device $deviceForBuilds, Device $deviceForCache, CertificatesAdapter $certificates, string $executionRetention, string $auditRetention, Log $log): void { $payload = $message->getPayload() ?? []; @@ -131,7 +131,7 @@ class Deletes extends Action $this->deleteExpiredSessions($project, $getProjectDB); break; case DELETE_TYPE_USAGE: - $this->deleteUsageStats($project, $getProjectDB, $hourlyUsageRetentionDatetime); + $this->deleteUsageStats($project, $getProjectDB, $getLogsDB, $hourlyUsageRetentionDatetime); break; case DELETE_TYPE_CACHE_BY_RESOURCE: $this->deleteCacheByResource($project, $getProjectDB, $resource, $resourceType); @@ -158,7 +158,7 @@ class Deletes extends Action $this->deleteExpiredTargets($project, $getProjectDB); $this->deleteExecutionLogs($project, $getProjectDB, $executionRetention); $this->deleteAuditLogs($project, $getProjectDB, $auditRetention); - $this->deleteUsageStats($project, $getProjectDB, $hourlyUsageRetentionDatetime); + $this->deleteUsageStats($project, $getProjectDB, $getLogsDB, $hourlyUsageRetentionDatetime); $this->deleteExpiredSessions($project, $getProjectDB); break; default: @@ -412,14 +412,22 @@ class Deletes extends Action * @return void * @throws Exception */ - private function deleteUsageStats(Document $project, callable $getProjectDB, string $hourlyUsageRetentionDatetime): void + private function deleteUsageStats(Document $project, callable $getProjectDB, callable $getLogsDB, string $hourlyUsageRetentionDatetime): void { $dbForProject = $getProjectDB($project); - // Delete Usage stats + $dbForLogs = $getLogsDB($project); + + // Delete Usage stats from projectDB $this->deleteByGroup('stats', [ Query::lessThan('time', $hourlyUsageRetentionDatetime), Query::equal('period', ['1h']), ], $dbForProject); + + // Delete Usage stats from logsDB + $this->deleteByGroup('stats', [ + Query::lessThan('time', $hourlyUsageRetentionDatetime), + Query::equal('period', ['1h']), + ], $dbForLogs); } /** @@ -851,7 +859,7 @@ class Deletes extends Action } else { Console::error('Failed to delete deployment files: ' . $deploymentPath); } - } catch (\Throwable $th) { + } catch (Throwable $th) { Console::error('Failed to delete deployment files: ' . $deploymentPath); Console::error('[Error] Type: ' . get_class($th)); Console::error('[Error] Message: ' . $th->getMessage()); @@ -881,7 +889,7 @@ class Deletes extends Action } else { Console::error('Failed to delete build files: ' . $buildPath); } - } catch (\Throwable $th) { + } catch (Throwable $th) { Console::error('Failed to delete deployment files: ' . $buildPath); Console::error('[Error] Type: ' . get_class($th)); Console::error('[Error] Message: ' . $th->getMessage()); @@ -947,7 +955,7 @@ class Deletes extends Action try { $documents = $database->deleteDocuments($collection, $queries); - } catch (\Throwable $th) { + } catch (Throwable $th) { Console::error('Failed to delete documents for collection ' . $collection . ': ' . $th->getMessage()); return; } From f6b5deda6da2e93c68693547994b381d5fcce8d6 Mon Sep 17 00:00:00 2001 From: Steven Nguyen Date: Fri, 7 Mar 2025 12:35:18 -0800 Subject: [PATCH 100/143] fix: add missing _APP_EMAIL_CERTIFICATES env var to deletes worker The deletes worker uses the certificates resource to delete certificates, but the certificates resource requires the _APP_EMAIL_CERTIFICATES env var to be set or it will throw an exception and not process any delete jobs. This commit adds the missing env var to the deletes worker. Also add _APP_SYSTEM_SECURITY_EMAIL_ADDRESS as a fallback. --- app/views/install/compose.phtml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/views/install/compose.phtml b/app/views/install/compose.phtml index 62fcd03624..e50c70de5b 100644 --- a/app/views/install/compose.phtml +++ b/app/views/install/compose.phtml @@ -342,6 +342,8 @@ $image = $this->getParam('image', ''); - _APP_MAINTENANCE_RETENTION_ABUSE - _APP_MAINTENANCE_RETENTION_AUDIT - _APP_MAINTENANCE_RETENTION_EXECUTION + - _APP_SYSTEM_SECURITY_EMAIL_ADDRESS + - _APP_EMAIL_CERTIFICATES appwrite-worker-databases: image: /: From 0fbd2c642518cbdf63a11b320fc8ec90016df80e Mon Sep 17 00:00:00 2001 From: Steven Nguyen Date: Fri, 7 Mar 2025 12:54:28 -0800 Subject: [PATCH 101/143] fix: prevent warning if no logging config is set If no logging config is set, there's a warning saying: > Using deprecated logging configuration. However, if they didn't set any config, it's not deprecated. --- app/init.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/init.php b/app/init.php index 1311a1429f..1d7f734723 100644 --- a/app/init.php +++ b/app/init.php @@ -817,6 +817,10 @@ $register->set('logger', function () { $providerName = System::getEnv('_APP_LOGGING_PROVIDER', ''); $providerConfig = System::getEnv('_APP_LOGGING_CONFIG', ''); + if (empty($providerConfig)) { + return; + } + try { $loggingProvider = new DSN($providerConfig ?? ''); From 59a8172e96d7956b376d993950eec06228d85100 Mon Sep 17 00:00:00 2001 From: Steven Nguyen Date: Fri, 7 Mar 2025 12:59:11 -0800 Subject: [PATCH 102/143] fix: prevent "Failed to initialize logging provider" errors If the _APP_EXPERIMENT_LOGGING_CONFIG env var isn't set, every 4XX error outputs this warning: Failed to initialize logging provider: Unable to parse DSN: scheme is required To reduce this unnecessary clutter when _APP_EXPERIMENT_LOGGING_CONFIG isn't set, this PR adds a check to only attempt to initialize the logging provider if the env var is set. --- app/controllers/general.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/app/controllers/general.php b/app/controllers/general.php index 65979d3475..d93766a5e7 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -859,11 +859,9 @@ App::error() $publish = $error->getCode() === 0 || $error->getCode() >= 500; } - if ($error->getCode() >= 400 && $error->getCode() < 500) { + $providerConfig = System::getEnv('_APP_EXPERIMENT_LOGGING_CONFIG', ''); + if (!empty($providerConfig) && $error->getCode() >= 400 && $error->getCode() < 500) { // Register error logger - $providerName = System::getEnv('_APP_EXPERIMENT_LOGGING_PROVIDER', ''); - $providerConfig = System::getEnv('_APP_EXPERIMENT_LOGGING_CONFIG', ''); - try { $loggingProvider = new DSN($providerConfig ?? ''); $providerName = $loggingProvider->getScheme(); From 6a6d99a9d0f87dabac610e7027b341ad0c9ac719 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Mon, 10 Mar 2025 08:54:20 +0000 Subject: [PATCH 103/143] chore: queue console project for maintenance delete --- app/cli.php | 45 +++++++++++++++++++++ src/Appwrite/Platform/Tasks/Maintenance.php | 14 +++++-- 2 files changed, 55 insertions(+), 4 deletions(-) diff --git a/app/cli.php b/app/cli.php index 0b2cb884e6..dbbdfdee4d 100644 --- a/app/cli.php +++ b/app/cli.php @@ -2,11 +2,13 @@ require_once __DIR__ . '/init.php'; +use Appwrite\Auth\Auth; use Appwrite\Event\Certificate; use Appwrite\Event\Delete; use Appwrite\Event\Func; use Appwrite\Event\StatsResources; use Appwrite\Event\StatsUsage; +use Appwrite\Network\Validator\Origin; use Appwrite\Platform\Appwrite; use Appwrite\Runtimes\Runtimes; use Utopia\Cache\Adapter\Sharding; @@ -16,6 +18,7 @@ use Utopia\CLI\Console; use Utopia\Config\Config; use Utopia\Database\Database; use Utopia\Database\Document; +use Utopia\Database\Helpers\ID; use Utopia\Database\Validator\Authorization; use Utopia\DSN\DSN; use Utopia\Logger\Log; @@ -99,6 +102,48 @@ CLI::setResource('dbForPlatform', function ($pools, $cache) { return $dbForPlatform; }, ['pools', 'cache']); +CLI::setResource('console', function () { + return new Document([ + '$id' => ID::custom('console'), + '$internalId' => ID::custom('console'), + 'name' => 'Appwrite', + '$collection' => ID::custom('projects'), + 'description' => 'Appwrite core engine', + 'logo' => '', + 'teamId' => null, + 'webhooks' => [], + 'keys' => [], + 'platforms' => [ + [ + '$collection' => ID::custom('platforms'), + 'name' => 'Localhost', + 'type' => Origin::CLIENT_TYPE_WEB, + 'hostname' => 'localhost', + ], // Current host is added on app init + ], + 'legalName' => '', + 'legalCountry' => '', + 'legalState' => '', + 'legalCity' => '', + 'legalAddress' => '', + 'legalTaxId' => '', + 'auths' => [ + 'mockNumbers' => [], + 'invites' => System::getEnv('_APP_CONSOLE_INVITES', 'enabled') === 'enabled', + 'limit' => (System::getEnv('_APP_CONSOLE_WHITELIST_ROOT', 'enabled') === 'enabled') ? 1 : 0, // limit signup to 1 user + 'duration' => Auth::TOKEN_EXPIRATION_LOGIN_LONG, // 1 Year in seconds + 'sessionAlerts' => System::getEnv('_APP_CONSOLE_SESSION_ALERTS', 'disabled') === 'enabled' + ], + 'authWhitelistEmails' => (!empty(System::getEnv('_APP_CONSOLE_WHITELIST_EMAILS', null))) ? \explode(',', System::getEnv('_APP_CONSOLE_WHITELIST_EMAILS', null)) : [], + 'authWhitelistIPs' => (!empty(System::getEnv('_APP_CONSOLE_WHITELIST_IPS', null))) ? \explode(',', System::getEnv('_APP_CONSOLE_WHITELIST_IPS', null)) : [], + 'oAuthProviders' => [ + 'githubEnabled' => true, + 'githubSecret' => System::getEnv('_APP_CONSOLE_GITHUB_SECRET', ''), + 'githubAppid' => System::getEnv('_APP_CONSOLE_GITHUB_APP_ID', '') + ], + ]); +}, []); + CLI::setResource('getProjectDB', function (Group $pools, Database $dbForPlatform, $cache) { $databases = []; // TODO: @Meldiron This should probably be responsibility of utopia-php/pools diff --git a/src/Appwrite/Platform/Tasks/Maintenance.php b/src/Appwrite/Platform/Tasks/Maintenance.php index 2d37bdbf70..98a3f4d295 100644 --- a/src/Appwrite/Platform/Tasks/Maintenance.php +++ b/src/Appwrite/Platform/Tasks/Maintenance.php @@ -24,12 +24,13 @@ class Maintenance extends Action $this ->desc('Schedules maintenance tasks and publishes them to our queues') ->inject('dbForPlatform') + ->inject('console') ->inject('queueForCertificates') ->inject('queueForDeletes') - ->callback(fn (Database $dbForPlatform, Certificate $queueForCertificates, Delete $queueForDeletes) => $this->action($dbForPlatform, $queueForCertificates, $queueForDeletes)); + ->callback(fn (Database $dbForPlatform, Document $console, Certificate $queueForCertificates, Delete $queueForDeletes) => $this->action($dbForPlatform, $console, $queueForCertificates, $queueForDeletes)); } - public function action(Database $dbForPlatform, Certificate $queueForCertificates, Delete $queueForDeletes): void + public function action(Database $dbForPlatform, Document $console, Certificate $queueForCertificates, Delete $queueForDeletes): void { Console::title('Maintenance V1'); Console::success(APP_NAME . ' maintenance process v1 has started'); @@ -41,7 +42,7 @@ class Maintenance extends Action $cacheRetention = (int) System::getEnv('_APP_MAINTENANCE_RETENTION_CACHE', '2592000'); // 30 days $schedulesDeletionRetention = (int) System::getEnv('_APP_MAINTENANCE_RETENTION_SCHEDULES', '86400'); // 1 Day - Console::loop(function () use ($interval, $cacheRetention, $schedulesDeletionRetention, $usageStatsRetentionHourly, $dbForPlatform, $queueForDeletes, $queueForCertificates) { + Console::loop(function () use ($interval, $cacheRetention, $schedulesDeletionRetention, $usageStatsRetentionHourly, $dbForPlatform, $console, $queueForDeletes, $queueForCertificates) { $time = DateTime::now(); Console::info("[{$time}] Notifying workers with maintenance tasks every {$interval} seconds"); @@ -52,9 +53,14 @@ class Maintenance extends Action ->setProject($project) ->setUsageRetentionHourlyDateTime(DateTime::addSeconds(new \DateTime(), -1 * $usageStatsRetentionHourly)) ->trigger(); - }); + $queueForDeletes + ->setType(DELETE_TYPE_MAINTENANCE) + ->setProject($console) + ->setUsageRetentionHourlyDateTime(DateTime::addSeconds(new \DateTime(), -1 * $usageStatsRetentionHourly)) + ->trigger(); + $this->notifyDeleteConnections($queueForDeletes); $this->renewCertificates($dbForPlatform, $queueForCertificates); $this->notifyDeleteCache($cacheRetention, $queueForDeletes); From 61377ceb6e518d664f050337eafbde64e507e749 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Mon, 10 Mar 2025 10:48:44 +0000 Subject: [PATCH 104/143] chore: shift initialization of console project to config --- app/cli.php | 43 +--------------------------------- app/config/console.php | 52 ++++++++++++++++++++++++++++++++++++++++++ app/init.php | 41 ++------------------------------- 3 files changed, 55 insertions(+), 81 deletions(-) create mode 100644 app/config/console.php diff --git a/app/cli.php b/app/cli.php index dbbdfdee4d..ce978c6d9d 100644 --- a/app/cli.php +++ b/app/cli.php @@ -2,13 +2,11 @@ require_once __DIR__ . '/init.php'; -use Appwrite\Auth\Auth; use Appwrite\Event\Certificate; use Appwrite\Event\Delete; use Appwrite\Event\Func; use Appwrite\Event\StatsResources; use Appwrite\Event\StatsUsage; -use Appwrite\Network\Validator\Origin; use Appwrite\Platform\Appwrite; use Appwrite\Runtimes\Runtimes; use Utopia\Cache\Adapter\Sharding; @@ -18,7 +16,6 @@ use Utopia\CLI\Console; use Utopia\Config\Config; use Utopia\Database\Database; use Utopia\Database\Document; -use Utopia\Database\Helpers\ID; use Utopia\Database\Validator\Authorization; use Utopia\DSN\DSN; use Utopia\Logger\Log; @@ -103,45 +100,7 @@ CLI::setResource('dbForPlatform', function ($pools, $cache) { }, ['pools', 'cache']); CLI::setResource('console', function () { - return new Document([ - '$id' => ID::custom('console'), - '$internalId' => ID::custom('console'), - 'name' => 'Appwrite', - '$collection' => ID::custom('projects'), - 'description' => 'Appwrite core engine', - 'logo' => '', - 'teamId' => null, - 'webhooks' => [], - 'keys' => [], - 'platforms' => [ - [ - '$collection' => ID::custom('platforms'), - 'name' => 'Localhost', - 'type' => Origin::CLIENT_TYPE_WEB, - 'hostname' => 'localhost', - ], // Current host is added on app init - ], - 'legalName' => '', - 'legalCountry' => '', - 'legalState' => '', - 'legalCity' => '', - 'legalAddress' => '', - 'legalTaxId' => '', - 'auths' => [ - 'mockNumbers' => [], - 'invites' => System::getEnv('_APP_CONSOLE_INVITES', 'enabled') === 'enabled', - 'limit' => (System::getEnv('_APP_CONSOLE_WHITELIST_ROOT', 'enabled') === 'enabled') ? 1 : 0, // limit signup to 1 user - 'duration' => Auth::TOKEN_EXPIRATION_LOGIN_LONG, // 1 Year in seconds - 'sessionAlerts' => System::getEnv('_APP_CONSOLE_SESSION_ALERTS', 'disabled') === 'enabled' - ], - 'authWhitelistEmails' => (!empty(System::getEnv('_APP_CONSOLE_WHITELIST_EMAILS', null))) ? \explode(',', System::getEnv('_APP_CONSOLE_WHITELIST_EMAILS', null)) : [], - 'authWhitelistIPs' => (!empty(System::getEnv('_APP_CONSOLE_WHITELIST_IPS', null))) ? \explode(',', System::getEnv('_APP_CONSOLE_WHITELIST_IPS', null)) : [], - 'oAuthProviders' => [ - 'githubEnabled' => true, - 'githubSecret' => System::getEnv('_APP_CONSOLE_GITHUB_SECRET', ''), - 'githubAppid' => System::getEnv('_APP_CONSOLE_GITHUB_APP_ID', '') - ], - ]); + return new Document(Config::getParam('console')); }, []); CLI::setResource('getProjectDB', function (Group $pools, Database $dbForPlatform, $cache) { diff --git a/app/config/console.php b/app/config/console.php new file mode 100644 index 0000000000..5c15a7930f --- /dev/null +++ b/app/config/console.php @@ -0,0 +1,52 @@ + ID::custom('console'), + '$internalId' => ID::custom('console'), + 'name' => 'Appwrite', + '$collection' => ID::custom('projects'), + 'description' => 'Appwrite core engine', + 'logo' => '', + 'teamId' => null, + 'webhooks' => [], + 'keys' => [], + 'platforms' => [ + [ + '$collection' => ID::custom('platforms'), + 'name' => 'Localhost', + 'type' => Origin::CLIENT_TYPE_WEB, + 'hostname' => 'localhost', + ], // Current host is added on app init + ], + 'legalName' => '', + 'legalCountry' => '', + 'legalState' => '', + 'legalCity' => '', + 'legalAddress' => '', + 'legalTaxId' => '', + 'auths' => [ + 'mockNumbers' => [], + 'invites' => System::getEnv('_APP_CONSOLE_INVITES', 'enabled') === 'enabled', + 'limit' => (System::getEnv('_APP_CONSOLE_WHITELIST_ROOT', 'enabled') === 'enabled') ? 1 : 0, // limit signup to 1 user + 'duration' => Auth::TOKEN_EXPIRATION_LOGIN_LONG, // 1 Year in seconds + 'sessionAlerts' => System::getEnv('_APP_CONSOLE_SESSION_ALERTS', 'disabled') === 'enabled' + ], + 'authWhitelistEmails' => (!empty(System::getEnv('_APP_CONSOLE_WHITELIST_EMAILS', null))) ? \explode(',', System::getEnv('_APP_CONSOLE_WHITELIST_EMAILS', null)) : [], + 'authWhitelistIPs' => (!empty(System::getEnv('_APP_CONSOLE_WHITELIST_IPS', null))) ? \explode(',', System::getEnv('_APP_CONSOLE_WHITELIST_IPS', null)) : [], + 'oAuthProviders' => [ + 'githubEnabled' => true, + 'githubSecret' => System::getEnv('_APP_CONSOLE_GITHUB_SECRET', ''), + 'githubAppid' => System::getEnv('_APP_CONSOLE_GITHUB_APP_ID', '') + ], +]; + +return $console; diff --git a/app/init.php b/app/init.php index 1d7f734723..17c1a884f9 100644 --- a/app/init.php +++ b/app/init.php @@ -347,6 +347,7 @@ Config::load('apis', __DIR__ . '/config/apis.php'); // List of APIs Config::load('errors', __DIR__ . '/config/errors.php'); Config::load('oAuthProviders', __DIR__ . '/config/oAuthProviders.php'); Config::load('platforms', __DIR__ . '/config/platforms.php'); +Config::load('console', __DIR__ . '/config/console.php'); Config::load('collections', __DIR__ . '/config/collections.php'); Config::load('runtimes', __DIR__ . '/config/runtimes.php'); Config::load('runtimes-v2', __DIR__ . '/config/runtimes-v2.php'); @@ -1408,45 +1409,7 @@ App::setResource('session', function (Document $user) { }, ['user']); App::setResource('console', function () { - return new Document([ - '$id' => ID::custom('console'), - '$internalId' => ID::custom('console'), - 'name' => 'Appwrite', - '$collection' => ID::custom('projects'), - 'description' => 'Appwrite core engine', - 'logo' => '', - 'teamId' => null, - 'webhooks' => [], - 'keys' => [], - 'platforms' => [ - [ - '$collection' => ID::custom('platforms'), - 'name' => 'Localhost', - 'type' => Origin::CLIENT_TYPE_WEB, - 'hostname' => 'localhost', - ], // Current host is added on app init - ], - 'legalName' => '', - 'legalCountry' => '', - 'legalState' => '', - 'legalCity' => '', - 'legalAddress' => '', - 'legalTaxId' => '', - 'auths' => [ - 'mockNumbers' => [], - 'invites' => System::getEnv('_APP_CONSOLE_INVITES', 'enabled') === 'enabled', - 'limit' => (System::getEnv('_APP_CONSOLE_WHITELIST_ROOT', 'enabled') === 'enabled') ? 1 : 0, // limit signup to 1 user - 'duration' => Auth::TOKEN_EXPIRATION_LOGIN_LONG, // 1 Year in seconds - 'sessionAlerts' => System::getEnv('_APP_CONSOLE_SESSION_ALERTS', 'disabled') === 'enabled' - ], - 'authWhitelistEmails' => (!empty(System::getEnv('_APP_CONSOLE_WHITELIST_EMAILS', null))) ? \explode(',', System::getEnv('_APP_CONSOLE_WHITELIST_EMAILS', null)) : [], - 'authWhitelistIPs' => (!empty(System::getEnv('_APP_CONSOLE_WHITELIST_IPS', null))) ? \explode(',', System::getEnv('_APP_CONSOLE_WHITELIST_IPS', null)) : [], - 'oAuthProviders' => [ - 'githubEnabled' => true, - 'githubSecret' => System::getEnv('_APP_CONSOLE_GITHUB_SECRET', ''), - 'githubAppid' => System::getEnv('_APP_CONSOLE_GITHUB_APP_ID', '') - ], - ]); + return new Document(Config::getParam('console')); }, []); App::setResource('dbForProject', function (Group $pools, Database $dbForPlatform, Cache $cache, Document $project) { From e3f59c696d7588140134d1cdfcedfe471a5c24cc Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Tue, 11 Mar 2025 05:43:30 +0000 Subject: [PATCH 105/143] bump: python version --- app/config/platforms.php | 2 +- composer.lock | 28 ++++++++++++++-------------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/app/config/platforms.php b/app/config/platforms.php index 477d2a47b2..86f0a756c9 100644 --- a/app/config/platforms.php +++ b/app/config/platforms.php @@ -299,7 +299,7 @@ return [ [ 'key' => 'python', 'name' => 'Python', - 'version' => '6.2.0', + 'version' => '9.0.2', 'url' => 'https://github.com/appwrite/sdk-for-python', 'package' => 'https://pypi.org/project/appwrite/', 'enabled' => true, diff --git a/composer.lock b/composer.lock index e89d6b53b5..f033ea4ec0 100644 --- a/composer.lock +++ b/composer.lock @@ -5043,16 +5043,16 @@ "packages-dev": [ { "name": "appwrite/sdk-generator", - "version": "0.40.2", + "version": "0.40.6", "source": { "type": "git", "url": "https://github.com/appwrite/sdk-generator.git", - "reference": "56f09482d9e2f223911277ab887f197402708049" + "reference": "d8816209a07e7d64ef62dbcaf8ad4aa1262f58b9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/56f09482d9e2f223911277ab887f197402708049", - "reference": "56f09482d9e2f223911277ab887f197402708049", + "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/d8816209a07e7d64ef62dbcaf8ad4aa1262f58b9", + "reference": "d8816209a07e7d64ef62dbcaf8ad4aa1262f58b9", "shasum": "" }, "require": { @@ -5088,9 +5088,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/0.40.2" + "source": "https://github.com/appwrite/sdk-generator/tree/0.40.6" }, - "time": "2025-03-06T16:31:03+00:00" + "time": "2025-03-10T19:04:24+00:00" }, { "name": "doctrine/annotations", @@ -5317,16 +5317,16 @@ }, { "name": "laravel/pint", - "version": "v1.21.0", + "version": "v1.21.1", "source": { "type": "git", "url": "https://github.com/laravel/pint.git", - "reference": "531fa0871fbde719c51b12afa3a443b8f4e4b425" + "reference": "c44bffbb2334e90fba560933c45948fa4a3f3e86" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/pint/zipball/531fa0871fbde719c51b12afa3a443b8f4e4b425", - "reference": "531fa0871fbde719c51b12afa3a443b8f4e4b425", + "url": "https://api.github.com/repos/laravel/pint/zipball/c44bffbb2334e90fba560933c45948fa4a3f3e86", + "reference": "c44bffbb2334e90fba560933c45948fa4a3f3e86", "shasum": "" }, "require": { @@ -5337,9 +5337,9 @@ "php": "^8.2.0" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^3.68.5", - "illuminate/view": "^11.42.0", - "larastan/larastan": "^3.0.4", + "friendsofphp/php-cs-fixer": "^3.70.2", + "illuminate/view": "^11.44.1", + "larastan/larastan": "^3.1.0", "laravel-zero/framework": "^11.36.1", "mockery/mockery": "^1.6.12", "nunomaduro/termwind": "^2.3", @@ -5379,7 +5379,7 @@ "issues": "https://github.com/laravel/pint/issues", "source": "https://github.com/laravel/pint" }, - "time": "2025-02-18T03:18:57+00:00" + "time": "2025-03-11T03:22:21+00:00" }, { "name": "matthiasmullie/minify", From b387823d63a04b97bd69d91d7152a37ac60e3827 Mon Sep 17 00:00:00 2001 From: shimon Date: Tue, 11 Mar 2025 09:17:28 +0200 Subject: [PATCH 106/143] Update audit timestamp origin --- src/Appwrite/Platform/Workers/Audits.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Appwrite/Platform/Workers/Audits.php b/src/Appwrite/Platform/Workers/Audits.php index 269b29c4c8..4c052cbcaa 100644 --- a/src/Appwrite/Platform/Workers/Audits.php +++ b/src/Appwrite/Platform/Workers/Audits.php @@ -102,7 +102,7 @@ class Audits extends Action 'mode' => $mode, 'data' => $auditPayload, ], - 'timestamp' => DateTime::formatTz(DateTime::now()) + 'timestamp' => date("Y-m-d H:i:s", $message->getTimestamp()), ]; if (isset($this->logs[$project->getInternalId()])) { From 141f0fc376abd23fda870d15c3f1a28913fb3cda Mon Sep 17 00:00:00 2001 From: shimon Date: Tue, 11 Mar 2025 10:27:23 +0200 Subject: [PATCH 107/143] linter --- src/Appwrite/Platform/Workers/Audits.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Appwrite/Platform/Workers/Audits.php b/src/Appwrite/Platform/Workers/Audits.php index 4c052cbcaa..ed5ff8010a 100644 --- a/src/Appwrite/Platform/Workers/Audits.php +++ b/src/Appwrite/Platform/Workers/Audits.php @@ -7,7 +7,6 @@ use Exception; use Throwable; use Utopia\Audit\Audit; use Utopia\CLI\Console; -use Utopia\Database\DateTime; use Utopia\Database\Document; use Utopia\Database\Exception\Authorization; use Utopia\Database\Exception\Structure; From 2052fb975bf0cb6c3be90aa98af258af0642f38e Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Wed, 12 Mar 2025 16:53:08 +1300 Subject: [PATCH 108/143] Use bulk deletes for audit cleanup --- src/Appwrite/Platform/Workers/Deletes.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Appwrite/Platform/Workers/Deletes.php b/src/Appwrite/Platform/Workers/Deletes.php index 643fc4d010..06838e0232 100644 --- a/src/Appwrite/Platform/Workers/Deletes.php +++ b/src/Appwrite/Platform/Workers/Deletes.php @@ -732,10 +732,12 @@ class Deletes extends Action { $projectId = $project->getId(); $dbForProject = $getProjectDB($project); - $audit = new Audit($dbForProject); try { - $audit->cleanup($auditRetention); + $this->deleteByGroup(Audit::COLLECTION, [ + Query::lessThan('time', $auditRetention), + Query::orderDesc('time'), + ], $dbForProject); } catch (DatabaseException $e) { Console::error('Failed to delete audit logs for project ' . $projectId . ': ' . $e->getMessage()); } @@ -943,7 +945,7 @@ class Deletes extends Action * @param Database $database * @param ?callable $callback * @return void - * @throws Exception + * @throws DatabaseException */ protected function deleteByGroup( string $collection, From d77940d22f1d0a537591d6a5ad37d8b60cdd7367 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Wed, 12 Mar 2025 10:00:07 +0000 Subject: [PATCH 109/143] Feat: calculate and log time taken for each project --- src/Appwrite/Platform/Workers/StatsResources.php | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/Appwrite/Platform/Workers/StatsResources.php b/src/Appwrite/Platform/Workers/StatsResources.php index 969d43e895..420aeb5f77 100644 --- a/src/Appwrite/Platform/Workers/StatsResources.php +++ b/src/Appwrite/Platform/Workers/StatsResources.php @@ -77,7 +77,21 @@ class StatsResources extends Action // Reset documents for each job $this->documents = []; + $startTime = microtime(true); $this->countForProject($dbForPlatform, $getLogsDB, $getProjectDB, $project); + $endTime = microtime(true); + $executionTime = $endTime - $startTime; + Console::info('Project: ' . $project->getId() . '(' . $project->getInternalId() . ') aggregated in ' . $this->formatTime($executionTime) .''); + } + + public function formatTime($microseconds) + { + $seconds = $microseconds / 1000000; // Convert microseconds to seconds + $hours = floor($seconds / 3600); + $minutes = floor(($seconds % 3600) / 60); + $remainingSeconds = $seconds % 60; + + return sprintf('%02d:%02d:%06.3f', $hours, $minutes, $remainingSeconds); } From 5c25b583519b868ac994f2866dfff9a885a3113a Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Wed, 12 Mar 2025 10:19:55 +0000 Subject: [PATCH 110/143] Fix date format --- src/Appwrite/Platform/Workers/StatsResources.php | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/src/Appwrite/Platform/Workers/StatsResources.php b/src/Appwrite/Platform/Workers/StatsResources.php index 420aeb5f77..ecfe80bf2d 100644 --- a/src/Appwrite/Platform/Workers/StatsResources.php +++ b/src/Appwrite/Platform/Workers/StatsResources.php @@ -81,20 +81,9 @@ class StatsResources extends Action $this->countForProject($dbForPlatform, $getLogsDB, $getProjectDB, $project); $endTime = microtime(true); $executionTime = $endTime - $startTime; - Console::info('Project: ' . $project->getId() . '(' . $project->getInternalId() . ') aggregated in ' . $this->formatTime($executionTime) .''); + Console::info('Project: ' . $project->getId() . '(' . $project->getInternalId() . ') aggregated in ' . date('H:i:s.u', intval($executionTime)) .''); } - public function formatTime($microseconds) - { - $seconds = $microseconds / 1000000; // Convert microseconds to seconds - $hours = floor($seconds / 3600); - $minutes = floor(($seconds % 3600) / 60); - $remainingSeconds = $seconds % 60; - - return sprintf('%02d:%02d:%06.3f', $hours, $minutes, $remainingSeconds); - } - - protected function countForProject(Database $dbForPlatform, callable $getLogsDB, callable $getProjectDB, Document $project): void { Console::info('Begining count for: ' . $project->getId()); From 74f4ebf90ef32ac84242b77ec434221b1ad35d2a Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Wed, 12 Mar 2025 10:26:01 +0000 Subject: [PATCH 111/143] chore: update sdk generator --- composer.lock | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/composer.lock b/composer.lock index f033ea4ec0..83c8ea479a 100644 --- a/composer.lock +++ b/composer.lock @@ -5043,16 +5043,16 @@ "packages-dev": [ { "name": "appwrite/sdk-generator", - "version": "0.40.6", + "version": "0.40.7", "source": { "type": "git", "url": "https://github.com/appwrite/sdk-generator.git", - "reference": "d8816209a07e7d64ef62dbcaf8ad4aa1262f58b9" + "reference": "9e89b0bc4d8e6c81817d27096629f34a149fa873" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/d8816209a07e7d64ef62dbcaf8ad4aa1262f58b9", - "reference": "d8816209a07e7d64ef62dbcaf8ad4aa1262f58b9", + "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/9e89b0bc4d8e6c81817d27096629f34a149fa873", + "reference": "9e89b0bc4d8e6c81817d27096629f34a149fa873", "shasum": "" }, "require": { @@ -5088,9 +5088,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/0.40.6" + "source": "https://github.com/appwrite/sdk-generator/tree/0.40.7" }, - "time": "2025-03-10T19:04:24+00:00" + "time": "2025-03-12T08:43:55+00:00" }, { "name": "doctrine/annotations", @@ -5794,16 +5794,16 @@ }, { "name": "phpbench/phpbench", - "version": "1.4.0", + "version": "1.4.1", "source": { "type": "git", "url": "https://github.com/phpbench/phpbench.git", - "reference": "4248817222514421cba466bfa7adc7d8932345d4" + "reference": "78cd98a9aa34e0f8f80ca01972a8b88d2c30194b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpbench/phpbench/zipball/4248817222514421cba466bfa7adc7d8932345d4", - "reference": "4248817222514421cba466bfa7adc7d8932345d4", + "url": "https://api.github.com/repos/phpbench/phpbench/zipball/78cd98a9aa34e0f8f80ca01972a8b88d2c30194b", + "reference": "78cd98a9aa34e0f8f80ca01972a8b88d2c30194b", "shasum": "" }, "require": { @@ -5880,7 +5880,7 @@ ], "support": { "issues": "https://github.com/phpbench/phpbench/issues", - "source": "https://github.com/phpbench/phpbench/tree/1.4.0" + "source": "https://github.com/phpbench/phpbench/tree/1.4.1" }, "funding": [ { @@ -5888,7 +5888,7 @@ "type": "github" } ], - "time": "2025-01-26T19:54:45+00:00" + "time": "2025-03-12T08:01:40+00:00" }, { "name": "phpunit/php-code-coverage", From 81159e82d588a93c4b26827636a7c7c7eac40399 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Wed, 12 Mar 2025 10:29:07 +0000 Subject: [PATCH 112/143] show time in seconds --- src/Appwrite/Platform/Workers/StatsResources.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Appwrite/Platform/Workers/StatsResources.php b/src/Appwrite/Platform/Workers/StatsResources.php index ecfe80bf2d..1140698342 100644 --- a/src/Appwrite/Platform/Workers/StatsResources.php +++ b/src/Appwrite/Platform/Workers/StatsResources.php @@ -81,7 +81,7 @@ class StatsResources extends Action $this->countForProject($dbForPlatform, $getLogsDB, $getProjectDB, $project); $endTime = microtime(true); $executionTime = $endTime - $startTime; - Console::info('Project: ' . $project->getId() . '(' . $project->getInternalId() . ') aggregated in ' . date('H:i:s.u', intval($executionTime)) .''); + Console::info('Project: ' . $project->getId() . '(' . $project->getInternalId() . ') aggregated in ' . $executionTime .' seconds'); } protected function countForProject(Database $dbForPlatform, callable $getLogsDB, callable $getProjectDB, Document $project): void From ae00a69592d665fc63d5b246708ea3b87c14aac0 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Wed, 12 Mar 2025 11:58:06 +0000 Subject: [PATCH 113/143] chore: update initializing dbForLogs --- src/Appwrite/Platform/Workers/Deletes.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Appwrite/Platform/Workers/Deletes.php b/src/Appwrite/Platform/Workers/Deletes.php index 643fc4d010..d93ee3ebcf 100644 --- a/src/Appwrite/Platform/Workers/Deletes.php +++ b/src/Appwrite/Platform/Workers/Deletes.php @@ -414,8 +414,11 @@ class Deletes extends Action */ private function deleteUsageStats(Document $project, callable $getProjectDB, callable $getLogsDB, string $hourlyUsageRetentionDatetime): void { + /** @var \Utopia\Database\Database $dbForProject*/ $dbForProject = $getProjectDB($project); - $dbForLogs = $getLogsDB($project); + + /** @var \Utopia\Database\Database $dbForLogs*/ + $dbForLogs = call_user_func($getLogsDB, $project); // Delete Usage stats from projectDB $this->deleteByGroup('stats', [ From 46693faa67673bb0c24d23c7b8533e4825f3f3d1 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Wed, 12 Mar 2025 12:35:56 +0000 Subject: [PATCH 114/143] chore: prevent console project to queue for delete stats --- src/Appwrite/Platform/Workers/Deletes.php | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/Appwrite/Platform/Workers/Deletes.php b/src/Appwrite/Platform/Workers/Deletes.php index d93ee3ebcf..433b99c7a9 100644 --- a/src/Appwrite/Platform/Workers/Deletes.php +++ b/src/Appwrite/Platform/Workers/Deletes.php @@ -417,20 +417,22 @@ class Deletes extends Action /** @var \Utopia\Database\Database $dbForProject*/ $dbForProject = $getProjectDB($project); - /** @var \Utopia\Database\Database $dbForLogs*/ - $dbForLogs = call_user_func($getLogsDB, $project); - // Delete Usage stats from projectDB $this->deleteByGroup('stats', [ Query::lessThan('time', $hourlyUsageRetentionDatetime), Query::equal('period', ['1h']), ], $dbForProject); - // Delete Usage stats from logsDB - $this->deleteByGroup('stats', [ - Query::lessThan('time', $hourlyUsageRetentionDatetime), - Query::equal('period', ['1h']), - ], $dbForLogs); + if ($project->getId() !== 'console') { + /** @var \Utopia\Database\Database $dbForLogs*/ + $dbForLogs = call_user_func($getLogsDB, $project); + + // Delete Usage stats from logsDB + $this->deleteByGroup('stats', [ + Query::lessThan('time', $hourlyUsageRetentionDatetime), + Query::equal('period', ['1h']), + ], $dbForLogs); + } } /** From 7402145c9d2deb7e5c120d08973469d5553cb596 Mon Sep 17 00:00:00 2001 From: Steven Nguyen Date: Wed, 12 Mar 2025 15:22:43 -0700 Subject: [PATCH 115/143] Bump console to version 5.2.53 --- app/views/install/compose.phtml | 2 +- docker-compose.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/install/compose.phtml b/app/views/install/compose.phtml index 62fcd03624..b16806895f 100644 --- a/app/views/install/compose.phtml +++ b/app/views/install/compose.phtml @@ -168,7 +168,7 @@ $image = $this->getParam('image', ''); appwrite-console: <<: *x-logging container_name: appwrite-console - image: /console:5.2.27 + image: /console:5.2.53 restart: unless-stopped networks: - appwrite diff --git a/docker-compose.yml b/docker-compose.yml index facf0e6db9..b88f46e674 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -201,7 +201,7 @@ services: appwrite-console: <<: *x-logging container_name: appwrite-console - image: appwrite/console:5.2.27 + image: appwrite/console:5.2.53 restart: unless-stopped networks: - appwrite From 5bce1e17cb4b96d83d529787416021e017ac5a46 Mon Sep 17 00:00:00 2001 From: Steven Nguyen Date: Wed, 12 Mar 2025 15:34:38 -0700 Subject: [PATCH 116/143] chore: regenerate latest and 1.6.x specs --- app/config/specs/open-api3-1.6.x-client.json | 10 +- app/config/specs/open-api3-1.6.x-console.json | 222 ++++++++++------- app/config/specs/open-api3-1.6.x-server.json | 182 +++++++------- .../specs/open-api3-latest-console.json | 22 +- app/config/specs/open-api3-latest-server.json | 4 - app/config/specs/swagger2-1.6.x-client.json | 10 +- app/config/specs/swagger2-1.6.x-console.json | 226 +++++++++++------- app/config/specs/swagger2-1.6.x-server.json | 184 +++++++------- app/config/specs/swagger2-latest-console.json | 23 +- app/config/specs/swagger2-latest-server.json | 4 - 10 files changed, 495 insertions(+), 392 deletions(-) diff --git a/app/config/specs/open-api3-1.6.x-client.json b/app/config/specs/open-api3-1.6.x-client.json index 820b1f55e0..316fe13116 100644 --- a/app/config/specs/open-api3-1.6.x-client.json +++ b/app/config/specs/open-api3-1.6.x-client.json @@ -3383,7 +3383,7 @@ "parameters": [ { "name": "code", - "description": "Credit Card Code. Possible values: amex, argencard, cabal, cencosud, diners, discover, elo, hipercard, jcb, mastercard, naranja, targeta-shopping, union-china-pay, visa, mir, maestro.", + "description": "Credit Card Code. Possible values: amex, argencard, cabal, cencosud, diners, discover, elo, hipercard, jcb, mastercard, naranja, targeta-shopping, union-china-pay, visa, mir, maestro, rupay.", "required": true, "schema": { "type": "string", @@ -3404,7 +3404,8 @@ "union-china-pay", "visa", "mir", - "maestro" + "maestro", + "rupay" ], "x-enum-name": "CreditCard", "x-enum-keys": [ @@ -3423,7 +3424,8 @@ "Union China Pay", "Visa", "MIR", - "Maestro" + "Maestro", + "Rupay" ] }, "in": "path" @@ -4365,7 +4367,7 @@ "tags": [ "databases" ], - "description": "Create a new Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console.", + "description": "Create a new Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console.\n", "responses": { "201": { "description": "Document", diff --git a/app/config/specs/open-api3-1.6.x-console.json b/app/config/specs/open-api3-1.6.x-console.json index 7f57dfc437..54161c4262 100644 --- a/app/config/specs/open-api3-1.6.x-console.json +++ b/app/config/specs/open-api3-1.6.x-console.json @@ -3387,7 +3387,7 @@ "parameters": [ { "name": "code", - "description": "Credit Card Code. Possible values: amex, argencard, cabal, cencosud, diners, discover, elo, hipercard, jcb, mastercard, naranja, targeta-shopping, union-china-pay, visa, mir, maestro.", + "description": "Credit Card Code. Possible values: amex, argencard, cabal, cencosud, diners, discover, elo, hipercard, jcb, mastercard, naranja, targeta-shopping, union-china-pay, visa, mir, maestro, rupay.", "required": true, "schema": { "type": "string", @@ -3408,7 +3408,8 @@ "union-china-pay", "visa", "mir", - "maestro" + "maestro", + "rupay" ], "x-enum-name": "CreditCard", "x-enum-keys": [ @@ -3427,7 +3428,8 @@ "Union China Pay", "Visa", "MIR", - "Maestro" + "Maestro", + "Rupay" ] }, "in": "path" @@ -6407,8 +6409,6 @@ }, "required": [ "required", - "min", - "max", "default" ] } @@ -6644,8 +6644,6 @@ }, "required": [ "required", - "min", - "max", "default" ] } @@ -7823,7 +7821,7 @@ "tags": [ "databases" ], - "description": "Create a new Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console.", + "description": "Create a new Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console.\n", "responses": { "201": { "description": "Document", @@ -11585,7 +11583,7 @@ }, "x-appwrite": { "method": "getCertificate", - "weight": 134, + "weight": 133, "cookies": false, "type": "", "deprecated": false, @@ -11692,7 +11690,7 @@ }, "x-appwrite": { "method": "getPubSub", - "weight": 130, + "weight": 129, "cookies": false, "type": "", "deprecated": false, @@ -11718,54 +11716,6 @@ ] } }, - "\/health\/queue": { - "get": { - "summary": "Get queue", - "operationId": "healthGetQueue", - "tags": [ - "health" - ], - "description": "Check the Appwrite queue messaging servers are up and connection is successful.", - "responses": { - "200": { - "description": "Health Status", - "content": { - "application\/json": { - "schema": { - "$ref": "#\/components\/schemas\/healthStatus" - } - } - } - } - }, - "x-appwrite": { - "method": "getQueue", - "weight": 129, - "cookies": false, - "type": "", - "deprecated": false, - "demo": "health\/get-queue.md", - "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue.md", - "rate-limit": 0, - "rate-time": 3600, - "rate-key": "url:{url},ip:{ip}", - "scope": "health.read", - "platforms": [ - "server" - ], - "packaging": false, - "auth": { - "Project": [] - } - }, - "security": [ - { - "Project": [], - "Key": [] - } - ] - } - }, "\/health\/queue\/builds": { "get": { "summary": "Get builds queue", @@ -11788,7 +11738,7 @@ }, "x-appwrite": { "method": "getQueueBuilds", - "weight": 136, + "weight": 135, "cookies": false, "type": "", "deprecated": false, @@ -11849,7 +11799,7 @@ }, "x-appwrite": { "method": "getQueueCertificates", - "weight": 135, + "weight": 134, "cookies": false, "type": "", "deprecated": false, @@ -11910,7 +11860,7 @@ }, "x-appwrite": { "method": "getQueueDatabases", - "weight": 137, + "weight": 136, "cookies": false, "type": "", "deprecated": false, @@ -11982,7 +11932,7 @@ }, "x-appwrite": { "method": "getQueueDeletes", - "weight": 138, + "weight": 137, "cookies": false, "type": "", "deprecated": false, @@ -12081,8 +12031,9 @@ "v1-audits", "v1-mails", "v1-functions", - "v1-usage", - "v1-usage-dump", + "v1-stats-resources", + "v1-stats-usage", + "v1-stats-usage-dump", "v1-webhooks", "v1-certificates", "v1-builds", @@ -12130,7 +12081,7 @@ }, "x-appwrite": { "method": "getQueueFunctions", - "weight": 142, + "weight": 141, "cookies": false, "type": "", "deprecated": false, @@ -12191,7 +12142,7 @@ }, "x-appwrite": { "method": "getQueueLogs", - "weight": 133, + "weight": 132, "cookies": false, "type": "", "deprecated": false, @@ -12252,7 +12203,7 @@ }, "x-appwrite": { "method": "getQueueMails", - "weight": 139, + "weight": 138, "cookies": false, "type": "", "deprecated": false, @@ -12313,7 +12264,7 @@ }, "x-appwrite": { "method": "getQueueMessaging", - "weight": 140, + "weight": 139, "cookies": false, "type": "", "deprecated": false, @@ -12374,7 +12325,7 @@ }, "x-appwrite": { "method": "getQueueMigrations", - "weight": 141, + "weight": 140, "cookies": false, "type": "", "deprecated": false, @@ -12413,14 +12364,14 @@ ] } }, - "\/health\/queue\/usage": { + "\/health\/queue\/stats-resources": { "get": { - "summary": "Get usage queue", - "operationId": "healthGetQueueUsage", + "summary": "Get stats resources queue", + "operationId": "healthGetQueueStatsResources", "tags": [ "health" ], - "description": "Get the number of metrics that are waiting to be processed in the Appwrite internal queue server.", + "description": "Get the number of metrics that are waiting to be processed in the Appwrite stats resources queue.", "responses": { "200": { "description": "Health Queue", @@ -12434,13 +12385,13 @@ } }, "x-appwrite": { - "method": "getQueueUsage", - "weight": 143, + "method": "getQueueStatsResources", + "weight": 142, "cookies": false, "type": "", "deprecated": false, - "demo": "health\/get-queue-usage.md", - "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-usage.md", + "demo": "health\/get-queue-stats-resources.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-stats-resources.md", "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", @@ -12474,10 +12425,71 @@ ] } }, - "\/health\/queue\/usage-dump": { + "\/health\/queue\/stats-usage": { + "get": { + "summary": "Get stats usage queue", + "operationId": "healthGetQueueUsage", + "tags": [ + "health" + ], + "description": "Get the number of metrics that are waiting to be processed in the Appwrite internal queue server.", + "responses": { + "200": { + "description": "Health Queue", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/healthQueue" + } + } + } + } + }, + "x-appwrite": { + "method": "getQueueUsage", + "weight": 143, + "cookies": false, + "type": "", + "deprecated": false, + "demo": "health\/get-queue-usage.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-stats-usage.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "health.read", + "platforms": [ + "server" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Key": [] + } + ], + "parameters": [ + { + "name": "threshold", + "description": "Queue size threshold. When hit (equal or higher), endpoint returns server error. Default value is 5000.", + "required": false, + "schema": { + "type": "integer", + "format": "int32", + "default": 5000 + }, + "in": "query" + } + ] + } + }, + "\/health\/queue\/stats-usage-dump": { "get": { "summary": "Get usage dump queue", - "operationId": "healthGetQueueUsageDump", + "operationId": "healthGetQueueStatsUsageDump", "tags": [ "health" ], @@ -12495,13 +12507,13 @@ } }, "x-appwrite": { - "method": "getQueueUsageDump", + "method": "getQueueStatsUsageDump", "weight": 144, "cookies": false, "type": "", "deprecated": false, - "demo": "health\/get-queue-usage-dump.md", - "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-usage-dump.md", + "demo": "health\/get-queue-stats-usage-dump.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-stats-usage-dump.md", "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", @@ -12557,7 +12569,7 @@ }, "x-appwrite": { "method": "getQueueWebhooks", - "weight": 132, + "weight": 131, "cookies": false, "type": "", "deprecated": false, @@ -12714,7 +12726,7 @@ }, "x-appwrite": { "method": "getTime", - "weight": 131, + "weight": 130, "cookies": false, "type": "", "deprecated": false, @@ -17609,17 +17621,17 @@ }, "endpoint": { "type": "string", - "description": "Source's Appwrite Endpoint", + "description": "Source Appwrite endpoint", "x-example": "https:\/\/example.com" }, "projectId": { "type": "string", - "description": "Source's Project ID", + "description": "Source Project ID", "x-example": "" }, "apiKey": { "type": "string", - "description": "Source's API Key", + "description": "Source API Key", "x-example": "" } }, @@ -36052,6 +36064,20 @@ "$ref": "#\/components\/schemas\/metric" }, "x-example": [] + }, + "imageTransformations": { + "type": "array", + "description": "Aggregated number of files transformations per period.", + "items": { + "$ref": "#\/components\/schemas\/metric" + }, + "x-example": [] + }, + "imageTransformationsTotal": { + "type": "integer", + "description": "Total aggregated number of files transformations.", + "x-example": 0, + "format": "int32" } }, "required": [ @@ -36059,7 +36085,9 @@ "filesTotal", "filesStorageTotal", "files", - "storage" + "storage", + "imageTransformations", + "imageTransformationsTotal" ] }, "usageFunctions": { @@ -36597,6 +36625,20 @@ "$ref": "#\/components\/schemas\/metric" }, "x-example": [] + }, + "imageTransformations": { + "type": "array", + "description": "An array of aggregated number of image transformations.", + "items": { + "$ref": "#\/components\/schemas\/metric" + }, + "x-example": [] + }, + "imageTransformationsTotal": { + "type": "integer", + "description": "Total aggregated number of image transformations.", + "x-example": 0, + "format": "int32" } }, "required": [ @@ -36628,7 +36670,9 @@ "authPhoneEstimate", "authPhoneCountryBreakdown", "databasesReads", - "databasesWrites" + "databasesWrites", + "imageTransformations", + "imageTransformationsTotal" ] }, "headers": { diff --git a/app/config/specs/open-api3-1.6.x-server.json b/app/config/specs/open-api3-1.6.x-server.json index 68d408762a..3d32d3e978 100644 --- a/app/config/specs/open-api3-1.6.x-server.json +++ b/app/config/specs/open-api3-1.6.x-server.json @@ -3077,7 +3077,7 @@ "parameters": [ { "name": "code", - "description": "Credit Card Code. Possible values: amex, argencard, cabal, cencosud, diners, discover, elo, hipercard, jcb, mastercard, naranja, targeta-shopping, union-china-pay, visa, mir, maestro.", + "description": "Credit Card Code. Possible values: amex, argencard, cabal, cencosud, diners, discover, elo, hipercard, jcb, mastercard, naranja, targeta-shopping, union-china-pay, visa, mir, maestro, rupay.", "required": true, "schema": { "type": "string", @@ -3098,7 +3098,8 @@ "union-china-pay", "visa", "mir", - "maestro" + "maestro", + "rupay" ], "x-enum-name": "CreditCard", "x-enum-keys": [ @@ -3117,7 +3118,8 @@ "Union China Pay", "Visa", "MIR", - "Maestro" + "Maestro", + "Rupay" ] }, "in": "path" @@ -5951,8 +5953,6 @@ }, "required": [ "required", - "min", - "max", "default" ] } @@ -6190,8 +6190,6 @@ }, "required": [ "required", - "min", - "max", "default" ] } @@ -7381,7 +7379,7 @@ "tags": [ "databases" ], - "description": "Create a new Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console.", + "description": "Create a new Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console.\n", "responses": { "201": { "description": "Document", @@ -10461,7 +10459,7 @@ }, "x-appwrite": { "method": "getCertificate", - "weight": 134, + "weight": 133, "cookies": false, "type": "", "deprecated": false, @@ -10570,7 +10568,7 @@ }, "x-appwrite": { "method": "getPubSub", - "weight": 130, + "weight": 129, "cookies": false, "type": "", "deprecated": false, @@ -10597,55 +10595,6 @@ ] } }, - "\/health\/queue": { - "get": { - "summary": "Get queue", - "operationId": "healthGetQueue", - "tags": [ - "health" - ], - "description": "Check the Appwrite queue messaging servers are up and connection is successful.", - "responses": { - "200": { - "description": "Health Status", - "content": { - "application\/json": { - "schema": { - "$ref": "#\/components\/schemas\/healthStatus" - } - } - } - } - }, - "x-appwrite": { - "method": "getQueue", - "weight": 129, - "cookies": false, - "type": "", - "deprecated": false, - "demo": "health\/get-queue.md", - "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue.md", - "rate-limit": 0, - "rate-time": 3600, - "rate-key": "url:{url},ip:{ip}", - "scope": "health.read", - "platforms": [ - "server" - ], - "packaging": false, - "auth": { - "Project": [], - "Key": [] - } - }, - "security": [ - { - "Project": [], - "Key": [] - } - ] - } - }, "\/health\/queue\/builds": { "get": { "summary": "Get builds queue", @@ -10668,7 +10617,7 @@ }, "x-appwrite": { "method": "getQueueBuilds", - "weight": 136, + "weight": 135, "cookies": false, "type": "", "deprecated": false, @@ -10730,7 +10679,7 @@ }, "x-appwrite": { "method": "getQueueCertificates", - "weight": 135, + "weight": 134, "cookies": false, "type": "", "deprecated": false, @@ -10792,7 +10741,7 @@ }, "x-appwrite": { "method": "getQueueDatabases", - "weight": 137, + "weight": 136, "cookies": false, "type": "", "deprecated": false, @@ -10865,7 +10814,7 @@ }, "x-appwrite": { "method": "getQueueDeletes", - "weight": 138, + "weight": 137, "cookies": false, "type": "", "deprecated": false, @@ -10966,8 +10915,9 @@ "v1-audits", "v1-mails", "v1-functions", - "v1-usage", - "v1-usage-dump", + "v1-stats-resources", + "v1-stats-usage", + "v1-stats-usage-dump", "v1-webhooks", "v1-certificates", "v1-builds", @@ -11015,7 +10965,7 @@ }, "x-appwrite": { "method": "getQueueFunctions", - "weight": 142, + "weight": 141, "cookies": false, "type": "", "deprecated": false, @@ -11077,7 +11027,7 @@ }, "x-appwrite": { "method": "getQueueLogs", - "weight": 133, + "weight": 132, "cookies": false, "type": "", "deprecated": false, @@ -11139,7 +11089,7 @@ }, "x-appwrite": { "method": "getQueueMails", - "weight": 139, + "weight": 138, "cookies": false, "type": "", "deprecated": false, @@ -11201,7 +11151,7 @@ }, "x-appwrite": { "method": "getQueueMessaging", - "weight": 140, + "weight": 139, "cookies": false, "type": "", "deprecated": false, @@ -11263,7 +11213,7 @@ }, "x-appwrite": { "method": "getQueueMigrations", - "weight": 141, + "weight": 140, "cookies": false, "type": "", "deprecated": false, @@ -11303,14 +11253,14 @@ ] } }, - "\/health\/queue\/usage": { + "\/health\/queue\/stats-resources": { "get": { - "summary": "Get usage queue", - "operationId": "healthGetQueueUsage", + "summary": "Get stats resources queue", + "operationId": "healthGetQueueStatsResources", "tags": [ "health" ], - "description": "Get the number of metrics that are waiting to be processed in the Appwrite internal queue server.", + "description": "Get the number of metrics that are waiting to be processed in the Appwrite stats resources queue.", "responses": { "200": { "description": "Health Queue", @@ -11324,13 +11274,13 @@ } }, "x-appwrite": { - "method": "getQueueUsage", - "weight": 143, + "method": "getQueueStatsResources", + "weight": 142, "cookies": false, "type": "", "deprecated": false, - "demo": "health\/get-queue-usage.md", - "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-usage.md", + "demo": "health\/get-queue-stats-resources.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-stats-resources.md", "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", @@ -11365,10 +11315,72 @@ ] } }, - "\/health\/queue\/usage-dump": { + "\/health\/queue\/stats-usage": { + "get": { + "summary": "Get stats usage queue", + "operationId": "healthGetQueueUsage", + "tags": [ + "health" + ], + "description": "Get the number of metrics that are waiting to be processed in the Appwrite internal queue server.", + "responses": { + "200": { + "description": "Health Queue", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/healthQueue" + } + } + } + } + }, + "x-appwrite": { + "method": "getQueueUsage", + "weight": 143, + "cookies": false, + "type": "", + "deprecated": false, + "demo": "health\/get-queue-usage.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-stats-usage.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "health.read", + "platforms": [ + "server" + ], + "packaging": false, + "auth": { + "Project": [], + "Key": [] + } + }, + "security": [ + { + "Project": [], + "Key": [] + } + ], + "parameters": [ + { + "name": "threshold", + "description": "Queue size threshold. When hit (equal or higher), endpoint returns server error. Default value is 5000.", + "required": false, + "schema": { + "type": "integer", + "format": "int32", + "default": 5000 + }, + "in": "query" + } + ] + } + }, + "\/health\/queue\/stats-usage-dump": { "get": { "summary": "Get usage dump queue", - "operationId": "healthGetQueueUsageDump", + "operationId": "healthGetQueueStatsUsageDump", "tags": [ "health" ], @@ -11386,13 +11398,13 @@ } }, "x-appwrite": { - "method": "getQueueUsageDump", + "method": "getQueueStatsUsageDump", "weight": 144, "cookies": false, "type": "", "deprecated": false, - "demo": "health\/get-queue-usage-dump.md", - "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-usage-dump.md", + "demo": "health\/get-queue-stats-usage-dump.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-stats-usage-dump.md", "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", @@ -11449,7 +11461,7 @@ }, "x-appwrite": { "method": "getQueueWebhooks", - "weight": 132, + "weight": 131, "cookies": false, "type": "", "deprecated": false, @@ -11609,7 +11621,7 @@ }, "x-appwrite": { "method": "getTime", - "weight": 131, + "weight": 130, "cookies": false, "type": "", "deprecated": false, diff --git a/app/config/specs/open-api3-latest-console.json b/app/config/specs/open-api3-latest-console.json index bc216226fb..54161c4262 100644 --- a/app/config/specs/open-api3-latest-console.json +++ b/app/config/specs/open-api3-latest-console.json @@ -6409,8 +6409,6 @@ }, "required": [ "required", - "min", - "max", "default" ] } @@ -6646,8 +6644,6 @@ }, "required": [ "required", - "min", - "max", "default" ] } @@ -36630,13 +36626,15 @@ }, "x-example": [] }, - "imageTransformationsTotal": { - "type": "integer", - "description": "An array of aggregated number of image transformations.", - "x-example": 0, - "format": "int32" - }, "imageTransformations": { + "type": "array", + "description": "An array of aggregated number of image transformations.", + "items": { + "$ref": "#\/components\/schemas\/metric" + }, + "x-example": [] + }, + "imageTransformationsTotal": { "type": "integer", "description": "Total aggregated number of image transformations.", "x-example": 0, @@ -36673,8 +36671,8 @@ "authPhoneCountryBreakdown", "databasesReads", "databasesWrites", - "imageTransformationsTotal", - "imageTransformations" + "imageTransformations", + "imageTransformationsTotal" ] }, "headers": { diff --git a/app/config/specs/open-api3-latest-server.json b/app/config/specs/open-api3-latest-server.json index 280c080514..3d32d3e978 100644 --- a/app/config/specs/open-api3-latest-server.json +++ b/app/config/specs/open-api3-latest-server.json @@ -5953,8 +5953,6 @@ }, "required": [ "required", - "min", - "max", "default" ] } @@ -6192,8 +6190,6 @@ }, "required": [ "required", - "min", - "max", "default" ] } diff --git a/app/config/specs/swagger2-1.6.x-client.json b/app/config/specs/swagger2-1.6.x-client.json index c0980c44ce..8960bfaa5c 100644 --- a/app/config/specs/swagger2-1.6.x-client.json +++ b/app/config/specs/swagger2-1.6.x-client.json @@ -3557,7 +3557,7 @@ "parameters": [ { "name": "code", - "description": "Credit Card Code. Possible values: amex, argencard, cabal, cencosud, diners, discover, elo, hipercard, jcb, mastercard, naranja, targeta-shopping, union-china-pay, visa, mir, maestro.", + "description": "Credit Card Code. Possible values: amex, argencard, cabal, cencosud, diners, discover, elo, hipercard, jcb, mastercard, naranja, targeta-shopping, union-china-pay, visa, mir, maestro, rupay.", "required": true, "type": "string", "x-example": "amex", @@ -3577,7 +3577,8 @@ "union-china-pay", "visa", "mir", - "maestro" + "maestro", + "rupay" ], "x-enum-name": "CreditCard", "x-enum-keys": [ @@ -3596,7 +3597,8 @@ "Union China Pay", "Visa", "MIR", - "Maestro" + "Maestro", + "Rupay" ], "in": "path" }, @@ -4547,7 +4549,7 @@ "tags": [ "databases" ], - "description": "Create a new Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console.", + "description": "Create a new Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console.\n", "responses": { "201": { "description": "Document", diff --git a/app/config/specs/swagger2-1.6.x-console.json b/app/config/specs/swagger2-1.6.x-console.json index 94b0d55199..8fc7e7daf3 100644 --- a/app/config/specs/swagger2-1.6.x-console.json +++ b/app/config/specs/swagger2-1.6.x-console.json @@ -3577,7 +3577,7 @@ "parameters": [ { "name": "code", - "description": "Credit Card Code. Possible values: amex, argencard, cabal, cencosud, diners, discover, elo, hipercard, jcb, mastercard, naranja, targeta-shopping, union-china-pay, visa, mir, maestro.", + "description": "Credit Card Code. Possible values: amex, argencard, cabal, cencosud, diners, discover, elo, hipercard, jcb, mastercard, naranja, targeta-shopping, union-china-pay, visa, mir, maestro, rupay.", "required": true, "type": "string", "x-example": "amex", @@ -3597,7 +3597,8 @@ "union-china-pay", "visa", "mir", - "maestro" + "maestro", + "rupay" ], "x-enum-name": "CreditCard", "x-enum-keys": [ @@ -3616,7 +3617,8 @@ "Union China Pay", "Visa", "MIR", - "Maestro" + "Maestro", + "Rupay" ], "in": "path" }, @@ -6607,8 +6609,6 @@ }, "required": [ "required", - "min", - "max", "default" ] } @@ -6845,8 +6845,6 @@ }, "required": [ "required", - "min", - "max", "default" ] } @@ -8012,7 +8010,7 @@ "tags": [ "databases" ], - "description": "Create a new Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console.", + "description": "Create a new Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console.\n", "responses": { "201": { "description": "Document", @@ -11810,7 +11808,7 @@ }, "x-appwrite": { "method": "getCertificate", - "weight": 134, + "weight": 133, "cookies": false, "type": "", "deprecated": false, @@ -11919,7 +11917,7 @@ }, "x-appwrite": { "method": "getPubSub", - "weight": 130, + "weight": 129, "cookies": false, "type": "", "deprecated": false, @@ -11945,56 +11943,6 @@ ] } }, - "\/health\/queue": { - "get": { - "summary": "Get queue", - "operationId": "healthGetQueue", - "consumes": [ - "application\/json" - ], - "produces": [ - "application\/json" - ], - "tags": [ - "health" - ], - "description": "Check the Appwrite queue messaging servers are up and connection is successful.", - "responses": { - "200": { - "description": "Health Status", - "schema": { - "$ref": "#\/definitions\/healthStatus" - } - } - }, - "x-appwrite": { - "method": "getQueue", - "weight": 129, - "cookies": false, - "type": "", - "deprecated": false, - "demo": "health\/get-queue.md", - "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue.md", - "rate-limit": 0, - "rate-time": 3600, - "rate-key": "url:{url},ip:{ip}", - "scope": "health.read", - "platforms": [ - "server" - ], - "packaging": false, - "auth": { - "Project": [] - } - }, - "security": [ - { - "Project": [], - "Key": [] - } - ] - } - }, "\/health\/queue\/builds": { "get": { "summary": "Get builds queue", @@ -12019,7 +11967,7 @@ }, "x-appwrite": { "method": "getQueueBuilds", - "weight": 136, + "weight": 135, "cookies": false, "type": "", "deprecated": false, @@ -12080,7 +12028,7 @@ }, "x-appwrite": { "method": "getQueueCertificates", - "weight": 135, + "weight": 134, "cookies": false, "type": "", "deprecated": false, @@ -12141,7 +12089,7 @@ }, "x-appwrite": { "method": "getQueueDatabases", - "weight": 137, + "weight": 136, "cookies": false, "type": "", "deprecated": false, @@ -12211,7 +12159,7 @@ }, "x-appwrite": { "method": "getQueueDeletes", - "weight": 138, + "weight": 137, "cookies": false, "type": "", "deprecated": false, @@ -12309,8 +12257,9 @@ "v1-audits", "v1-mails", "v1-functions", - "v1-usage", - "v1-usage-dump", + "v1-stats-resources", + "v1-stats-usage", + "v1-stats-usage-dump", "v1-webhooks", "v1-certificates", "v1-builds", @@ -12357,7 +12306,7 @@ }, "x-appwrite": { "method": "getQueueFunctions", - "weight": 142, + "weight": 141, "cookies": false, "type": "", "deprecated": false, @@ -12418,7 +12367,7 @@ }, "x-appwrite": { "method": "getQueueLogs", - "weight": 133, + "weight": 132, "cookies": false, "type": "", "deprecated": false, @@ -12479,7 +12428,7 @@ }, "x-appwrite": { "method": "getQueueMails", - "weight": 139, + "weight": 138, "cookies": false, "type": "", "deprecated": false, @@ -12540,7 +12489,7 @@ }, "x-appwrite": { "method": "getQueueMessaging", - "weight": 140, + "weight": 139, "cookies": false, "type": "", "deprecated": false, @@ -12601,7 +12550,7 @@ }, "x-appwrite": { "method": "getQueueMigrations", - "weight": 141, + "weight": 140, "cookies": false, "type": "", "deprecated": false, @@ -12638,10 +12587,10 @@ ] } }, - "\/health\/queue\/usage": { + "\/health\/queue\/stats-resources": { "get": { - "summary": "Get usage queue", - "operationId": "healthGetQueueUsage", + "summary": "Get stats resources queue", + "operationId": "healthGetQueueStatsResources", "consumes": [ "application\/json" ], @@ -12651,7 +12600,7 @@ "tags": [ "health" ], - "description": "Get the number of metrics that are waiting to be processed in the Appwrite internal queue server.", + "description": "Get the number of metrics that are waiting to be processed in the Appwrite stats resources queue.", "responses": { "200": { "description": "Health Queue", @@ -12661,13 +12610,13 @@ } }, "x-appwrite": { - "method": "getQueueUsage", - "weight": 143, + "method": "getQueueStatsResources", + "weight": 142, "cookies": false, "type": "", "deprecated": false, - "demo": "health\/get-queue-usage.md", - "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-usage.md", + "demo": "health\/get-queue-stats-resources.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-stats-resources.md", "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", @@ -12699,10 +12648,71 @@ ] } }, - "\/health\/queue\/usage-dump": { + "\/health\/queue\/stats-usage": { + "get": { + "summary": "Get stats usage queue", + "operationId": "healthGetQueueUsage", + "consumes": [ + "application\/json" + ], + "produces": [ + "application\/json" + ], + "tags": [ + "health" + ], + "description": "Get the number of metrics that are waiting to be processed in the Appwrite internal queue server.", + "responses": { + "200": { + "description": "Health Queue", + "schema": { + "$ref": "#\/definitions\/healthQueue" + } + } + }, + "x-appwrite": { + "method": "getQueueUsage", + "weight": 143, + "cookies": false, + "type": "", + "deprecated": false, + "demo": "health\/get-queue-usage.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-stats-usage.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "health.read", + "platforms": [ + "server" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Key": [] + } + ], + "parameters": [ + { + "name": "threshold", + "description": "Queue size threshold. When hit (equal or higher), endpoint returns server error. Default value is 5000.", + "required": false, + "type": "integer", + "format": "int32", + "default": 5000, + "in": "query" + } + ] + } + }, + "\/health\/queue\/stats-usage-dump": { "get": { "summary": "Get usage dump queue", - "operationId": "healthGetQueueUsageDump", + "operationId": "healthGetQueueStatsUsageDump", "consumes": [ "application\/json" ], @@ -12722,13 +12732,13 @@ } }, "x-appwrite": { - "method": "getQueueUsageDump", + "method": "getQueueStatsUsageDump", "weight": 144, "cookies": false, "type": "", "deprecated": false, - "demo": "health\/get-queue-usage-dump.md", - "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-usage-dump.md", + "demo": "health\/get-queue-stats-usage-dump.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-stats-usage-dump.md", "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", @@ -12784,7 +12794,7 @@ }, "x-appwrite": { "method": "getQueueWebhooks", - "weight": 132, + "weight": 131, "cookies": false, "type": "", "deprecated": false, @@ -12945,7 +12955,7 @@ }, "x-appwrite": { "method": "getTime", - "weight": 131, + "weight": 130, "cookies": false, "type": "", "deprecated": false, @@ -18070,19 +18080,19 @@ }, "endpoint": { "type": "string", - "description": "Source's Appwrite Endpoint", + "description": "Source Appwrite endpoint", "default": null, "x-example": "https:\/\/example.com" }, "projectId": { "type": "string", - "description": "Source's Project ID", + "description": "Source Project ID", "default": null, "x-example": "" }, "apiKey": { "type": "string", - "description": "Source's API Key", + "description": "Source API Key", "default": null, "x-example": "" } @@ -36608,6 +36618,21 @@ "$ref": "#\/definitions\/metric" }, "x-example": [] + }, + "imageTransformations": { + "type": "array", + "description": "Aggregated number of files transformations per period.", + "items": { + "type": "object", + "$ref": "#\/definitions\/metric" + }, + "x-example": [] + }, + "imageTransformationsTotal": { + "type": "integer", + "description": "Total aggregated number of files transformations.", + "x-example": 0, + "format": "int32" } }, "required": [ @@ -36615,7 +36640,9 @@ "filesTotal", "filesStorageTotal", "files", - "storage" + "storage", + "imageTransformations", + "imageTransformationsTotal" ] }, "usageFunctions": { @@ -37185,6 +37212,21 @@ "$ref": "#\/definitions\/metric" }, "x-example": [] + }, + "imageTransformations": { + "type": "array", + "description": "An array of aggregated number of image transformations.", + "items": { + "type": "object", + "$ref": "#\/definitions\/metric" + }, + "x-example": [] + }, + "imageTransformationsTotal": { + "type": "integer", + "description": "Total aggregated number of image transformations.", + "x-example": 0, + "format": "int32" } }, "required": [ @@ -37216,7 +37258,9 @@ "authPhoneEstimate", "authPhoneCountryBreakdown", "databasesReads", - "databasesWrites" + "databasesWrites", + "imageTransformations", + "imageTransformationsTotal" ] }, "headers": { diff --git a/app/config/specs/swagger2-1.6.x-server.json b/app/config/specs/swagger2-1.6.x-server.json index e38495629c..83757c94f4 100644 --- a/app/config/specs/swagger2-1.6.x-server.json +++ b/app/config/specs/swagger2-1.6.x-server.json @@ -3261,7 +3261,7 @@ "parameters": [ { "name": "code", - "description": "Credit Card Code. Possible values: amex, argencard, cabal, cencosud, diners, discover, elo, hipercard, jcb, mastercard, naranja, targeta-shopping, union-china-pay, visa, mir, maestro.", + "description": "Credit Card Code. Possible values: amex, argencard, cabal, cencosud, diners, discover, elo, hipercard, jcb, mastercard, naranja, targeta-shopping, union-china-pay, visa, mir, maestro, rupay.", "required": true, "type": "string", "x-example": "amex", @@ -3281,7 +3281,8 @@ "union-china-pay", "visa", "mir", - "maestro" + "maestro", + "rupay" ], "x-enum-name": "CreditCard", "x-enum-keys": [ @@ -3300,7 +3301,8 @@ "Union China Pay", "Visa", "MIR", - "Maestro" + "Maestro", + "Rupay" ], "in": "path" }, @@ -6133,8 +6135,6 @@ }, "required": [ "required", - "min", - "max", "default" ] } @@ -6373,8 +6373,6 @@ }, "required": [ "required", - "min", - "max", "default" ] } @@ -7552,7 +7550,7 @@ "tags": [ "databases" ], - "description": "Create a new Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console.", + "description": "Create a new Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console.\n", "responses": { "201": { "description": "Document", @@ -10689,7 +10687,7 @@ }, "x-appwrite": { "method": "getCertificate", - "weight": 134, + "weight": 133, "cookies": false, "type": "", "deprecated": false, @@ -10800,7 +10798,7 @@ }, "x-appwrite": { "method": "getPubSub", - "weight": 130, + "weight": 129, "cookies": false, "type": "", "deprecated": false, @@ -10827,57 +10825,6 @@ ] } }, - "\/health\/queue": { - "get": { - "summary": "Get queue", - "operationId": "healthGetQueue", - "consumes": [ - "application\/json" - ], - "produces": [ - "application\/json" - ], - "tags": [ - "health" - ], - "description": "Check the Appwrite queue messaging servers are up and connection is successful.", - "responses": { - "200": { - "description": "Health Status", - "schema": { - "$ref": "#\/definitions\/healthStatus" - } - } - }, - "x-appwrite": { - "method": "getQueue", - "weight": 129, - "cookies": false, - "type": "", - "deprecated": false, - "demo": "health\/get-queue.md", - "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue.md", - "rate-limit": 0, - "rate-time": 3600, - "rate-key": "url:{url},ip:{ip}", - "scope": "health.read", - "platforms": [ - "server" - ], - "packaging": false, - "auth": { - "Project": [], - "Key": [] - } - }, - "security": [ - { - "Project": [], - "Key": [] - } - ] - } - }, "\/health\/queue\/builds": { "get": { "summary": "Get builds queue", @@ -10902,7 +10849,7 @@ }, "x-appwrite": { "method": "getQueueBuilds", - "weight": 136, + "weight": 135, "cookies": false, "type": "", "deprecated": false, @@ -10964,7 +10911,7 @@ }, "x-appwrite": { "method": "getQueueCertificates", - "weight": 135, + "weight": 134, "cookies": false, "type": "", "deprecated": false, @@ -11026,7 +10973,7 @@ }, "x-appwrite": { "method": "getQueueDatabases", - "weight": 137, + "weight": 136, "cookies": false, "type": "", "deprecated": false, @@ -11097,7 +11044,7 @@ }, "x-appwrite": { "method": "getQueueDeletes", - "weight": 138, + "weight": 137, "cookies": false, "type": "", "deprecated": false, @@ -11197,8 +11144,9 @@ "v1-audits", "v1-mails", "v1-functions", - "v1-usage", - "v1-usage-dump", + "v1-stats-resources", + "v1-stats-usage", + "v1-stats-usage-dump", "v1-webhooks", "v1-certificates", "v1-builds", @@ -11245,7 +11193,7 @@ }, "x-appwrite": { "method": "getQueueFunctions", - "weight": 142, + "weight": 141, "cookies": false, "type": "", "deprecated": false, @@ -11307,7 +11255,7 @@ }, "x-appwrite": { "method": "getQueueLogs", - "weight": 133, + "weight": 132, "cookies": false, "type": "", "deprecated": false, @@ -11369,7 +11317,7 @@ }, "x-appwrite": { "method": "getQueueMails", - "weight": 139, + "weight": 138, "cookies": false, "type": "", "deprecated": false, @@ -11431,7 +11379,7 @@ }, "x-appwrite": { "method": "getQueueMessaging", - "weight": 140, + "weight": 139, "cookies": false, "type": "", "deprecated": false, @@ -11493,7 +11441,7 @@ }, "x-appwrite": { "method": "getQueueMigrations", - "weight": 141, + "weight": 140, "cookies": false, "type": "", "deprecated": false, @@ -11531,10 +11479,10 @@ ] } }, - "\/health\/queue\/usage": { + "\/health\/queue\/stats-resources": { "get": { - "summary": "Get usage queue", - "operationId": "healthGetQueueUsage", + "summary": "Get stats resources queue", + "operationId": "healthGetQueueStatsResources", "consumes": [ "application\/json" ], @@ -11544,7 +11492,7 @@ "tags": [ "health" ], - "description": "Get the number of metrics that are waiting to be processed in the Appwrite internal queue server.", + "description": "Get the number of metrics that are waiting to be processed in the Appwrite stats resources queue.", "responses": { "200": { "description": "Health Queue", @@ -11554,13 +11502,13 @@ } }, "x-appwrite": { - "method": "getQueueUsage", - "weight": 143, + "method": "getQueueStatsResources", + "weight": 142, "cookies": false, "type": "", "deprecated": false, - "demo": "health\/get-queue-usage.md", - "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-usage.md", + "demo": "health\/get-queue-stats-resources.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-stats-resources.md", "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", @@ -11593,10 +11541,72 @@ ] } }, - "\/health\/queue\/usage-dump": { + "\/health\/queue\/stats-usage": { + "get": { + "summary": "Get stats usage queue", + "operationId": "healthGetQueueUsage", + "consumes": [ + "application\/json" + ], + "produces": [ + "application\/json" + ], + "tags": [ + "health" + ], + "description": "Get the number of metrics that are waiting to be processed in the Appwrite internal queue server.", + "responses": { + "200": { + "description": "Health Queue", + "schema": { + "$ref": "#\/definitions\/healthQueue" + } + } + }, + "x-appwrite": { + "method": "getQueueUsage", + "weight": 143, + "cookies": false, + "type": "", + "deprecated": false, + "demo": "health\/get-queue-usage.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-stats-usage.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "health.read", + "platforms": [ + "server" + ], + "packaging": false, + "auth": { + "Project": [], + "Key": [] + } + }, + "security": [ + { + "Project": [], + "Key": [] + } + ], + "parameters": [ + { + "name": "threshold", + "description": "Queue size threshold. When hit (equal or higher), endpoint returns server error. Default value is 5000.", + "required": false, + "type": "integer", + "format": "int32", + "default": 5000, + "in": "query" + } + ] + } + }, + "\/health\/queue\/stats-usage-dump": { "get": { "summary": "Get usage dump queue", - "operationId": "healthGetQueueUsageDump", + "operationId": "healthGetQueueStatsUsageDump", "consumes": [ "application\/json" ], @@ -11616,13 +11626,13 @@ } }, "x-appwrite": { - "method": "getQueueUsageDump", + "method": "getQueueStatsUsageDump", "weight": 144, "cookies": false, "type": "", "deprecated": false, - "demo": "health\/get-queue-usage-dump.md", - "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-usage-dump.md", + "demo": "health\/get-queue-stats-usage-dump.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-stats-usage-dump.md", "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", @@ -11679,7 +11689,7 @@ }, "x-appwrite": { "method": "getQueueWebhooks", - "weight": 132, + "weight": 131, "cookies": false, "type": "", "deprecated": false, @@ -11843,7 +11853,7 @@ }, "x-appwrite": { "method": "getTime", - "weight": 131, + "weight": 130, "cookies": false, "type": "", "deprecated": false, diff --git a/app/config/specs/swagger2-latest-console.json b/app/config/specs/swagger2-latest-console.json index 2dddc72802..8fc7e7daf3 100644 --- a/app/config/specs/swagger2-latest-console.json +++ b/app/config/specs/swagger2-latest-console.json @@ -6609,8 +6609,6 @@ }, "required": [ "required", - "min", - "max", "default" ] } @@ -6847,8 +6845,6 @@ }, "required": [ "required", - "min", - "max", "default" ] } @@ -37217,13 +37213,16 @@ }, "x-example": [] }, - "imageTransformationsTotal": { - "type": "integer", - "description": "An array of aggregated number of image transformations.", - "x-example": 0, - "format": "int32" - }, "imageTransformations": { + "type": "array", + "description": "An array of aggregated number of image transformations.", + "items": { + "type": "object", + "$ref": "#\/definitions\/metric" + }, + "x-example": [] + }, + "imageTransformationsTotal": { "type": "integer", "description": "Total aggregated number of image transformations.", "x-example": 0, @@ -37260,8 +37259,8 @@ "authPhoneCountryBreakdown", "databasesReads", "databasesWrites", - "imageTransformationsTotal", - "imageTransformations" + "imageTransformations", + "imageTransformationsTotal" ] }, "headers": { diff --git a/app/config/specs/swagger2-latest-server.json b/app/config/specs/swagger2-latest-server.json index 749f87553b..83757c94f4 100644 --- a/app/config/specs/swagger2-latest-server.json +++ b/app/config/specs/swagger2-latest-server.json @@ -6135,8 +6135,6 @@ }, "required": [ "required", - "min", - "max", "default" ] } @@ -6375,8 +6373,6 @@ }, "required": [ "required", - "min", - "max", "default" ] } From 301471354ee985b698f201f613be975e1d55378c Mon Sep 17 00:00:00 2001 From: Steven Nguyen Date: Wed, 12 Mar 2025 15:45:40 -0700 Subject: [PATCH 117/143] chore: regenerated 1.6.x examples To ensure we didn't have anything extra, I deleted the 1.6.x folder and then regenerated the examples via the sdk command. --- .../java/functions/get-deployment-download.md | 23 ----------- .../java/functions/get-template.md | 22 ---------- .../java/functions/list-templates.md | 25 ----------- .../functions/get-deployment-download.md | 14 ------- .../kotlin/functions/get-template.md | 13 ------ .../kotlin/functions/list-templates.md | 16 -------- .../examples/account/update-mfa-challenge.md | 2 +- .../functions/get-deployment-download.md | 13 ------ .../examples/functions/get-template.md | 12 ------ .../examples/functions/list-templates.md | 15 ------- .../examples/account/update-mfa-challenge.md | 2 +- .../functions/get-deployment-download.md | 29 ------------- .../examples/functions/get-template.md | 11 ----- .../examples/functions/list-templates.md | 14 ------- .../examples/account/update-mfa-challenge.md | 30 +++++++++++++- .../functions/get-deployment-download.md | 8 ---- .../examples/functions/get-template.md | 35 ---------------- .../examples/functions/list-templates.md | 41 ------------------- .../functions/get-deployment-download.md | 14 ------- .../examples/functions/get-template.md | 13 ------ .../examples/functions/list-templates.md | 16 -------- .../functions/get-deployment-download.md | 8 ---- .../examples/functions/get-template.md | 6 --- .../examples/functions/list-templates.md | 6 --- .../functions/get-deployment-download.md | 14 ------- .../examples/functions/get-template.md | 13 ------ .../examples/functions/list-templates.md | 16 -------- .../examples/functions/download-deployment.md | 3 -- .../examples/functions/get-specifications.md | 1 - .../create-firebase-o-auth-migration.md | 3 -- .../migrations/delete-firebase-auth.md | 1 - .../migrations/get-firebase-report-o-auth.md | 3 -- .../migrations/list-firebase-projects.md | 1 - .../examples/functions/download-deployment.md | 14 ------- .../examples/functions/get-specifications.md | 11 ----- .../create-firebase-o-auth-migration.md | 14 ------- .../migrations/delete-firebase-auth.md | 11 ----- .../migrations/get-firebase-report-o-auth.md | 14 ------- .../migrations/list-firebase-projects.md | 11 ----- .../examples/account/update-mfa-challenge.md | 2 +- .../examples/functions/download-deployment.md | 13 ------ .../examples/functions/get-template.md | 11 ----- .../examples/functions/list-templates.md | 14 ------- .../examples/functions/download-deployment.md | 13 ------ .../examples/functions/get-template.md | 11 ----- .../examples/functions/list-templates.md | 14 ------- .../examples/account/update-mfa-challenge.md | 2 +- .../examples/functions/download-deployment.md | 15 ------- .../examples/functions/get-template.md | 13 ------ .../examples/functions/list-templates.md | 16 -------- .../examples/functions/download-deployment.md | 27 ------------ .../examples/functions/get-template.md | 25 ----------- .../examples/functions/list-templates.md | 28 ------------- .../examples/account/update-mfa-challenge.md | 30 +++++++++++++- .../examples/functions/download-deployment.md | 8 ---- .../examples/functions/get-template.md | 35 ---------------- .../examples/functions/list-templates.md | 41 ------------------- .../users/delete-mfa-authenticator.md | 30 -------------- .../java/functions/download-deployment.md | 24 ----------- .../java/functions/get-template.md | 22 ---------- .../java/functions/list-templates.md | 25 ----------- .../kotlin/functions/download-deployment.md | 15 ------- .../kotlin/functions/get-template.md | 13 ------ .../kotlin/functions/list-templates.md | 16 -------- .../examples/functions/download-deployment.md | 13 ------ .../examples/functions/get-template.md | 11 ----- .../examples/functions/list-templates.md | 14 ------- .../examples/functions/download-deployment.md | 16 -------- .../examples/functions/get-template.md | 14 ------- .../examples/functions/list-templates.md | 17 -------- .../examples/functions/download-deployment.md | 13 ------ .../examples/functions/get-template.md | 11 ----- .../examples/functions/list-templates.md | 14 ------- .../examples/functions/download-deployment.md | 7 ---- .../examples/functions/get-template.md | 6 --- .../examples/functions/list-templates.md | 6 --- .../examples/functions/download-deployment.md | 15 ------- .../examples/functions/get-template.md | 13 ------ .../examples/functions/list-templates.md | 16 -------- .../examples/account/update-mfa-challenge.md | 2 +- .../examples/functions/download-deployment.md | 14 ------- .../examples/functions/get-template.md | 12 ------ .../examples/functions/list-templates.md | 15 ------- .../users/delete-mfa-authenticator.md | 2 +- 84 files changed, 64 insertions(+), 1148 deletions(-) delete mode 100644 docs/examples/1.6.x/client-android/java/functions/get-deployment-download.md delete mode 100644 docs/examples/1.6.x/client-android/java/functions/get-template.md delete mode 100644 docs/examples/1.6.x/client-android/java/functions/list-templates.md delete mode 100644 docs/examples/1.6.x/client-android/kotlin/functions/get-deployment-download.md delete mode 100644 docs/examples/1.6.x/client-android/kotlin/functions/get-template.md delete mode 100644 docs/examples/1.6.x/client-android/kotlin/functions/list-templates.md delete mode 100644 docs/examples/1.6.x/client-apple/examples/functions/get-deployment-download.md delete mode 100644 docs/examples/1.6.x/client-apple/examples/functions/get-template.md delete mode 100644 docs/examples/1.6.x/client-apple/examples/functions/list-templates.md delete mode 100644 docs/examples/1.6.x/client-flutter/examples/functions/get-deployment-download.md delete mode 100644 docs/examples/1.6.x/client-flutter/examples/functions/get-template.md delete mode 100644 docs/examples/1.6.x/client-flutter/examples/functions/list-templates.md delete mode 100644 docs/examples/1.6.x/client-graphql/examples/functions/get-deployment-download.md delete mode 100644 docs/examples/1.6.x/client-graphql/examples/functions/get-template.md delete mode 100644 docs/examples/1.6.x/client-graphql/examples/functions/list-templates.md delete mode 100644 docs/examples/1.6.x/client-react-native/examples/functions/get-deployment-download.md delete mode 100644 docs/examples/1.6.x/client-react-native/examples/functions/get-template.md delete mode 100644 docs/examples/1.6.x/client-react-native/examples/functions/list-templates.md delete mode 100644 docs/examples/1.6.x/client-rest/examples/functions/get-deployment-download.md delete mode 100644 docs/examples/1.6.x/client-rest/examples/functions/get-template.md delete mode 100644 docs/examples/1.6.x/client-rest/examples/functions/list-templates.md delete mode 100644 docs/examples/1.6.x/client-web/examples/functions/get-deployment-download.md delete mode 100644 docs/examples/1.6.x/client-web/examples/functions/get-template.md delete mode 100644 docs/examples/1.6.x/client-web/examples/functions/list-templates.md delete mode 100644 docs/examples/1.6.x/console-cli/examples/functions/download-deployment.md delete mode 100644 docs/examples/1.6.x/console-cli/examples/functions/get-specifications.md delete mode 100644 docs/examples/1.6.x/console-cli/examples/migrations/create-firebase-o-auth-migration.md delete mode 100644 docs/examples/1.6.x/console-cli/examples/migrations/delete-firebase-auth.md delete mode 100644 docs/examples/1.6.x/console-cli/examples/migrations/get-firebase-report-o-auth.md delete mode 100644 docs/examples/1.6.x/console-cli/examples/migrations/list-firebase-projects.md delete mode 100644 docs/examples/1.6.x/console-web/examples/functions/download-deployment.md delete mode 100644 docs/examples/1.6.x/console-web/examples/functions/get-specifications.md delete mode 100644 docs/examples/1.6.x/console-web/examples/migrations/create-firebase-o-auth-migration.md delete mode 100644 docs/examples/1.6.x/console-web/examples/migrations/delete-firebase-auth.md delete mode 100644 docs/examples/1.6.x/console-web/examples/migrations/get-firebase-report-o-auth.md delete mode 100644 docs/examples/1.6.x/console-web/examples/migrations/list-firebase-projects.md delete mode 100644 docs/examples/1.6.x/server-dart/examples/functions/download-deployment.md delete mode 100644 docs/examples/1.6.x/server-dart/examples/functions/get-template.md delete mode 100644 docs/examples/1.6.x/server-dart/examples/functions/list-templates.md delete mode 100644 docs/examples/1.6.x/server-deno/examples/functions/download-deployment.md delete mode 100644 docs/examples/1.6.x/server-deno/examples/functions/get-template.md delete mode 100644 docs/examples/1.6.x/server-deno/examples/functions/list-templates.md delete mode 100644 docs/examples/1.6.x/server-dotnet/examples/functions/download-deployment.md delete mode 100644 docs/examples/1.6.x/server-dotnet/examples/functions/get-template.md delete mode 100644 docs/examples/1.6.x/server-dotnet/examples/functions/list-templates.md delete mode 100644 docs/examples/1.6.x/server-go/examples/functions/download-deployment.md delete mode 100644 docs/examples/1.6.x/server-go/examples/functions/get-template.md delete mode 100644 docs/examples/1.6.x/server-go/examples/functions/list-templates.md delete mode 100644 docs/examples/1.6.x/server-graphql/examples/functions/download-deployment.md delete mode 100644 docs/examples/1.6.x/server-graphql/examples/functions/get-template.md delete mode 100644 docs/examples/1.6.x/server-graphql/examples/functions/list-templates.md delete mode 100644 docs/examples/1.6.x/server-kotlin/java/functions/download-deployment.md delete mode 100644 docs/examples/1.6.x/server-kotlin/java/functions/get-template.md delete mode 100644 docs/examples/1.6.x/server-kotlin/java/functions/list-templates.md delete mode 100644 docs/examples/1.6.x/server-kotlin/kotlin/functions/download-deployment.md delete mode 100644 docs/examples/1.6.x/server-kotlin/kotlin/functions/get-template.md delete mode 100644 docs/examples/1.6.x/server-kotlin/kotlin/functions/list-templates.md delete mode 100644 docs/examples/1.6.x/server-nodejs/examples/functions/download-deployment.md delete mode 100644 docs/examples/1.6.x/server-nodejs/examples/functions/get-template.md delete mode 100644 docs/examples/1.6.x/server-nodejs/examples/functions/list-templates.md delete mode 100644 docs/examples/1.6.x/server-php/examples/functions/download-deployment.md delete mode 100644 docs/examples/1.6.x/server-php/examples/functions/get-template.md delete mode 100644 docs/examples/1.6.x/server-php/examples/functions/list-templates.md delete mode 100644 docs/examples/1.6.x/server-python/examples/functions/download-deployment.md delete mode 100644 docs/examples/1.6.x/server-python/examples/functions/get-template.md delete mode 100644 docs/examples/1.6.x/server-python/examples/functions/list-templates.md delete mode 100644 docs/examples/1.6.x/server-rest/examples/functions/download-deployment.md delete mode 100644 docs/examples/1.6.x/server-rest/examples/functions/get-template.md delete mode 100644 docs/examples/1.6.x/server-rest/examples/functions/list-templates.md delete mode 100644 docs/examples/1.6.x/server-ruby/examples/functions/download-deployment.md delete mode 100644 docs/examples/1.6.x/server-ruby/examples/functions/get-template.md delete mode 100644 docs/examples/1.6.x/server-ruby/examples/functions/list-templates.md delete mode 100644 docs/examples/1.6.x/server-swift/examples/functions/download-deployment.md delete mode 100644 docs/examples/1.6.x/server-swift/examples/functions/get-template.md delete mode 100644 docs/examples/1.6.x/server-swift/examples/functions/list-templates.md diff --git a/docs/examples/1.6.x/client-android/java/functions/get-deployment-download.md b/docs/examples/1.6.x/client-android/java/functions/get-deployment-download.md deleted file mode 100644 index 200cdf2fc5..0000000000 --- a/docs/examples/1.6.x/client-android/java/functions/get-deployment-download.md +++ /dev/null @@ -1,23 +0,0 @@ -import io.appwrite.Client; -import io.appwrite.coroutines.CoroutineCallback; -import io.appwrite.services.Functions; - -Client client = new Client(context) - .setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint - .setProject(""); // Your project ID - -Functions functions = new Functions(client); - -functions.getDeploymentDownload( - "", // functionId - "", // deploymentId - new CoroutineCallback<>((result, error) -> { - if (error != null) { - error.printStackTrace(); - return; - } - - Log.d("Appwrite", result.toString()); - }) -); - diff --git a/docs/examples/1.6.x/client-android/java/functions/get-template.md b/docs/examples/1.6.x/client-android/java/functions/get-template.md deleted file mode 100644 index af9efc7b92..0000000000 --- a/docs/examples/1.6.x/client-android/java/functions/get-template.md +++ /dev/null @@ -1,22 +0,0 @@ -import io.appwrite.Client; -import io.appwrite.coroutines.CoroutineCallback; -import io.appwrite.services.Functions; - -Client client = new Client(context) - .setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint - .setProject(""); // Your project ID - -Functions functions = new Functions(client); - -functions.getTemplate( - "", // templateId - new CoroutineCallback<>((result, error) -> { - if (error != null) { - error.printStackTrace(); - return; - } - - Log.d("Appwrite", result.toString()); - }) -); - diff --git a/docs/examples/1.6.x/client-android/java/functions/list-templates.md b/docs/examples/1.6.x/client-android/java/functions/list-templates.md deleted file mode 100644 index 6c051f3b04..0000000000 --- a/docs/examples/1.6.x/client-android/java/functions/list-templates.md +++ /dev/null @@ -1,25 +0,0 @@ -import io.appwrite.Client; -import io.appwrite.coroutines.CoroutineCallback; -import io.appwrite.services.Functions; - -Client client = new Client(context) - .setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint - .setProject(""); // Your project ID - -Functions functions = new Functions(client); - -functions.listTemplates( - listOf(), // runtimes (optional) - listOf(), // useCases (optional) - 1, // limit (optional) - 0, // offset (optional) - new CoroutineCallback<>((result, error) -> { - if (error != null) { - error.printStackTrace(); - return; - } - - Log.d("Appwrite", result.toString()); - }) -); - diff --git a/docs/examples/1.6.x/client-android/kotlin/functions/get-deployment-download.md b/docs/examples/1.6.x/client-android/kotlin/functions/get-deployment-download.md deleted file mode 100644 index 21e99cb731..0000000000 --- a/docs/examples/1.6.x/client-android/kotlin/functions/get-deployment-download.md +++ /dev/null @@ -1,14 +0,0 @@ -import io.appwrite.Client -import io.appwrite.coroutines.CoroutineCallback -import io.appwrite.services.Functions - -val client = Client(context) - .setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint - .setProject("") // Your project ID - -val functions = Functions(client) - -val result = functions.getDeploymentDownload( - functionId = "", - deploymentId = "", -) \ No newline at end of file diff --git a/docs/examples/1.6.x/client-android/kotlin/functions/get-template.md b/docs/examples/1.6.x/client-android/kotlin/functions/get-template.md deleted file mode 100644 index 4af83bda3e..0000000000 --- a/docs/examples/1.6.x/client-android/kotlin/functions/get-template.md +++ /dev/null @@ -1,13 +0,0 @@ -import io.appwrite.Client -import io.appwrite.coroutines.CoroutineCallback -import io.appwrite.services.Functions - -val client = Client(context) - .setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint - .setProject("") // Your project ID - -val functions = Functions(client) - -val result = functions.getTemplate( - templateId = "", -) \ No newline at end of file diff --git a/docs/examples/1.6.x/client-android/kotlin/functions/list-templates.md b/docs/examples/1.6.x/client-android/kotlin/functions/list-templates.md deleted file mode 100644 index a1a59d9438..0000000000 --- a/docs/examples/1.6.x/client-android/kotlin/functions/list-templates.md +++ /dev/null @@ -1,16 +0,0 @@ -import io.appwrite.Client -import io.appwrite.coroutines.CoroutineCallback -import io.appwrite.services.Functions - -val client = Client(context) - .setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint - .setProject("") // Your project ID - -val functions = Functions(client) - -val result = functions.listTemplates( - runtimes = listOf(), // (optional) - useCases = listOf(), // (optional) - limit = 1, // (optional) - offset = 0, // (optional) -) \ No newline at end of file diff --git a/docs/examples/1.6.x/client-apple/examples/account/update-mfa-challenge.md b/docs/examples/1.6.x/client-apple/examples/account/update-mfa-challenge.md index a237537ae3..1c5874f784 100644 --- a/docs/examples/1.6.x/client-apple/examples/account/update-mfa-challenge.md +++ b/docs/examples/1.6.x/client-apple/examples/account/update-mfa-challenge.md @@ -6,7 +6,7 @@ let client = Client() let account = Account(client) -let result = try await account.updateMfaChallenge( +let session = try await account.updateMfaChallenge( challengeId: "", otp: "" ) diff --git a/docs/examples/1.6.x/client-apple/examples/functions/get-deployment-download.md b/docs/examples/1.6.x/client-apple/examples/functions/get-deployment-download.md deleted file mode 100644 index 0e6659969c..0000000000 --- a/docs/examples/1.6.x/client-apple/examples/functions/get-deployment-download.md +++ /dev/null @@ -1,13 +0,0 @@ -import Appwrite - -let client = Client() - .setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint - .setProject("") // Your project ID - -let functions = Functions(client) - -let bytes = try await functions.getDeploymentDownload( - functionId: "", - deploymentId: "" -) - diff --git a/docs/examples/1.6.x/client-apple/examples/functions/get-template.md b/docs/examples/1.6.x/client-apple/examples/functions/get-template.md deleted file mode 100644 index bc7a9a3aef..0000000000 --- a/docs/examples/1.6.x/client-apple/examples/functions/get-template.md +++ /dev/null @@ -1,12 +0,0 @@ -import Appwrite - -let client = Client() - .setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint - .setProject("") // Your project ID - -let functions = Functions(client) - -let templateFunction = try await functions.getTemplate( - templateId: "" -) - diff --git a/docs/examples/1.6.x/client-apple/examples/functions/list-templates.md b/docs/examples/1.6.x/client-apple/examples/functions/list-templates.md deleted file mode 100644 index d0090ab803..0000000000 --- a/docs/examples/1.6.x/client-apple/examples/functions/list-templates.md +++ /dev/null @@ -1,15 +0,0 @@ -import Appwrite - -let client = Client() - .setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint - .setProject("") // Your project ID - -let functions = Functions(client) - -let templateFunctionList = try await functions.listTemplates( - runtimes: [], // optional - useCases: [], // optional - limit: 1, // optional - offset: 0 // optional -) - diff --git a/docs/examples/1.6.x/client-flutter/examples/account/update-mfa-challenge.md b/docs/examples/1.6.x/client-flutter/examples/account/update-mfa-challenge.md index c8e1af7e90..bbe7c03470 100644 --- a/docs/examples/1.6.x/client-flutter/examples/account/update-mfa-challenge.md +++ b/docs/examples/1.6.x/client-flutter/examples/account/update-mfa-challenge.md @@ -6,7 +6,7 @@ Client client = Client() Account account = Account(client); - result = await account.updateMfaChallenge( +Session result = await account.updateMfaChallenge( challengeId: '', otp: '', ); diff --git a/docs/examples/1.6.x/client-flutter/examples/functions/get-deployment-download.md b/docs/examples/1.6.x/client-flutter/examples/functions/get-deployment-download.md deleted file mode 100644 index bb0ee903df..0000000000 --- a/docs/examples/1.6.x/client-flutter/examples/functions/get-deployment-download.md +++ /dev/null @@ -1,29 +0,0 @@ -import 'package:appwrite/appwrite.dart'; - -Client client = Client() - .setEndpoint('https://cloud.appwrite.io/v1') // Your API Endpoint - .setProject(''); // Your project ID - -Functions functions = Functions(client); - -// Downloading file -UInt8List bytes = await functions.getDeploymentDownload( - functionId: '', - deploymentId: '', -) - -final file = File('path_to_file/filename.ext'); -file.writeAsBytesSync(bytes); - -// Displaying image preview -FutureBuilder( - future: functions.getDeploymentDownload( - functionId:'' , - deploymentId:'' , -), // Works for both public file and private file, for private files you need to be logged in - builder: (context, snapshot) { - return snapshot.hasData && snapshot.data != null - ? Image.memory(snapshot.data) - : CircularProgressIndicator(); - } -); diff --git a/docs/examples/1.6.x/client-flutter/examples/functions/get-template.md b/docs/examples/1.6.x/client-flutter/examples/functions/get-template.md deleted file mode 100644 index 560a4d94ed..0000000000 --- a/docs/examples/1.6.x/client-flutter/examples/functions/get-template.md +++ /dev/null @@ -1,11 +0,0 @@ -import 'package:appwrite/appwrite.dart'; - -Client client = Client() - .setEndpoint('https://cloud.appwrite.io/v1') // Your API Endpoint - .setProject(''); // Your project ID - -Functions functions = Functions(client); - -TemplateFunction result = await functions.getTemplate( - templateId: '', -); diff --git a/docs/examples/1.6.x/client-flutter/examples/functions/list-templates.md b/docs/examples/1.6.x/client-flutter/examples/functions/list-templates.md deleted file mode 100644 index d3d8c7aa4c..0000000000 --- a/docs/examples/1.6.x/client-flutter/examples/functions/list-templates.md +++ /dev/null @@ -1,14 +0,0 @@ -import 'package:appwrite/appwrite.dart'; - -Client client = Client() - .setEndpoint('https://cloud.appwrite.io/v1') // Your API Endpoint - .setProject(''); // Your project ID - -Functions functions = Functions(client); - -TemplateFunctionList result = await functions.listTemplates( - runtimes: [], // optional - useCases: [], // optional - limit: 1, // optional - offset: 0, // optional -); diff --git a/docs/examples/1.6.x/client-graphql/examples/account/update-mfa-challenge.md b/docs/examples/1.6.x/client-graphql/examples/account/update-mfa-challenge.md index 8237431bcf..0bcec2157f 100644 --- a/docs/examples/1.6.x/client-graphql/examples/account/update-mfa-challenge.md +++ b/docs/examples/1.6.x/client-graphql/examples/account/update-mfa-challenge.md @@ -3,6 +3,34 @@ mutation { challengeId: "", otp: "" ) { - status + _id + _createdAt + _updatedAt + userId + expire + provider + providerUid + providerAccessToken + providerAccessTokenExpiry + providerRefreshToken + ip + osCode + osName + osVersion + clientType + clientCode + clientName + clientVersion + clientEngine + clientEngineVersion + deviceName + deviceBrand + deviceModel + countryCode + countryName + current + factors + secret + mfaUpdatedAt } } diff --git a/docs/examples/1.6.x/client-graphql/examples/functions/get-deployment-download.md b/docs/examples/1.6.x/client-graphql/examples/functions/get-deployment-download.md deleted file mode 100644 index 396047bb94..0000000000 --- a/docs/examples/1.6.x/client-graphql/examples/functions/get-deployment-download.md +++ /dev/null @@ -1,8 +0,0 @@ -query { - functionsGetDeploymentDownload( - functionId: "", - deploymentId: "" - ) { - status - } -} diff --git a/docs/examples/1.6.x/client-graphql/examples/functions/get-template.md b/docs/examples/1.6.x/client-graphql/examples/functions/get-template.md deleted file mode 100644 index 44a466de27..0000000000 --- a/docs/examples/1.6.x/client-graphql/examples/functions/get-template.md +++ /dev/null @@ -1,35 +0,0 @@ -query { - functionsGetTemplate( - templateId: "" - ) { - icon - id - name - tagline - permissions - events - cron - timeout - useCases - runtimes { - name - commands - entrypoint - providerRootDirectory - } - instructions - vcsProvider - providerRepositoryId - providerOwner - providerVersion - variables { - name - description - value - placeholder - required - type - } - scopes - } -} diff --git a/docs/examples/1.6.x/client-graphql/examples/functions/list-templates.md b/docs/examples/1.6.x/client-graphql/examples/functions/list-templates.md deleted file mode 100644 index cb289ddabb..0000000000 --- a/docs/examples/1.6.x/client-graphql/examples/functions/list-templates.md +++ /dev/null @@ -1,41 +0,0 @@ -query { - functionsListTemplates( - runtimes: [], - useCases: [], - limit: 1, - offset: 0 - ) { - total - templates { - icon - id - name - tagline - permissions - events - cron - timeout - useCases - runtimes { - name - commands - entrypoint - providerRootDirectory - } - instructions - vcsProvider - providerRepositoryId - providerOwner - providerVersion - variables { - name - description - value - placeholder - required - type - } - scopes - } - } -} diff --git a/docs/examples/1.6.x/client-react-native/examples/functions/get-deployment-download.md b/docs/examples/1.6.x/client-react-native/examples/functions/get-deployment-download.md deleted file mode 100644 index 7eac2b753f..0000000000 --- a/docs/examples/1.6.x/client-react-native/examples/functions/get-deployment-download.md +++ /dev/null @@ -1,14 +0,0 @@ -import { Client, Functions } from "react-native-appwrite"; - -const client = new Client() - .setEndpoint('https://cloud.appwrite.io/v1') // Your API Endpoint - .setProject(''); // Your project ID - -const functions = new Functions(client); - -const result = functions.getDeploymentDownload( - '', // functionId - '' // deploymentId -); - -console.log(result); diff --git a/docs/examples/1.6.x/client-react-native/examples/functions/get-template.md b/docs/examples/1.6.x/client-react-native/examples/functions/get-template.md deleted file mode 100644 index ea756323dc..0000000000 --- a/docs/examples/1.6.x/client-react-native/examples/functions/get-template.md +++ /dev/null @@ -1,13 +0,0 @@ -import { Client, Functions } from "react-native-appwrite"; - -const client = new Client() - .setEndpoint('https://cloud.appwrite.io/v1') // Your API Endpoint - .setProject(''); // Your project ID - -const functions = new Functions(client); - -const result = await functions.getTemplate( - '' // templateId -); - -console.log(result); diff --git a/docs/examples/1.6.x/client-react-native/examples/functions/list-templates.md b/docs/examples/1.6.x/client-react-native/examples/functions/list-templates.md deleted file mode 100644 index 3811dd8e40..0000000000 --- a/docs/examples/1.6.x/client-react-native/examples/functions/list-templates.md +++ /dev/null @@ -1,16 +0,0 @@ -import { Client, Functions } from "react-native-appwrite"; - -const client = new Client() - .setEndpoint('https://cloud.appwrite.io/v1') // Your API Endpoint - .setProject(''); // Your project ID - -const functions = new Functions(client); - -const result = await functions.listTemplates( - [], // runtimes (optional) - [], // useCases (optional) - 1, // limit (optional) - 0 // offset (optional) -); - -console.log(result); diff --git a/docs/examples/1.6.x/client-rest/examples/functions/get-deployment-download.md b/docs/examples/1.6.x/client-rest/examples/functions/get-deployment-download.md deleted file mode 100644 index 0f6166b77f..0000000000 --- a/docs/examples/1.6.x/client-rest/examples/functions/get-deployment-download.md +++ /dev/null @@ -1,8 +0,0 @@ -GET /v1/functions/{functionId}/deployments/{deploymentId}/download HTTP/1.1 -Host: cloud.appwrite.io -Content-Type: application/json -X-Appwrite-Response-Format: 1.6.0 -X-Appwrite-Project: -X-Appwrite-Session: -X-Appwrite-JWT: - diff --git a/docs/examples/1.6.x/client-rest/examples/functions/get-template.md b/docs/examples/1.6.x/client-rest/examples/functions/get-template.md deleted file mode 100644 index d4e1f95cf5..0000000000 --- a/docs/examples/1.6.x/client-rest/examples/functions/get-template.md +++ /dev/null @@ -1,6 +0,0 @@ -GET /v1/functions/templates/{templateId} HTTP/1.1 -Host: cloud.appwrite.io -Content-Type: application/json -X-Appwrite-Response-Format: 1.6.0 -X-Appwrite-Project: - diff --git a/docs/examples/1.6.x/client-rest/examples/functions/list-templates.md b/docs/examples/1.6.x/client-rest/examples/functions/list-templates.md deleted file mode 100644 index b671bedebf..0000000000 --- a/docs/examples/1.6.x/client-rest/examples/functions/list-templates.md +++ /dev/null @@ -1,6 +0,0 @@ -GET /v1/functions/templates HTTP/1.1 -Host: cloud.appwrite.io -Content-Type: application/json -X-Appwrite-Response-Format: 1.6.0 -X-Appwrite-Project: - diff --git a/docs/examples/1.6.x/client-web/examples/functions/get-deployment-download.md b/docs/examples/1.6.x/client-web/examples/functions/get-deployment-download.md deleted file mode 100644 index 62e3ee6551..0000000000 --- a/docs/examples/1.6.x/client-web/examples/functions/get-deployment-download.md +++ /dev/null @@ -1,14 +0,0 @@ -import { Client, Functions } from "appwrite"; - -const client = new Client() - .setEndpoint('https://cloud.appwrite.io/v1') // Your API Endpoint - .setProject(''); // Your project ID - -const functions = new Functions(client); - -const result = functions.getDeploymentDownload( - '', // functionId - '' // deploymentId -); - -console.log(result); diff --git a/docs/examples/1.6.x/client-web/examples/functions/get-template.md b/docs/examples/1.6.x/client-web/examples/functions/get-template.md deleted file mode 100644 index dd2d20284e..0000000000 --- a/docs/examples/1.6.x/client-web/examples/functions/get-template.md +++ /dev/null @@ -1,13 +0,0 @@ -import { Client, Functions } from "appwrite"; - -const client = new Client() - .setEndpoint('https://cloud.appwrite.io/v1') // Your API Endpoint - .setProject(''); // Your project ID - -const functions = new Functions(client); - -const result = await functions.getTemplate( - '' // templateId -); - -console.log(result); diff --git a/docs/examples/1.6.x/client-web/examples/functions/list-templates.md b/docs/examples/1.6.x/client-web/examples/functions/list-templates.md deleted file mode 100644 index 141c56322b..0000000000 --- a/docs/examples/1.6.x/client-web/examples/functions/list-templates.md +++ /dev/null @@ -1,16 +0,0 @@ -import { Client, Functions } from "appwrite"; - -const client = new Client() - .setEndpoint('https://cloud.appwrite.io/v1') // Your API Endpoint - .setProject(''); // Your project ID - -const functions = new Functions(client); - -const result = await functions.listTemplates( - [], // runtimes (optional) - [], // useCases (optional) - 1, // limit (optional) - 0 // offset (optional) -); - -console.log(result); diff --git a/docs/examples/1.6.x/console-cli/examples/functions/download-deployment.md b/docs/examples/1.6.x/console-cli/examples/functions/download-deployment.md deleted file mode 100644 index aa440b5145..0000000000 --- a/docs/examples/1.6.x/console-cli/examples/functions/download-deployment.md +++ /dev/null @@ -1,3 +0,0 @@ -appwrite functions downloadDeployment \ - --functionId \ - --deploymentId diff --git a/docs/examples/1.6.x/console-cli/examples/functions/get-specifications.md b/docs/examples/1.6.x/console-cli/examples/functions/get-specifications.md deleted file mode 100644 index 4e612280e7..0000000000 --- a/docs/examples/1.6.x/console-cli/examples/functions/get-specifications.md +++ /dev/null @@ -1 +0,0 @@ -appwrite functions getSpecifications diff --git a/docs/examples/1.6.x/console-cli/examples/migrations/create-firebase-o-auth-migration.md b/docs/examples/1.6.x/console-cli/examples/migrations/create-firebase-o-auth-migration.md deleted file mode 100644 index 207becba03..0000000000 --- a/docs/examples/1.6.x/console-cli/examples/migrations/create-firebase-o-auth-migration.md +++ /dev/null @@ -1,3 +0,0 @@ -appwrite migrations createFirebaseOAuthMigration \ - --resources one two three \ - --projectId diff --git a/docs/examples/1.6.x/console-cli/examples/migrations/delete-firebase-auth.md b/docs/examples/1.6.x/console-cli/examples/migrations/delete-firebase-auth.md deleted file mode 100644 index d58abd9878..0000000000 --- a/docs/examples/1.6.x/console-cli/examples/migrations/delete-firebase-auth.md +++ /dev/null @@ -1 +0,0 @@ -appwrite migrations deleteFirebaseAuth diff --git a/docs/examples/1.6.x/console-cli/examples/migrations/get-firebase-report-o-auth.md b/docs/examples/1.6.x/console-cli/examples/migrations/get-firebase-report-o-auth.md deleted file mode 100644 index 0bdbf3e435..0000000000 --- a/docs/examples/1.6.x/console-cli/examples/migrations/get-firebase-report-o-auth.md +++ /dev/null @@ -1,3 +0,0 @@ -appwrite migrations getFirebaseReportOAuth \ - --resources one two three \ - --projectId diff --git a/docs/examples/1.6.x/console-cli/examples/migrations/list-firebase-projects.md b/docs/examples/1.6.x/console-cli/examples/migrations/list-firebase-projects.md deleted file mode 100644 index a0e59713eb..0000000000 --- a/docs/examples/1.6.x/console-cli/examples/migrations/list-firebase-projects.md +++ /dev/null @@ -1 +0,0 @@ -appwrite migrations listFirebaseProjects diff --git a/docs/examples/1.6.x/console-web/examples/functions/download-deployment.md b/docs/examples/1.6.x/console-web/examples/functions/download-deployment.md deleted file mode 100644 index 8115c7e130..0000000000 --- a/docs/examples/1.6.x/console-web/examples/functions/download-deployment.md +++ /dev/null @@ -1,14 +0,0 @@ -import { Client, Functions } from "@appwrite.io/console"; - -const client = new Client() - .setEndpoint('https://cloud.appwrite.io/v1') // Your API Endpoint - .setProject(''); // Your project ID - -const functions = new Functions(client); - -const result = functions.downloadDeployment( - '', // functionId - '' // deploymentId -); - -console.log(result); diff --git a/docs/examples/1.6.x/console-web/examples/functions/get-specifications.md b/docs/examples/1.6.x/console-web/examples/functions/get-specifications.md deleted file mode 100644 index c5cb3fc043..0000000000 --- a/docs/examples/1.6.x/console-web/examples/functions/get-specifications.md +++ /dev/null @@ -1,11 +0,0 @@ -import { Client, Functions } from "@appwrite.io/console"; - -const client = new Client() - .setEndpoint('https://cloud.appwrite.io/v1') // Your API Endpoint - .setProject(''); // Your project ID - -const functions = new Functions(client); - -const result = await functions.getSpecifications(); - -console.log(result); diff --git a/docs/examples/1.6.x/console-web/examples/migrations/create-firebase-o-auth-migration.md b/docs/examples/1.6.x/console-web/examples/migrations/create-firebase-o-auth-migration.md deleted file mode 100644 index bf57d427e9..0000000000 --- a/docs/examples/1.6.x/console-web/examples/migrations/create-firebase-o-auth-migration.md +++ /dev/null @@ -1,14 +0,0 @@ -import { Client, Migrations } from "@appwrite.io/console"; - -const client = new Client() - .setEndpoint('https://cloud.appwrite.io/v1') // Your API Endpoint - .setProject(''); // Your project ID - -const migrations = new Migrations(client); - -const result = await migrations.createFirebaseOAuthMigration( - [], // resources - '' // projectId -); - -console.log(result); diff --git a/docs/examples/1.6.x/console-web/examples/migrations/delete-firebase-auth.md b/docs/examples/1.6.x/console-web/examples/migrations/delete-firebase-auth.md deleted file mode 100644 index 8c7683b46a..0000000000 --- a/docs/examples/1.6.x/console-web/examples/migrations/delete-firebase-auth.md +++ /dev/null @@ -1,11 +0,0 @@ -import { Client, Migrations } from "@appwrite.io/console"; - -const client = new Client() - .setEndpoint('https://cloud.appwrite.io/v1') // Your API Endpoint - .setProject(''); // Your project ID - -const migrations = new Migrations(client); - -const result = await migrations.deleteFirebaseAuth(); - -console.log(result); diff --git a/docs/examples/1.6.x/console-web/examples/migrations/get-firebase-report-o-auth.md b/docs/examples/1.6.x/console-web/examples/migrations/get-firebase-report-o-auth.md deleted file mode 100644 index 055ddfba7c..0000000000 --- a/docs/examples/1.6.x/console-web/examples/migrations/get-firebase-report-o-auth.md +++ /dev/null @@ -1,14 +0,0 @@ -import { Client, Migrations } from "@appwrite.io/console"; - -const client = new Client() - .setEndpoint('https://cloud.appwrite.io/v1') // Your API Endpoint - .setProject(''); // Your project ID - -const migrations = new Migrations(client); - -const result = await migrations.getFirebaseReportOAuth( - [], // resources - '' // projectId -); - -console.log(result); diff --git a/docs/examples/1.6.x/console-web/examples/migrations/list-firebase-projects.md b/docs/examples/1.6.x/console-web/examples/migrations/list-firebase-projects.md deleted file mode 100644 index 833c05fe24..0000000000 --- a/docs/examples/1.6.x/console-web/examples/migrations/list-firebase-projects.md +++ /dev/null @@ -1,11 +0,0 @@ -import { Client, Migrations } from "@appwrite.io/console"; - -const client = new Client() - .setEndpoint('https://cloud.appwrite.io/v1') // Your API Endpoint - .setProject(''); // Your project ID - -const migrations = new Migrations(client); - -const result = await migrations.listFirebaseProjects(); - -console.log(result); diff --git a/docs/examples/1.6.x/server-dart/examples/account/update-mfa-challenge.md b/docs/examples/1.6.x/server-dart/examples/account/update-mfa-challenge.md index fd64c61cf9..2843d2f1b4 100644 --- a/docs/examples/1.6.x/server-dart/examples/account/update-mfa-challenge.md +++ b/docs/examples/1.6.x/server-dart/examples/account/update-mfa-challenge.md @@ -7,7 +7,7 @@ Client client = Client() Account account = Account(client); - result = await account.updateMfaChallenge( +Session result = await account.updateMfaChallenge( challengeId: '', otp: '', ); diff --git a/docs/examples/1.6.x/server-dart/examples/functions/download-deployment.md b/docs/examples/1.6.x/server-dart/examples/functions/download-deployment.md deleted file mode 100644 index 981de76756..0000000000 --- a/docs/examples/1.6.x/server-dart/examples/functions/download-deployment.md +++ /dev/null @@ -1,13 +0,0 @@ -import 'package:dart_appwrite/dart_appwrite.dart'; - -Client client = Client() - .setEndpoint('https://cloud.appwrite.io/v1') // Your API Endpoint - .setProject('') // Your project ID - .setKey(''); // Your secret API key - -Functions functions = Functions(client); - -UInt8List result = await functions.downloadDeployment( - functionId: '', - deploymentId: '', -); diff --git a/docs/examples/1.6.x/server-dart/examples/functions/get-template.md b/docs/examples/1.6.x/server-dart/examples/functions/get-template.md deleted file mode 100644 index 881dac6782..0000000000 --- a/docs/examples/1.6.x/server-dart/examples/functions/get-template.md +++ /dev/null @@ -1,11 +0,0 @@ -import 'package:dart_appwrite/dart_appwrite.dart'; - -Client client = Client() - .setEndpoint('https://cloud.appwrite.io/v1') // Your API Endpoint - .setProject(''); // Your project ID - -Functions functions = Functions(client); - -TemplateFunction result = await functions.getTemplate( - templateId: '', -); diff --git a/docs/examples/1.6.x/server-dart/examples/functions/list-templates.md b/docs/examples/1.6.x/server-dart/examples/functions/list-templates.md deleted file mode 100644 index 7bef5106ed..0000000000 --- a/docs/examples/1.6.x/server-dart/examples/functions/list-templates.md +++ /dev/null @@ -1,14 +0,0 @@ -import 'package:dart_appwrite/dart_appwrite.dart'; - -Client client = Client() - .setEndpoint('https://cloud.appwrite.io/v1') // Your API Endpoint - .setProject(''); // Your project ID - -Functions functions = Functions(client); - -TemplateFunctionList result = await functions.listTemplates( - runtimes: [], // (optional) - useCases: [], // (optional) - limit: 1, // (optional) - offset: 0, // (optional) -); diff --git a/docs/examples/1.6.x/server-deno/examples/functions/download-deployment.md b/docs/examples/1.6.x/server-deno/examples/functions/download-deployment.md deleted file mode 100644 index bb0a4e71b4..0000000000 --- a/docs/examples/1.6.x/server-deno/examples/functions/download-deployment.md +++ /dev/null @@ -1,13 +0,0 @@ -import { Client, Functions } from "https://deno.land/x/appwrite/mod.ts"; - -const client = new Client() - .setEndpoint('https://cloud.appwrite.io/v1') // Your API Endpoint - .setProject('') // Your project ID - .setKey(''); // Your secret API key - -const functions = new Functions(client); - -const result = functions.downloadDeployment( - '', // functionId - '' // deploymentId -); diff --git a/docs/examples/1.6.x/server-deno/examples/functions/get-template.md b/docs/examples/1.6.x/server-deno/examples/functions/get-template.md deleted file mode 100644 index 7a58c95a34..0000000000 --- a/docs/examples/1.6.x/server-deno/examples/functions/get-template.md +++ /dev/null @@ -1,11 +0,0 @@ -import { Client, Functions } from "https://deno.land/x/appwrite/mod.ts"; - -const client = new Client() - .setEndpoint('https://cloud.appwrite.io/v1') // Your API Endpoint - .setProject(''); // Your project ID - -const functions = new Functions(client); - -const response = await functions.getTemplate( - '' // templateId -); diff --git a/docs/examples/1.6.x/server-deno/examples/functions/list-templates.md b/docs/examples/1.6.x/server-deno/examples/functions/list-templates.md deleted file mode 100644 index 06203e404b..0000000000 --- a/docs/examples/1.6.x/server-deno/examples/functions/list-templates.md +++ /dev/null @@ -1,14 +0,0 @@ -import { Client, Functions } from "https://deno.land/x/appwrite/mod.ts"; - -const client = new Client() - .setEndpoint('https://cloud.appwrite.io/v1') // Your API Endpoint - .setProject(''); // Your project ID - -const functions = new Functions(client); - -const response = await functions.listTemplates( - [], // runtimes (optional) - [], // useCases (optional) - 1, // limit (optional) - 0 // offset (optional) -); diff --git a/docs/examples/1.6.x/server-dotnet/examples/account/update-mfa-challenge.md b/docs/examples/1.6.x/server-dotnet/examples/account/update-mfa-challenge.md index edf863a36e..ec32f8c900 100644 --- a/docs/examples/1.6.x/server-dotnet/examples/account/update-mfa-challenge.md +++ b/docs/examples/1.6.x/server-dotnet/examples/account/update-mfa-challenge.md @@ -9,7 +9,7 @@ Client client = new Client() Account account = new Account(client); - result = await account.UpdateMfaChallenge( +Session result = await account.UpdateMfaChallenge( challengeId: "", otp: "" ); \ No newline at end of file diff --git a/docs/examples/1.6.x/server-dotnet/examples/functions/download-deployment.md b/docs/examples/1.6.x/server-dotnet/examples/functions/download-deployment.md deleted file mode 100644 index 83f56a8e14..0000000000 --- a/docs/examples/1.6.x/server-dotnet/examples/functions/download-deployment.md +++ /dev/null @@ -1,15 +0,0 @@ -using Appwrite; -using Appwrite.Models; -using Appwrite.Services; - -Client client = new Client() - .SetEndPoint("https://cloud.appwrite.io/v1") // Your API Endpoint - .SetProject("") // Your project ID - .SetKey(""); // Your secret API key - -Functions functions = new Functions(client); - -byte[] result = await functions.DownloadDeployment( - functionId: "", - deploymentId: "" -); \ No newline at end of file diff --git a/docs/examples/1.6.x/server-dotnet/examples/functions/get-template.md b/docs/examples/1.6.x/server-dotnet/examples/functions/get-template.md deleted file mode 100644 index f57d61a024..0000000000 --- a/docs/examples/1.6.x/server-dotnet/examples/functions/get-template.md +++ /dev/null @@ -1,13 +0,0 @@ -using Appwrite; -using Appwrite.Models; -using Appwrite.Services; - -Client client = new Client() - .SetEndPoint("https://cloud.appwrite.io/v1") // Your API Endpoint - .SetProject(""); // Your project ID - -Functions functions = new Functions(client); - -TemplateFunction result = await functions.GetTemplate( - templateId: "" -); \ No newline at end of file diff --git a/docs/examples/1.6.x/server-dotnet/examples/functions/list-templates.md b/docs/examples/1.6.x/server-dotnet/examples/functions/list-templates.md deleted file mode 100644 index f76bd5fa9c..0000000000 --- a/docs/examples/1.6.x/server-dotnet/examples/functions/list-templates.md +++ /dev/null @@ -1,16 +0,0 @@ -using Appwrite; -using Appwrite.Models; -using Appwrite.Services; - -Client client = new Client() - .SetEndPoint("https://cloud.appwrite.io/v1") // Your API Endpoint - .SetProject(""); // Your project ID - -Functions functions = new Functions(client); - -TemplateFunctionList result = await functions.ListTemplates( - runtimes: new List(), // optional - useCases: new List(), // optional - limit: 1, // optional - offset: 0 // optional -); \ No newline at end of file diff --git a/docs/examples/1.6.x/server-go/examples/functions/download-deployment.md b/docs/examples/1.6.x/server-go/examples/functions/download-deployment.md deleted file mode 100644 index 37d2149541..0000000000 --- a/docs/examples/1.6.x/server-go/examples/functions/download-deployment.md +++ /dev/null @@ -1,27 +0,0 @@ -package main - -import ( - "fmt" - "github.com/appwrite/sdk-for-go/client" - "github.com/appwrite/sdk-for-go/functions" -) - -func main() { - client := client.NewClient() - - client.SetEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint - client.SetProject("") // Your project ID - client.SetKey("") // Your secret API key - - service := functions.NewFunctions(client) - response, error := service.DownloadDeployment( - "", - "", - ) - - if error != nil { - panic(error) - } - - fmt.Println(response) -} diff --git a/docs/examples/1.6.x/server-go/examples/functions/get-template.md b/docs/examples/1.6.x/server-go/examples/functions/get-template.md deleted file mode 100644 index fe4e1debb9..0000000000 --- a/docs/examples/1.6.x/server-go/examples/functions/get-template.md +++ /dev/null @@ -1,25 +0,0 @@ -package main - -import ( - "fmt" - "github.com/appwrite/sdk-for-go/client" - "github.com/appwrite/sdk-for-go/functions" -) - -func main() { - client := client.NewClient() - - client.SetEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint - client.SetProject("") // Your project ID - - service := functions.NewFunctions(client) - response, error := service.GetTemplate( - "", - ) - - if error != nil { - panic(error) - } - - fmt.Println(response) -} diff --git a/docs/examples/1.6.x/server-go/examples/functions/list-templates.md b/docs/examples/1.6.x/server-go/examples/functions/list-templates.md deleted file mode 100644 index 19edf6d360..0000000000 --- a/docs/examples/1.6.x/server-go/examples/functions/list-templates.md +++ /dev/null @@ -1,28 +0,0 @@ -package main - -import ( - "fmt" - "github.com/appwrite/sdk-for-go/client" - "github.com/appwrite/sdk-for-go/functions" -) - -func main() { - client := client.NewClient() - - client.SetEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint - client.SetProject("") // Your project ID - - service := functions.NewFunctions(client) - response, error := service.ListTemplates( - functions.WithListTemplatesRuntimes([]interface{}{}), - functions.WithListTemplatesUseCases([]interface{}{}), - functions.WithListTemplatesLimit(1), - functions.WithListTemplatesOffset(0), - ) - - if error != nil { - panic(error) - } - - fmt.Println(response) -} diff --git a/docs/examples/1.6.x/server-graphql/examples/account/update-mfa-challenge.md b/docs/examples/1.6.x/server-graphql/examples/account/update-mfa-challenge.md index 8237431bcf..0bcec2157f 100644 --- a/docs/examples/1.6.x/server-graphql/examples/account/update-mfa-challenge.md +++ b/docs/examples/1.6.x/server-graphql/examples/account/update-mfa-challenge.md @@ -3,6 +3,34 @@ mutation { challengeId: "", otp: "" ) { - status + _id + _createdAt + _updatedAt + userId + expire + provider + providerUid + providerAccessToken + providerAccessTokenExpiry + providerRefreshToken + ip + osCode + osName + osVersion + clientType + clientCode + clientName + clientVersion + clientEngine + clientEngineVersion + deviceName + deviceBrand + deviceModel + countryCode + countryName + current + factors + secret + mfaUpdatedAt } } diff --git a/docs/examples/1.6.x/server-graphql/examples/functions/download-deployment.md b/docs/examples/1.6.x/server-graphql/examples/functions/download-deployment.md deleted file mode 100644 index f791338765..0000000000 --- a/docs/examples/1.6.x/server-graphql/examples/functions/download-deployment.md +++ /dev/null @@ -1,8 +0,0 @@ -query { - functionsDownloadDeployment( - functionId: "", - deploymentId: "" - ) { - status - } -} diff --git a/docs/examples/1.6.x/server-graphql/examples/functions/get-template.md b/docs/examples/1.6.x/server-graphql/examples/functions/get-template.md deleted file mode 100644 index 44a466de27..0000000000 --- a/docs/examples/1.6.x/server-graphql/examples/functions/get-template.md +++ /dev/null @@ -1,35 +0,0 @@ -query { - functionsGetTemplate( - templateId: "" - ) { - icon - id - name - tagline - permissions - events - cron - timeout - useCases - runtimes { - name - commands - entrypoint - providerRootDirectory - } - instructions - vcsProvider - providerRepositoryId - providerOwner - providerVersion - variables { - name - description - value - placeholder - required - type - } - scopes - } -} diff --git a/docs/examples/1.6.x/server-graphql/examples/functions/list-templates.md b/docs/examples/1.6.x/server-graphql/examples/functions/list-templates.md deleted file mode 100644 index cb289ddabb..0000000000 --- a/docs/examples/1.6.x/server-graphql/examples/functions/list-templates.md +++ /dev/null @@ -1,41 +0,0 @@ -query { - functionsListTemplates( - runtimes: [], - useCases: [], - limit: 1, - offset: 0 - ) { - total - templates { - icon - id - name - tagline - permissions - events - cron - timeout - useCases - runtimes { - name - commands - entrypoint - providerRootDirectory - } - instructions - vcsProvider - providerRepositoryId - providerOwner - providerVersion - variables { - name - description - value - placeholder - required - type - } - scopes - } - } -} diff --git a/docs/examples/1.6.x/server-graphql/examples/users/delete-mfa-authenticator.md b/docs/examples/1.6.x/server-graphql/examples/users/delete-mfa-authenticator.md index 26c9594a53..43f73404f0 100644 --- a/docs/examples/1.6.x/server-graphql/examples/users/delete-mfa-authenticator.md +++ b/docs/examples/1.6.x/server-graphql/examples/users/delete-mfa-authenticator.md @@ -3,36 +3,6 @@ mutation { userId: "", type: "totp" ) { - _id - _createdAt - _updatedAt - name - password - hash - hashOptions - registration status - labels - passwordUpdate - email - phone - emailVerification - phoneVerification - mfa - prefs { - data - } - targets { - _id - _createdAt - _updatedAt - name - userId - providerId - providerType - identifier - expired - } - accessedAt } } diff --git a/docs/examples/1.6.x/server-kotlin/java/functions/download-deployment.md b/docs/examples/1.6.x/server-kotlin/java/functions/download-deployment.md deleted file mode 100644 index 77e4809379..0000000000 --- a/docs/examples/1.6.x/server-kotlin/java/functions/download-deployment.md +++ /dev/null @@ -1,24 +0,0 @@ -import io.appwrite.Client; -import io.appwrite.coroutines.CoroutineCallback; -import io.appwrite.services.Functions; - -Client client = new Client() - .setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint - .setProject("") // Your project ID - .setKey(""); // Your secret API key - -Functions functions = new Functions(client); - -functions.downloadDeployment( - "", // functionId - "", // deploymentId - new CoroutineCallback<>((result, error) -> { - if (error != null) { - error.printStackTrace(); - return; - } - - System.out.println(result); - }) -); - diff --git a/docs/examples/1.6.x/server-kotlin/java/functions/get-template.md b/docs/examples/1.6.x/server-kotlin/java/functions/get-template.md deleted file mode 100644 index 1521fa47c1..0000000000 --- a/docs/examples/1.6.x/server-kotlin/java/functions/get-template.md +++ /dev/null @@ -1,22 +0,0 @@ -import io.appwrite.Client; -import io.appwrite.coroutines.CoroutineCallback; -import io.appwrite.services.Functions; - -Client client = new Client() - .setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint - .setProject(""); // Your project ID - -Functions functions = new Functions(client); - -functions.getTemplate( - "", // templateId - new CoroutineCallback<>((result, error) -> { - if (error != null) { - error.printStackTrace(); - return; - } - - System.out.println(result); - }) -); - diff --git a/docs/examples/1.6.x/server-kotlin/java/functions/list-templates.md b/docs/examples/1.6.x/server-kotlin/java/functions/list-templates.md deleted file mode 100644 index e88e19124f..0000000000 --- a/docs/examples/1.6.x/server-kotlin/java/functions/list-templates.md +++ /dev/null @@ -1,25 +0,0 @@ -import io.appwrite.Client; -import io.appwrite.coroutines.CoroutineCallback; -import io.appwrite.services.Functions; - -Client client = new Client() - .setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint - .setProject(""); // Your project ID - -Functions functions = new Functions(client); - -functions.listTemplates( - listOf(), // runtimes (optional) - listOf(), // useCases (optional) - 1, // limit (optional) - 0, // offset (optional) - new CoroutineCallback<>((result, error) -> { - if (error != null) { - error.printStackTrace(); - return; - } - - System.out.println(result); - }) -); - diff --git a/docs/examples/1.6.x/server-kotlin/kotlin/functions/download-deployment.md b/docs/examples/1.6.x/server-kotlin/kotlin/functions/download-deployment.md deleted file mode 100644 index 0ae36b314f..0000000000 --- a/docs/examples/1.6.x/server-kotlin/kotlin/functions/download-deployment.md +++ /dev/null @@ -1,15 +0,0 @@ -import io.appwrite.Client -import io.appwrite.coroutines.CoroutineCallback -import io.appwrite.services.Functions - -val client = Client() - .setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint - .setProject("") // Your project ID - .setKey("") // Your secret API key - -val functions = Functions(client) - -val result = functions.downloadDeployment( - functionId = "", - deploymentId = "" -) diff --git a/docs/examples/1.6.x/server-kotlin/kotlin/functions/get-template.md b/docs/examples/1.6.x/server-kotlin/kotlin/functions/get-template.md deleted file mode 100644 index 53d838f7e6..0000000000 --- a/docs/examples/1.6.x/server-kotlin/kotlin/functions/get-template.md +++ /dev/null @@ -1,13 +0,0 @@ -import io.appwrite.Client -import io.appwrite.coroutines.CoroutineCallback -import io.appwrite.services.Functions - -val client = Client() - .setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint - .setProject("") // Your project ID - -val functions = Functions(client) - -val response = functions.getTemplate( - templateId = "" -) diff --git a/docs/examples/1.6.x/server-kotlin/kotlin/functions/list-templates.md b/docs/examples/1.6.x/server-kotlin/kotlin/functions/list-templates.md deleted file mode 100644 index 37dc89ce89..0000000000 --- a/docs/examples/1.6.x/server-kotlin/kotlin/functions/list-templates.md +++ /dev/null @@ -1,16 +0,0 @@ -import io.appwrite.Client -import io.appwrite.coroutines.CoroutineCallback -import io.appwrite.services.Functions - -val client = Client() - .setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint - .setProject("") // Your project ID - -val functions = Functions(client) - -val response = functions.listTemplates( - runtimes = listOf(), // optional - useCases = listOf(), // optional - limit = 1, // optional - offset = 0 // optional -) diff --git a/docs/examples/1.6.x/server-nodejs/examples/functions/download-deployment.md b/docs/examples/1.6.x/server-nodejs/examples/functions/download-deployment.md deleted file mode 100644 index e0962db0ed..0000000000 --- a/docs/examples/1.6.x/server-nodejs/examples/functions/download-deployment.md +++ /dev/null @@ -1,13 +0,0 @@ -const sdk = require('node-appwrite'); - -const client = new sdk.Client() - .setEndpoint('https://cloud.appwrite.io/v1') // Your API Endpoint - .setProject('') // Your project ID - .setKey(''); // Your secret API key - -const functions = new sdk.Functions(client); - -const result = await functions.downloadDeployment( - '', // functionId - '' // deploymentId -); diff --git a/docs/examples/1.6.x/server-nodejs/examples/functions/get-template.md b/docs/examples/1.6.x/server-nodejs/examples/functions/get-template.md deleted file mode 100644 index 36195db7f3..0000000000 --- a/docs/examples/1.6.x/server-nodejs/examples/functions/get-template.md +++ /dev/null @@ -1,11 +0,0 @@ -const sdk = require('node-appwrite'); - -const client = new sdk.Client() - .setEndpoint('https://cloud.appwrite.io/v1') // Your API Endpoint - .setProject(''); // Your project ID - -const functions = new sdk.Functions(client); - -const result = await functions.getTemplate( - '' // templateId -); diff --git a/docs/examples/1.6.x/server-nodejs/examples/functions/list-templates.md b/docs/examples/1.6.x/server-nodejs/examples/functions/list-templates.md deleted file mode 100644 index 6f896cac97..0000000000 --- a/docs/examples/1.6.x/server-nodejs/examples/functions/list-templates.md +++ /dev/null @@ -1,14 +0,0 @@ -const sdk = require('node-appwrite'); - -const client = new sdk.Client() - .setEndpoint('https://cloud.appwrite.io/v1') // Your API Endpoint - .setProject(''); // Your project ID - -const functions = new sdk.Functions(client); - -const result = await functions.listTemplates( - [], // runtimes (optional) - [], // useCases (optional) - 1, // limit (optional) - 0 // offset (optional) -); diff --git a/docs/examples/1.6.x/server-php/examples/functions/download-deployment.md b/docs/examples/1.6.x/server-php/examples/functions/download-deployment.md deleted file mode 100644 index 83c8bb756f..0000000000 --- a/docs/examples/1.6.x/server-php/examples/functions/download-deployment.md +++ /dev/null @@ -1,16 +0,0 @@ -setEndpoint('https://cloud.appwrite.io/v1') // Your API Endpoint - ->setProject('') // Your project ID - ->setKey(''); // Your secret API key - -$functions = new Functions($client); - -$result = $functions->downloadDeployment( - functionId: '', - deploymentId: '' -); \ No newline at end of file diff --git a/docs/examples/1.6.x/server-php/examples/functions/get-template.md b/docs/examples/1.6.x/server-php/examples/functions/get-template.md deleted file mode 100644 index 421557dcd2..0000000000 --- a/docs/examples/1.6.x/server-php/examples/functions/get-template.md +++ /dev/null @@ -1,14 +0,0 @@ -setEndpoint('https://cloud.appwrite.io/v1') // Your API Endpoint - ->setProject(''); // Your project ID - -$functions = new Functions($client); - -$result = $functions->getTemplate( - templateId: '' -); \ No newline at end of file diff --git a/docs/examples/1.6.x/server-php/examples/functions/list-templates.md b/docs/examples/1.6.x/server-php/examples/functions/list-templates.md deleted file mode 100644 index a661903306..0000000000 --- a/docs/examples/1.6.x/server-php/examples/functions/list-templates.md +++ /dev/null @@ -1,17 +0,0 @@ -setEndpoint('https://cloud.appwrite.io/v1') // Your API Endpoint - ->setProject(''); // Your project ID - -$functions = new Functions($client); - -$result = $functions->listTemplates( - runtimes: [], // optional - useCases: [], // optional - limit: 1, // optional - offset: 0 // optional -); \ No newline at end of file diff --git a/docs/examples/1.6.x/server-python/examples/functions/download-deployment.md b/docs/examples/1.6.x/server-python/examples/functions/download-deployment.md deleted file mode 100644 index f9d395451e..0000000000 --- a/docs/examples/1.6.x/server-python/examples/functions/download-deployment.md +++ /dev/null @@ -1,13 +0,0 @@ -from appwrite.client import Client - -client = Client() -client.set_endpoint('https://cloud.appwrite.io/v1') # Your API Endpoint -client.set_project('') # Your project ID -client.set_key('') # Your secret API key - -functions = Functions(client) - -result = functions.download_deployment( - function_id = '', - deployment_id = '' -) diff --git a/docs/examples/1.6.x/server-python/examples/functions/get-template.md b/docs/examples/1.6.x/server-python/examples/functions/get-template.md deleted file mode 100644 index bea5b6ab03..0000000000 --- a/docs/examples/1.6.x/server-python/examples/functions/get-template.md +++ /dev/null @@ -1,11 +0,0 @@ -from appwrite.client import Client - -client = Client() -client.set_endpoint('https://cloud.appwrite.io/v1') # Your API Endpoint -client.set_project('') # Your project ID - -functions = Functions(client) - -result = functions.get_template( - template_id = '' -) diff --git a/docs/examples/1.6.x/server-python/examples/functions/list-templates.md b/docs/examples/1.6.x/server-python/examples/functions/list-templates.md deleted file mode 100644 index b3ee38aef7..0000000000 --- a/docs/examples/1.6.x/server-python/examples/functions/list-templates.md +++ /dev/null @@ -1,14 +0,0 @@ -from appwrite.client import Client - -client = Client() -client.set_endpoint('https://cloud.appwrite.io/v1') # Your API Endpoint -client.set_project('') # Your project ID - -functions = Functions(client) - -result = functions.list_templates( - runtimes = [], # optional - use_cases = [], # optional - limit = 1, # optional - offset = 0 # optional -) diff --git a/docs/examples/1.6.x/server-rest/examples/functions/download-deployment.md b/docs/examples/1.6.x/server-rest/examples/functions/download-deployment.md deleted file mode 100644 index ccd37283c9..0000000000 --- a/docs/examples/1.6.x/server-rest/examples/functions/download-deployment.md +++ /dev/null @@ -1,7 +0,0 @@ -GET /v1/functions/{functionId}/deployments/{deploymentId}/download HTTP/1.1 -Host: cloud.appwrite.io -Content-Type: application/json -X-Appwrite-Response-Format: 1.5.0 -X-Appwrite-Project: -X-Appwrite-Key: - diff --git a/docs/examples/1.6.x/server-rest/examples/functions/get-template.md b/docs/examples/1.6.x/server-rest/examples/functions/get-template.md deleted file mode 100644 index d4e1f95cf5..0000000000 --- a/docs/examples/1.6.x/server-rest/examples/functions/get-template.md +++ /dev/null @@ -1,6 +0,0 @@ -GET /v1/functions/templates/{templateId} HTTP/1.1 -Host: cloud.appwrite.io -Content-Type: application/json -X-Appwrite-Response-Format: 1.6.0 -X-Appwrite-Project: - diff --git a/docs/examples/1.6.x/server-rest/examples/functions/list-templates.md b/docs/examples/1.6.x/server-rest/examples/functions/list-templates.md deleted file mode 100644 index b671bedebf..0000000000 --- a/docs/examples/1.6.x/server-rest/examples/functions/list-templates.md +++ /dev/null @@ -1,6 +0,0 @@ -GET /v1/functions/templates HTTP/1.1 -Host: cloud.appwrite.io -Content-Type: application/json -X-Appwrite-Response-Format: 1.6.0 -X-Appwrite-Project: - diff --git a/docs/examples/1.6.x/server-ruby/examples/functions/download-deployment.md b/docs/examples/1.6.x/server-ruby/examples/functions/download-deployment.md deleted file mode 100644 index 748d0c9d01..0000000000 --- a/docs/examples/1.6.x/server-ruby/examples/functions/download-deployment.md +++ /dev/null @@ -1,15 +0,0 @@ -require 'appwrite' - -include Appwrite - -client = Client.new - .set_endpoint('https://cloud.appwrite.io/v1') # Your API Endpoint - .set_project('') # Your project ID - .set_key('') # Your secret API key - -functions = Functions.new(client) - -result = functions.download_deployment( - function_id: '', - deployment_id: '' -) diff --git a/docs/examples/1.6.x/server-ruby/examples/functions/get-template.md b/docs/examples/1.6.x/server-ruby/examples/functions/get-template.md deleted file mode 100644 index 5274880f67..0000000000 --- a/docs/examples/1.6.x/server-ruby/examples/functions/get-template.md +++ /dev/null @@ -1,13 +0,0 @@ -require 'appwrite' - -include Appwrite - -client = Client.new - .set_endpoint('https://cloud.appwrite.io/v1') # Your API Endpoint - .set_project('') # Your project ID - -functions = Functions.new(client) - -result = functions.get_template( - template_id: '' -) diff --git a/docs/examples/1.6.x/server-ruby/examples/functions/list-templates.md b/docs/examples/1.6.x/server-ruby/examples/functions/list-templates.md deleted file mode 100644 index 8bee6b0187..0000000000 --- a/docs/examples/1.6.x/server-ruby/examples/functions/list-templates.md +++ /dev/null @@ -1,16 +0,0 @@ -require 'appwrite' - -include Appwrite - -client = Client.new - .set_endpoint('https://cloud.appwrite.io/v1') # Your API Endpoint - .set_project('') # Your project ID - -functions = Functions.new(client) - -result = functions.list_templates( - runtimes: [], # optional - use_cases: [], # optional - limit: 1, # optional - offset: 0 # optional -) diff --git a/docs/examples/1.6.x/server-swift/examples/account/update-mfa-challenge.md b/docs/examples/1.6.x/server-swift/examples/account/update-mfa-challenge.md index eed3bfade7..fee76bf0dd 100644 --- a/docs/examples/1.6.x/server-swift/examples/account/update-mfa-challenge.md +++ b/docs/examples/1.6.x/server-swift/examples/account/update-mfa-challenge.md @@ -7,7 +7,7 @@ let client = Client() let account = Account(client) -let result = try await account.updateMfaChallenge( +let session = try await account.updateMfaChallenge( challengeId: "", otp: "" ) diff --git a/docs/examples/1.6.x/server-swift/examples/functions/download-deployment.md b/docs/examples/1.6.x/server-swift/examples/functions/download-deployment.md deleted file mode 100644 index 753d7058e6..0000000000 --- a/docs/examples/1.6.x/server-swift/examples/functions/download-deployment.md +++ /dev/null @@ -1,14 +0,0 @@ -import Appwrite - -let client = Client() - .setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint - .setProject("") // Your project ID - .setKey("") // Your secret API key - -let functions = Functions(client) - -let bytes = try await functions.downloadDeployment( - functionId: "", - deploymentId: "" -) - diff --git a/docs/examples/1.6.x/server-swift/examples/functions/get-template.md b/docs/examples/1.6.x/server-swift/examples/functions/get-template.md deleted file mode 100644 index bc7a9a3aef..0000000000 --- a/docs/examples/1.6.x/server-swift/examples/functions/get-template.md +++ /dev/null @@ -1,12 +0,0 @@ -import Appwrite - -let client = Client() - .setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint - .setProject("") // Your project ID - -let functions = Functions(client) - -let templateFunction = try await functions.getTemplate( - templateId: "" -) - diff --git a/docs/examples/1.6.x/server-swift/examples/functions/list-templates.md b/docs/examples/1.6.x/server-swift/examples/functions/list-templates.md deleted file mode 100644 index d0090ab803..0000000000 --- a/docs/examples/1.6.x/server-swift/examples/functions/list-templates.md +++ /dev/null @@ -1,15 +0,0 @@ -import Appwrite - -let client = Client() - .setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint - .setProject("") // Your project ID - -let functions = Functions(client) - -let templateFunctionList = try await functions.listTemplates( - runtimes: [], // optional - useCases: [], // optional - limit: 1, // optional - offset: 0 // optional -) - diff --git a/docs/examples/1.6.x/server-swift/examples/users/delete-mfa-authenticator.md b/docs/examples/1.6.x/server-swift/examples/users/delete-mfa-authenticator.md index 902d0c904c..5f1d6a0eeb 100644 --- a/docs/examples/1.6.x/server-swift/examples/users/delete-mfa-authenticator.md +++ b/docs/examples/1.6.x/server-swift/examples/users/delete-mfa-authenticator.md @@ -8,7 +8,7 @@ let client = Client() let users = Users(client) -let user = try await users.deleteMfaAuthenticator( +let result = try await users.deleteMfaAuthenticator( userId: "", type: .totp ) From 556f7338c104b3bb01b484b7591b9ee9572d4020 Mon Sep 17 00:00:00 2001 From: Steven Nguyen Date: Wed, 12 Mar 2025 15:52:29 -0700 Subject: [PATCH 118/143] Bump appwrite version to 1.6.2 --- README-CN.md | 6 +++--- README.md | 6 +++--- app/init.php | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/README-CN.md b/README-CN.md index c024491907..a81d99c3c3 100644 --- a/README-CN.md +++ b/README-CN.md @@ -72,7 +72,7 @@ docker run -it --rm \ --volume /var/run/docker.sock:/var/run/docker.sock \ --volume "$(pwd)"/appwrite:/usr/src/code/appwrite:rw \ --entrypoint="install" \ - appwrite/appwrite:1.6.1 + appwrite/appwrite:1.6.2 ``` ### Windows @@ -84,7 +84,7 @@ docker run -it --rm ^ --volume //var/run/docker.sock:/var/run/docker.sock ^ --volume "%cd%"/appwrite:/usr/src/code/appwrite:rw ^ --entrypoint="install" ^ - appwrite/appwrite:1.6.1 + appwrite/appwrite:1.6.2 ``` #### PowerShell @@ -94,7 +94,7 @@ docker run -it --rm ` --volume /var/run/docker.sock:/var/run/docker.sock ` --volume ${pwd}/appwrite:/usr/src/code/appwrite:rw ` --entrypoint="install" ` - appwrite/appwrite:1.6.1 + appwrite/appwrite:1.6.2 ``` 运行后,可以在浏览器上访问 http://localhost 找到 Appwrite 控制台。在非 Linux 的本机主机上完成安装后,服务器可能需要几分钟才能启动。 diff --git a/README.md b/README.md index 5bccf4739f..c2b1151e36 100644 --- a/README.md +++ b/README.md @@ -79,7 +79,7 @@ docker run -it --rm \ --volume /var/run/docker.sock:/var/run/docker.sock \ --volume "$(pwd)"/appwrite:/usr/src/code/appwrite:rw \ --entrypoint="install" \ - appwrite/appwrite:1.6.1 + appwrite/appwrite:1.6.2 ``` ### Windows @@ -91,7 +91,7 @@ docker run -it --rm ^ --volume //var/run/docker.sock:/var/run/docker.sock ^ --volume "%cd%"/appwrite:/usr/src/code/appwrite:rw ^ --entrypoint="install" ^ - appwrite/appwrite:1.6.1 + appwrite/appwrite:1.6.2 ``` #### PowerShell @@ -101,7 +101,7 @@ docker run -it --rm ` --volume /var/run/docker.sock:/var/run/docker.sock ` --volume ${pwd}/appwrite:/usr/src/code/appwrite:rw ` --entrypoint="install" ` - appwrite/appwrite:1.6.1 + appwrite/appwrite:1.6.2 ``` Once the Docker installation is complete, go to http://localhost to access the Appwrite console from your browser. Please note that on non-Linux native hosts, the server might take a few minutes to start after completing the installation. diff --git a/app/init.php b/app/init.php index 17c1a884f9..9a499412db 100644 --- a/app/init.php +++ b/app/init.php @@ -125,7 +125,7 @@ const APP_PROJECT_ACCESS = 24 * 60 * 60; // 24 hours const APP_FILE_ACCESS = 24 * 60 * 60; // 24 hours const APP_CACHE_UPDATE = 24 * 60 * 60; // 24 hours const APP_CACHE_BUSTER = 4318; -const APP_VERSION_STABLE = '1.6.1'; +const APP_VERSION_STABLE = '1.6.2'; const APP_DATABASE_ATTRIBUTE_EMAIL = 'email'; const APP_DATABASE_ATTRIBUTE_ENUM = 'enum'; const APP_DATABASE_ATTRIBUTE_IP = 'ip'; From e119bf6777effd3f18a627b4f141e460d60999bb Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Thu, 13 Mar 2025 05:30:09 +0000 Subject: [PATCH 119/143] chore: update changelogs --- docs/sdks/dart/CHANGELOG.md | 12 ++++++++++++ docs/sdks/flutter/CHANGELOG.md | 10 ++++++++++ 2 files changed, 22 insertions(+) diff --git a/docs/sdks/dart/CHANGELOG.md b/docs/sdks/dart/CHANGELOG.md index a46f1dcf0f..c41413b9ec 100644 --- a/docs/sdks/dart/CHANGELOG.md +++ b/docs/sdks/dart/CHANGELOG.md @@ -1,3 +1,15 @@ +## 14.0.0 + +* Breaking changes: + * Changed the typing of `AppwriteException`'s response parameter from a `dynamic` object to an optional string (`?String`). + +## 13.0.0 + +* Fixed realtime pong response. +* Fixed issues with `chunkedUpload` method. +* Fixed type mismatch bug where `List` was incorrectly causing runtime type errors. +* Updated return type of `updateMfaChallenge()` from raw data to properly typed `models.Session` object. + ## 12.0.0 * Support for Appwrite 1.6 diff --git a/docs/sdks/flutter/CHANGELOG.md b/docs/sdks/flutter/CHANGELOG.md index abb8967f84..110b6d08c3 100644 --- a/docs/sdks/flutter/CHANGELOG.md +++ b/docs/sdks/flutter/CHANGELOG.md @@ -1,3 +1,13 @@ +## 15.0.0 + +* Breaking changes: + * Changed the typing of `AppwriteException`'s response parameter from a `dynamic` object to an optional string (`?String`). + +## 14.0.0 + +* Fixed realtime pong response. +* Fixed issues with `chunkedUpload` method. + ## 13.0.0 * Fixed realtime reconnection issues From fcf15689e8ec27dee3c2da11e1b2a281e48b10a0 Mon Sep 17 00:00:00 2001 From: Fabian Gruber Date: Thu, 13 Mar 2025 14:02:32 +0100 Subject: [PATCH 120/143] update queue to 0.9.* --- composer.json | 4 ++-- composer.lock | 30 +++++++++++++++--------------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/composer.json b/composer.json index c9b057b061..bf64e4dc7f 100644 --- a/composer.json +++ b/composer.json @@ -62,10 +62,10 @@ "utopia-php/messaging": "0.16.*", "utopia-php/migration": "0.6.*", "utopia-php/orchestration": "0.9.*", - "utopia-php/platform": "0.7.3", + "utopia-php/platform": "0.7.*", "utopia-php/pools": "0.5.*", "utopia-php/preloader": "0.2.*", - "utopia-php/queue": "0.8.*", + "utopia-php/queue": "0.9.*", "utopia-php/registry": "0.5.*", "utopia-php/storage": "0.18.*", "utopia-php/swoole": "0.8.*", diff --git a/composer.lock b/composer.lock index 83c8ea479a..561220ba60 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "58ad2e1375ec47d944b96864d4aae63f", + "content-hash": "418a223d14b5ec5a5a8751623d5172b6", "packages": [ { "name": "adhocore/jwt", @@ -4325,16 +4325,16 @@ }, { "name": "utopia-php/platform", - "version": "0.7.3", + "version": "0.7.4", "source": { "type": "git", "url": "https://github.com/utopia-php/platform.git", - "reference": "463c2d817c893d7dbb678c2eac7a8291f2710e25" + "reference": "a5b93d8177702ec458c3af9137663133c012b71b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/platform/zipball/463c2d817c893d7dbb678c2eac7a8291f2710e25", - "reference": "463c2d817c893d7dbb678c2eac7a8291f2710e25", + "url": "https://api.github.com/repos/utopia-php/platform/zipball/a5b93d8177702ec458c3af9137663133c012b71b", + "reference": "a5b93d8177702ec458c3af9137663133c012b71b", "shasum": "" }, "require": { @@ -4343,7 +4343,7 @@ "php": ">=8.0", "utopia-php/cli": "0.15.*", "utopia-php/framework": "0.33.*", - "utopia-php/queue": "0.8.*" + "utopia-php/queue": "0.9.*" }, "require-dev": { "laravel/pint": "1.2.*", @@ -4369,9 +4369,9 @@ ], "support": { "issues": "https://github.com/utopia-php/platform/issues", - "source": "https://github.com/utopia-php/platform/tree/0.7.3" + "source": "https://github.com/utopia-php/platform/tree/0.7.4" }, - "time": "2025-02-04T15:09:00+00:00" + "time": "2025-03-13T13:00:12+00:00" }, { "name": "utopia-php/pools", @@ -4479,16 +4479,16 @@ }, { "name": "utopia-php/queue", - "version": "0.8.6", + "version": "0.9.0", "source": { "type": "git", "url": "https://github.com/utopia-php/queue.git", - "reference": "b713b997285c29d120bbcbe3d6e93762d850f87c" + "reference": "077075f1d57afa430f76c35ed3bf4616e0eee8e7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/queue/zipball/b713b997285c29d120bbcbe3d6e93762d850f87c", - "reference": "b713b997285c29d120bbcbe3d6e93762d850f87c", + "url": "https://api.github.com/repos/utopia-php/queue/zipball/077075f1d57afa430f76c35ed3bf4616e0eee8e7", + "reference": "077075f1d57afa430f76c35ed3bf4616e0eee8e7", "shasum": "" }, "require": { @@ -4538,9 +4538,9 @@ ], "support": { "issues": "https://github.com/utopia-php/queue/issues", - "source": "https://github.com/utopia-php/queue/tree/0.8.6" + "source": "https://github.com/utopia-php/queue/tree/0.9.0" }, - "time": "2025-02-10T03:35:00+00:00" + "time": "2025-03-13T12:22:41+00:00" }, { "name": "utopia-php/registry", @@ -8402,7 +8402,7 @@ ], "aliases": [], "minimum-stability": "stable", - "stability-flags": {}, + "stability-flags": [], "prefer-stable": false, "prefer-lowest": false, "platform": { From 49d89a42065e7ae0c47eb35a32f459d7bd44eebb Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Fri, 14 Mar 2025 15:29:23 +1300 Subject: [PATCH 121/143] Force clear timeout to ensure it's not set --- app/cli.php | 1 - app/worker.php | 5 ++- composer.json | 6 +-- composer.lock | 120 ++++++++++++++++++++++++------------------------- 4 files changed, 67 insertions(+), 65 deletions(-) diff --git a/app/cli.php b/app/cli.php index ce978c6d9d..6de98b28c5 100644 --- a/app/cli.php +++ b/app/cli.php @@ -187,7 +187,6 @@ CLI::setResource('getLogsDB', function (Group $pools, Cache $cache) { $database ->setSharedTables(true) ->setNamespace('logsV1') - ->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS) ->setMaxQueryValues(APP_DATABASE_QUERY_MAX_VALUES); // set tenant diff --git a/app/worker.php b/app/worker.php index f3e0541c9a..b217af074c 100644 --- a/app/worker.php +++ b/app/worker.php @@ -112,6 +112,8 @@ Server::setResource('dbForProject', function (Cache $cache, Registry $register, ->setNamespace('_' . $project->getInternalId()); } + $database->clearTimeout(); + return $database; }, ['cache', 'register', 'message', 'project', 'dbForPlatform']); @@ -173,6 +175,8 @@ Server::setResource('getProjectDB', function (Group $pools, Database $dbForPlatf ->setNamespace('_' . $project->getInternalId()); } + $database->clearTimeout(); + return $database; }; }, ['pools', 'dbForPlatform', 'cache']); @@ -198,7 +202,6 @@ Server::setResource('getLogsDB', function (Group $pools, Cache $cache) { $database ->setSharedTables(true) ->setNamespace('logsV1') - ->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS) ->setMaxQueryValues(APP_DATABASE_QUERY_MAX_VALUES); // set tenant diff --git a/composer.json b/composer.json index c9b057b061..62d565a438 100644 --- a/composer.json +++ b/composer.json @@ -45,13 +45,13 @@ "ext-sockets": "*", "appwrite/php-runtimes": "0.16.*", "appwrite/php-clamav": "2.0.*", - "utopia-php/abuse": "0.51.*", + "utopia-php/abuse": "0.52.*", "utopia-php/analytics": "0.10.*", - "utopia-php/audit": "0.54.0", + "utopia-php/audit": "0.55.*", "utopia-php/cache": "0.12.*", "utopia-php/cli": "0.15.*", "utopia-php/config": "0.2.*", - "utopia-php/database": "0.60.*", + "utopia-php/database": "0.61.*", "utopia-php/domains": "0.5.*", "utopia-php/dsn": "0.2.1", "utopia-php/framework": "0.33.*", diff --git a/composer.lock b/composer.lock index e89d6b53b5..ea13de51d3 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "58ad2e1375ec47d944b96864d4aae63f", + "content-hash": "71622d147e04860529ddba9e230035b8", "packages": [ { "name": "adhocore/jwt", @@ -709,16 +709,16 @@ }, { "name": "google/protobuf", - "version": "v4.30.0", + "version": "v4.30.1", "source": { "type": "git", "url": "https://github.com/protocolbuffers/protobuf-php.git", - "reference": "e1d66682f6836aa87820400f0aa07d9eb566feb6" + "reference": "f29ba8a30dfd940efb3a8a75dc44446539101f24" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/protocolbuffers/protobuf-php/zipball/e1d66682f6836aa87820400f0aa07d9eb566feb6", - "reference": "e1d66682f6836aa87820400f0aa07d9eb566feb6", + "url": "https://api.github.com/repos/protocolbuffers/protobuf-php/zipball/f29ba8a30dfd940efb3a8a75dc44446539101f24", + "reference": "f29ba8a30dfd940efb3a8a75dc44446539101f24", "shasum": "" }, "require": { @@ -747,9 +747,9 @@ "proto" ], "support": { - "source": "https://github.com/protocolbuffers/protobuf-php/tree/v4.30.0" + "source": "https://github.com/protocolbuffers/protobuf-php/tree/v4.30.1" }, - "time": "2025-03-04T22:54:49+00:00" + "time": "2025-03-13T21:08:17+00:00" }, { "name": "jean85/pretty-package-versions", @@ -3364,16 +3364,16 @@ }, { "name": "utopia-php/abuse", - "version": "0.51.0", + "version": "0.52.0", "source": { "type": "git", "url": "https://github.com/utopia-php/abuse.git", - "reference": "661687b03277f1d202a0e8cf9da6e58c97da2b5e" + "reference": "a0d6421e7e5baa3ac02755496dca9fdeaa814b93" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/abuse/zipball/661687b03277f1d202a0e8cf9da6e58c97da2b5e", - "reference": "661687b03277f1d202a0e8cf9da6e58c97da2b5e", + "url": "https://api.github.com/repos/utopia-php/abuse/zipball/a0d6421e7e5baa3ac02755496dca9fdeaa814b93", + "reference": "a0d6421e7e5baa3ac02755496dca9fdeaa814b93", "shasum": "" }, "require": { @@ -3381,7 +3381,7 @@ "ext-pdo": "*", "ext-redis": "*", "php": ">=8.0", - "utopia-php/database": "0.60.*" + "utopia-php/database": "0.*.*" }, "require-dev": { "laravel/pint": "1.*", @@ -3409,9 +3409,9 @@ ], "support": { "issues": "https://github.com/utopia-php/abuse/issues", - "source": "https://github.com/utopia-php/abuse/tree/0.51.0" + "source": "https://github.com/utopia-php/abuse/tree/0.52.0" }, - "time": "2025-02-17T11:10:18+00:00" + "time": "2025-03-06T03:48:29+00:00" }, { "name": "utopia-php/analytics", @@ -3461,21 +3461,21 @@ }, { "name": "utopia-php/audit", - "version": "0.54.0", + "version": "0.55.0", "source": { "type": "git", "url": "https://github.com/utopia-php/audit.git", - "reference": "1b0cb8ac6bfbd7703e3f9a753c6ba59ff1c39975" + "reference": "9f8cfe5fa5d5011b8dbf93b710236dfa91dc5518" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/audit/zipball/1b0cb8ac6bfbd7703e3f9a753c6ba59ff1c39975", - "reference": "1b0cb8ac6bfbd7703e3f9a753c6ba59ff1c39975", + "url": "https://api.github.com/repos/utopia-php/audit/zipball/9f8cfe5fa5d5011b8dbf93b710236dfa91dc5518", + "reference": "9f8cfe5fa5d5011b8dbf93b710236dfa91dc5518", "shasum": "" }, "require": { "php": ">=8.0", - "utopia-php/database": "0.60.*" + "utopia-php/database": "0.*.*" }, "require-dev": { "laravel/pint": "1.*", @@ -3502,9 +3502,9 @@ ], "support": { "issues": "https://github.com/utopia-php/audit/issues", - "source": "https://github.com/utopia-php/audit/tree/0.54.0" + "source": "https://github.com/utopia-php/audit/tree/0.55.0" }, - "time": "2025-02-25T07:21:07+00:00" + "time": "2025-03-06T03:47:47+00:00" }, { "name": "utopia-php/cache", @@ -3705,16 +3705,16 @@ }, { "name": "utopia-php/database", - "version": "0.60.6", + "version": "0.61.1", "source": { "type": "git", "url": "https://github.com/utopia-php/database.git", - "reference": "f3c9aa964b39c6205069f038a26e709a15541406" + "reference": "2e0165bd14a570ec151f400ed381108e81d15b94" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/database/zipball/f3c9aa964b39c6205069f038a26e709a15541406", - "reference": "f3c9aa964b39c6205069f038a26e709a15541406", + "url": "https://api.github.com/repos/utopia-php/database/zipball/2e0165bd14a570ec151f400ed381108e81d15b94", + "reference": "2e0165bd14a570ec151f400ed381108e81d15b94", "shasum": "" }, "require": { @@ -3755,9 +3755,9 @@ ], "support": { "issues": "https://github.com/utopia-php/database/issues", - "source": "https://github.com/utopia-php/database/tree/0.60.6" + "source": "https://github.com/utopia-php/database/tree/0.61.1" }, - "time": "2025-03-05T01:23:14+00:00" + "time": "2025-03-14T01:19:38+00:00" }, { "name": "utopia-php/domains", @@ -4159,16 +4159,16 @@ }, { "name": "utopia-php/migration", - "version": "0.6.20", + "version": "0.6.22", "source": { "type": "git", "url": "https://github.com/utopia-php/migration.git", - "reference": "8c9ba52196f50aaef4aa1903f0d8fe0c8d9997ba" + "reference": "a0269746bd318ff0993f5aa008675b971689d5b5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/migration/zipball/8c9ba52196f50aaef4aa1903f0d8fe0c8d9997ba", - "reference": "8c9ba52196f50aaef4aa1903f0d8fe0c8d9997ba", + "url": "https://api.github.com/repos/utopia-php/migration/zipball/a0269746bd318ff0993f5aa008675b971689d5b5", + "reference": "a0269746bd318ff0993f5aa008675b971689d5b5", "shasum": "" }, "require": { @@ -4176,7 +4176,7 @@ "ext-curl": "*", "ext-openssl": "*", "php": ">=8.1", - "utopia-php/database": "0.60.*", + "utopia-php/database": "0.61.*", "utopia-php/dsn": "0.2.*", "utopia-php/framework": "0.33.*", "utopia-php/storage": "0.18.*" @@ -4209,9 +4209,9 @@ ], "support": { "issues": "https://github.com/utopia-php/migration/issues", - "source": "https://github.com/utopia-php/migration/tree/0.6.20" + "source": "https://github.com/utopia-php/migration/tree/0.6.22" }, - "time": "2025-02-17T11:02:15+00:00" + "time": "2025-03-13T07:35:55+00:00" }, { "name": "utopia-php/mongo", @@ -4810,16 +4810,16 @@ }, { "name": "utopia-php/vcs", - "version": "0.9.3", + "version": "0.9.4", "source": { "type": "git", "url": "https://github.com/utopia-php/vcs.git", - "reference": "865a00c67e81a20938b883f9aa802303790dd3b5" + "reference": "1a8d280b176acc99ea8d9e7364b8767cbb206b4a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/vcs/zipball/865a00c67e81a20938b883f9aa802303790dd3b5", - "reference": "865a00c67e81a20938b883f9aa802303790dd3b5", + "url": "https://api.github.com/repos/utopia-php/vcs/zipball/1a8d280b176acc99ea8d9e7364b8767cbb206b4a", + "reference": "1a8d280b176acc99ea8d9e7364b8767cbb206b4a", "shasum": "" }, "require": { @@ -4854,9 +4854,9 @@ ], "support": { "issues": "https://github.com/utopia-php/vcs/issues", - "source": "https://github.com/utopia-php/vcs/tree/0.9.3" + "source": "https://github.com/utopia-php/vcs/tree/0.9.4" }, - "time": "2025-02-26T16:33:35+00:00" + "time": "2025-03-13T10:09:45+00:00" }, { "name": "utopia-php/websocket", @@ -5043,16 +5043,16 @@ "packages-dev": [ { "name": "appwrite/sdk-generator", - "version": "0.40.2", + "version": "0.40.7", "source": { "type": "git", "url": "https://github.com/appwrite/sdk-generator.git", - "reference": "56f09482d9e2f223911277ab887f197402708049" + "reference": "9e89b0bc4d8e6c81817d27096629f34a149fa873" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/56f09482d9e2f223911277ab887f197402708049", - "reference": "56f09482d9e2f223911277ab887f197402708049", + "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/9e89b0bc4d8e6c81817d27096629f34a149fa873", + "reference": "9e89b0bc4d8e6c81817d27096629f34a149fa873", "shasum": "" }, "require": { @@ -5088,9 +5088,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/0.40.2" + "source": "https://github.com/appwrite/sdk-generator/tree/0.40.7" }, - "time": "2025-03-06T16:31:03+00:00" + "time": "2025-03-12T08:43:55+00:00" }, { "name": "doctrine/annotations", @@ -5317,16 +5317,16 @@ }, { "name": "laravel/pint", - "version": "v1.21.0", + "version": "v1.21.1", "source": { "type": "git", "url": "https://github.com/laravel/pint.git", - "reference": "531fa0871fbde719c51b12afa3a443b8f4e4b425" + "reference": "c44bffbb2334e90fba560933c45948fa4a3f3e86" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/pint/zipball/531fa0871fbde719c51b12afa3a443b8f4e4b425", - "reference": "531fa0871fbde719c51b12afa3a443b8f4e4b425", + "url": "https://api.github.com/repos/laravel/pint/zipball/c44bffbb2334e90fba560933c45948fa4a3f3e86", + "reference": "c44bffbb2334e90fba560933c45948fa4a3f3e86", "shasum": "" }, "require": { @@ -5337,9 +5337,9 @@ "php": "^8.2.0" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^3.68.5", - "illuminate/view": "^11.42.0", - "larastan/larastan": "^3.0.4", + "friendsofphp/php-cs-fixer": "^3.70.2", + "illuminate/view": "^11.44.1", + "larastan/larastan": "^3.1.0", "laravel-zero/framework": "^11.36.1", "mockery/mockery": "^1.6.12", "nunomaduro/termwind": "^2.3", @@ -5379,7 +5379,7 @@ "issues": "https://github.com/laravel/pint/issues", "source": "https://github.com/laravel/pint" }, - "time": "2025-02-18T03:18:57+00:00" + "time": "2025-03-11T03:22:21+00:00" }, { "name": "matthiasmullie/minify", @@ -5794,16 +5794,16 @@ }, { "name": "phpbench/phpbench", - "version": "1.4.0", + "version": "1.4.1", "source": { "type": "git", "url": "https://github.com/phpbench/phpbench.git", - "reference": "4248817222514421cba466bfa7adc7d8932345d4" + "reference": "78cd98a9aa34e0f8f80ca01972a8b88d2c30194b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpbench/phpbench/zipball/4248817222514421cba466bfa7adc7d8932345d4", - "reference": "4248817222514421cba466bfa7adc7d8932345d4", + "url": "https://api.github.com/repos/phpbench/phpbench/zipball/78cd98a9aa34e0f8f80ca01972a8b88d2c30194b", + "reference": "78cd98a9aa34e0f8f80ca01972a8b88d2c30194b", "shasum": "" }, "require": { @@ -5880,7 +5880,7 @@ ], "support": { "issues": "https://github.com/phpbench/phpbench/issues", - "source": "https://github.com/phpbench/phpbench/tree/1.4.0" + "source": "https://github.com/phpbench/phpbench/tree/1.4.1" }, "funding": [ { @@ -5888,7 +5888,7 @@ "type": "github" } ], - "time": "2025-01-26T19:54:45+00:00" + "time": "2025-03-12T08:01:40+00:00" }, { "name": "phpunit/php-code-coverage", From 246c8ce93945f9a059bbe3decdee55cd48c26d22 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Fri, 14 Mar 2025 15:29:52 +1300 Subject: [PATCH 122/143] Add order on inequality queries --- src/Appwrite/Platform/Workers/Deletes.php | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/Appwrite/Platform/Workers/Deletes.php b/src/Appwrite/Platform/Workers/Deletes.php index 52dc3d73c9..dd219765c2 100644 --- a/src/Appwrite/Platform/Workers/Deletes.php +++ b/src/Appwrite/Platform/Workers/Deletes.php @@ -387,6 +387,7 @@ class Deletes extends Action $query = [ Query::lessThan('accessedAt', $datetime), + Query::orderDesc('accessedAt') ]; $this->deleteByGroup( @@ -420,6 +421,7 @@ class Deletes extends Action // Delete Usage stats from projectDB $this->deleteByGroup('stats', [ Query::lessThan('time', $hourlyUsageRetentionDatetime), + Query::orderDesc('time'), Query::equal('period', ['1h']), ], $dbForProject); @@ -430,6 +432,7 @@ class Deletes extends Action // Delete Usage stats from logsDB $this->deleteByGroup('stats', [ Query::lessThan('time', $hourlyUsageRetentionDatetime), + Query::orderDesc('time'), Query::equal('period', ['1h']), ], $dbForLogs); } @@ -688,9 +691,11 @@ class Deletes extends Action private function deleteExecutionLogs(Document $project, callable $getProjectDB, string $datetime): void { $dbForProject = $getProjectDB($project); + // Delete Executions $this->deleteByGroup('executions', [ - Query::lessThan('$createdAt', $datetime) + Query::lessThan('$createdAt', $datetime), + Query::orderDesc('$createdAt'), ], $dbForProject); } @@ -708,7 +713,8 @@ class Deletes extends Action // Delete Sessions $this->deleteByGroup('sessions', [ - Query::lessThan('$createdAt', $expired) + Query::lessThan('$createdAt', $expired), + Query::orderDesc('$createdAt'), ], $dbForProject); } @@ -722,7 +728,8 @@ class Deletes extends Action { // Delete Dead Realtime Logs $this->deleteByGroup('realtime', [ - Query::lessThan('timestamp', $datetime) + Query::lessThan('timestamp', $datetime), + Query::orderDesc('timestamp'), ], $dbForPlatform); } From d96910138f2da6065e84e565f844685908d9798f Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Fri, 14 Mar 2025 15:40:05 +1300 Subject: [PATCH 123/143] Update lock --- composer.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/composer.lock b/composer.lock index 2ecba25e1f..23937694f8 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "71622d147e04860529ddba9e230035b8", + "content-hash": "44c6436ced36b0b026139edba252052e", "packages": [ { "name": "adhocore/jwt", @@ -8402,7 +8402,7 @@ ], "aliases": [], "minimum-stability": "stable", - "stability-flags": [], + "stability-flags": {}, "prefer-stable": false, "prefer-lowest": false, "platform": { From 817a3808e52bb5a2e66ed02b8148808664c6655a Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Fri, 14 Mar 2025 16:40:57 +1300 Subject: [PATCH 124/143] Add 5 min timeout for workers and tasks DBs --- app/cli.php | 6 +++--- app/init.php | 12 +++++++----- app/worker.php | 5 +++-- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/app/cli.php b/app/cli.php index 6de98b28c5..f080217365 100644 --- a/app/cli.php +++ b/app/cli.php @@ -25,7 +25,7 @@ use Utopia\Queue\Publisher; use Utopia\Registry\Registry; use Utopia\System\System; -// overwriting runtimes to be architectur agnostic for CLI +// Overwriting runtimes to be architecture agnostic for CLI Config::setParam('runtimes', (new Runtimes('v4'))->getAll(supported: false)); // require controllers after overwriting runtimes @@ -43,8 +43,7 @@ CLI::setResource('cache', function ($pools) { $adapters[] = $pools ->get($value) ->pop() - ->getResource() - ; + ->getResource(); } return new Cache(new Sharding($adapters)); @@ -187,6 +186,7 @@ CLI::setResource('getLogsDB', function (Group $pools, Cache $cache) { $database ->setSharedTables(true) ->setNamespace('logsV1') + ->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_TASK) ->setMaxQueryValues(APP_DATABASE_QUERY_MAX_VALUES); // set tenant diff --git a/app/init.php b/app/init.php index 9a499412db..6eb2baafd8 100644 --- a/app/init.php +++ b/app/init.php @@ -134,7 +134,9 @@ const APP_DATABASE_ATTRIBUTE_URL = 'url'; const APP_DATABASE_ATTRIBUTE_INT_RANGE = 'intRange'; const APP_DATABASE_ATTRIBUTE_FLOAT_RANGE = 'floatRange'; const APP_DATABASE_ATTRIBUTE_STRING_MAX_LENGTH = 1_073_741_824; // 2^32 bits / 4 bits per char -const APP_DATABASE_TIMEOUT_MILLISECONDS = 15_000; +const APP_DATABASE_TIMEOUT_MILLISECONDS_API = 15 * 1000; // 15 seconds +const APP_DATABASE_TIMEOUT_MILLISECONDS_WORKER = 300 * 1000; // 5 minutes +const APP_DATABASE_TIMEOUT_MILLISECONDS_TASK = 300 * 1000; // 5 minutes const APP_DATABASE_QUERY_MAX_VALUES = 500; const APP_STORAGE_UPLOADS = '/storage/uploads'; const APP_STORAGE_FUNCTIONS = '/storage/functions'; @@ -1434,7 +1436,7 @@ App::setResource('dbForProject', function (Group $pools, Database $dbForPlatform $database ->setMetadata('host', \gethostname()) ->setMetadata('project', $project->getId()) - ->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS) + ->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_API) ->setMaxQueryValues(APP_DATABASE_QUERY_MAX_VALUES); $sharedTables = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', '')); @@ -1466,7 +1468,7 @@ App::setResource('dbForPlatform', function (Group $pools, Cache $cache) { ->setNamespace('_console') ->setMetadata('host', \gethostname()) ->setMetadata('project', 'console') - ->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS) + ->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_API) ->setMaxQueryValues(APP_DATABASE_QUERY_MAX_VALUES); return $database; @@ -1491,7 +1493,7 @@ App::setResource('getProjectDB', function (Group $pools, Database $dbForPlatform $database ->setMetadata('host', \gethostname()) ->setMetadata('project', $project->getId()) - ->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS) + ->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_API) ->setMaxQueryValues(APP_DATABASE_QUERY_MAX_VALUES); $sharedTables = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', '')); @@ -1549,7 +1551,7 @@ App::setResource('getLogsDB', function (Group $pools, Cache $cache) { $database ->setSharedTables(true) ->setNamespace('logsV1') - ->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS) + ->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_API) ->setMaxQueryValues(APP_DATABASE_QUERY_MAX_VALUES); // set tenant diff --git a/app/worker.php b/app/worker.php index b217af074c..eeefe80000 100644 --- a/app/worker.php +++ b/app/worker.php @@ -112,7 +112,7 @@ Server::setResource('dbForProject', function (Cache $cache, Registry $register, ->setNamespace('_' . $project->getInternalId()); } - $database->clearTimeout(); + $database->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_WORKER); return $database; }, ['cache', 'register', 'message', 'project', 'dbForPlatform']); @@ -175,7 +175,7 @@ Server::setResource('getProjectDB', function (Group $pools, Database $dbForPlatf ->setNamespace('_' . $project->getInternalId()); } - $database->clearTimeout(); + $database->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_WORKER); return $database; }; @@ -202,6 +202,7 @@ Server::setResource('getLogsDB', function (Group $pools, Cache $cache) { $database ->setSharedTables(true) ->setNamespace('logsV1') + ->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_WORKER) ->setMaxQueryValues(APP_DATABASE_QUERY_MAX_VALUES); // set tenant From f6fc425c981916d5b1fccfbe8c26ae8bbb3b2e18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Fri, 14 Mar 2025 09:37:44 +0100 Subject: [PATCH 125/143] Add rule attributes --- app/config/collections/platform.php | 38 ++++++++++++++++++++++++++++- app/controllers/api/functions.php | 2 ++ app/controllers/api/proxy.php | 2 ++ app/controllers/general.php | 10 +++++++- 4 files changed, 50 insertions(+), 2 deletions(-) diff --git a/app/config/collections/platform.php b/app/config/collections/platform.php index 8a46bfd3ec..8725f4b4ce 100644 --- a/app/config/collections/platform.php +++ b/app/config/collections/platform.php @@ -1066,7 +1066,29 @@ return [ 'default' => null, 'array' => false, 'filters' => [], - ] + ], + [ + '$id' => ID::custom('owner'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 16, + 'signed' => true, + 'required' => false, + 'default' => '', // "Appwrite" or empty string + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('region'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 16, + 'signed' => true, + 'required' => true, + 'default' => null, + 'array' => false, + 'filters' => [], + ], ], 'indexes' => [ [ @@ -1111,6 +1133,20 @@ return [ 'lengths' => [], 'orders' => [Database::ORDER_ASC], ], + [ + '$id' => ID::custom('_key_owner'), + 'type' => Database::INDEX_KEY, + 'attributes' => ['owner'], + 'lengths' => [16], + 'orders' => [Database::ORDER_ASC], + ], + [ + '$id' => ID::custom('_key_region'), + 'type' => Database::INDEX_KEY, + 'attributes' => ['region'], + 'lengths' => [16], + 'orders' => [Database::ORDER_ASC], + ], ], ], diff --git a/app/controllers/api/functions.php b/app/controllers/api/functions.php index 583468f6c1..88e1e2ae7b 100644 --- a/app/controllers/api/functions.php +++ b/app/controllers/api/functions.php @@ -387,6 +387,8 @@ App::post('/v1/functions') 'resourceInternalId' => $function->getInternalId(), 'status' => 'verified', 'certificateId' => '', + 'owner' => 'Appwrite', + 'region' => $project->getAttribute('region') ])) ); diff --git a/app/controllers/api/proxy.php b/app/controllers/api/proxy.php index 2567c23be6..393caad93d 100644 --- a/app/controllers/api/proxy.php +++ b/app/controllers/api/proxy.php @@ -130,6 +130,8 @@ App::post('/v1/proxy/rules') 'resourceId' => $resourceId, 'resourceInternalId' => $resourceInternalId, 'certificateId' => '', + 'owner' => '', + 'region' => $project->getAttribute('region') ]); $status = 'created'; diff --git a/app/controllers/general.php b/app/controllers/general.php index d93766a5e7..e3c28f4276 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -582,6 +582,12 @@ App::init() ]); } + $owner = ''; + $functionsDomain = System::getEnv('_APP_DOMAIN_FUNCTIONS', ''); + if (!empty($functionsDomain) && \str_ends_with($domain->get(), $functionsDomain)) { + $owner = 'Appwrite'; + } + if ($domainDocument->isEmpty()) { $domainDocument = new Document([ // TODO: @christyjacob remove once we migrate the rules in 1.7.x @@ -590,7 +596,9 @@ App::init() 'resourceType' => 'api', 'status' => 'verifying', 'projectId' => 'console', - 'projectInternalId' => 'console' + 'projectInternalId' => 'console', + 'owner' => $owner, + 'region' => 'fra' ]); $domainDocument = $dbForPlatform->createDocument('rules', $domainDocument); From 5f2512dc4f31ef8c457b746cd73f87a0788217e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Fri, 14 Mar 2025 12:07:38 +0100 Subject: [PATCH 126/143] code quality improvement --- app/config/console.php | 1 + app/controllers/general.php | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/app/config/console.php b/app/config/console.php index 5c15a7930f..e37c9b7836 100644 --- a/app/config/console.php +++ b/app/config/console.php @@ -27,6 +27,7 @@ $console = [ 'hostname' => 'localhost', ], // Current host is added on app init ], + 'region' => 'fra', 'legalName' => '', 'legalCountry' => '', 'legalState' => '', diff --git a/app/controllers/general.php b/app/controllers/general.php index e3c28f4276..a374aa3c39 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -595,10 +595,10 @@ App::init() 'domain' => $domain->get(), 'resourceType' => 'api', 'status' => 'verifying', - 'projectId' => 'console', - 'projectInternalId' => 'console', + 'projectId' => $console->getId(), + 'projectInternalId' => $console->getInternalId(), 'owner' => $owner, - 'region' => 'fra' + 'region' => $console->getAttribute('region') ]); $domainDocument = $dbForPlatform->createDocument('rules', $domainDocument); From 1933e48d9c6be0b8fac2f0fb0fd960f5d07592a7 Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Sat, 15 Mar 2025 12:00:36 +0100 Subject: [PATCH 127/143] Cleanups --- app/init.php | 1738 +-------------------------------- app/init/config.php | 36 + app/init/constants.php | 189 ++++ app/init/database/filters.php | 397 ++++++++ app/init/database/formats.php | 43 + app/init/locale.php | 26 + app/init/registers.php | 333 +++++++ app/init/resources.php | 732 ++++++++++++++ composer.json | 4 + 9 files changed, 1767 insertions(+), 1731 deletions(-) create mode 100644 app/init/config.php create mode 100644 app/init/constants.php create mode 100644 app/init/database/filters.php create mode 100644 app/init/database/formats.php create mode 100644 app/init/locale.php create mode 100644 app/init/registers.php create mode 100644 app/init/resources.php diff --git a/app/init.php b/app/init.php index b4ab772e0e..3e63062bac 100644 --- a/app/init.php +++ b/app/init.php @@ -18,1051 +18,15 @@ if (\file_exists(__DIR__ . '/../vendor/autoload.php')) { \ini_set('default_socket_timeout', -1); \error_reporting(E_ALL); -use Ahc\Jwt\JWT; -use Ahc\Jwt\JWTException; -use Appwrite\Auth\Auth; -use Appwrite\Event\Audit; -use Appwrite\Event\Build; -use Appwrite\Event\Certificate; -use Appwrite\Event\Database as EventDatabase; -use Appwrite\Event\Delete; -use Appwrite\Event\Event; -use Appwrite\Event\Func; -use Appwrite\Event\Mail; -use Appwrite\Event\Messaging; -use Appwrite\Event\Migration; -use Appwrite\Event\Usage; -use Appwrite\Extend\Exception; -use Appwrite\Functions\Specification; -use Appwrite\GraphQL\Promises\Adapter\Swoole; -use Appwrite\GraphQL\Schema; -use Appwrite\Hooks\Hooks; -use Appwrite\Network\Validator\Email; -use Appwrite\Network\Validator\Origin; -use Appwrite\OpenSSL\OpenSSL; -use Appwrite\URL\URL as AppwriteURL; -use MaxMind\Db\Reader; -use PHPMailer\PHPMailer\PHPMailer; -use Swoole\Database\PDOProxy; -use Utopia\App; -use Utopia\Cache\Adapter\Redis as RedisCache; -use Utopia\Cache\Adapter\Sharding; -use Utopia\Cache\Cache; -use Utopia\CLI\Console; -use Utopia\Config\Config; -use Utopia\Database\Adapter\MariaDB; -use Utopia\Database\Adapter\MySQL; -use Utopia\Database\Adapter\SQL; -use Utopia\Database\Database; -use Utopia\Database\Document; -use Utopia\Database\Helpers\ID; -use Utopia\Database\Query; -use Utopia\Database\Validator\Authorization; -use Utopia\Database\Validator\Datetime as DatetimeValidator; -use Utopia\Database\Validator\Structure; -use Utopia\Domains\Validator\PublicDomain; -use Utopia\DSN\DSN; -use Utopia\Locale\Locale; -use Utopia\Logger\Adapter\AppSignal; -use Utopia\Logger\Adapter\LogOwl; -use Utopia\Logger\Adapter\Raygun; -use Utopia\Logger\Adapter\Sentry; -use Utopia\Logger\Log; -use Utopia\Logger\Logger; -use Utopia\Pools\Group; -use Utopia\Pools\Pool; -use Utopia\Queue; -use Utopia\Queue\Connection; -use Utopia\Registry\Registry; -use Utopia\Storage\Device; -use Utopia\Storage\Device\Backblaze; -use Utopia\Storage\Device\DOSpaces; -use Utopia\Storage\Device\Linode; -use Utopia\Storage\Device\Local; -use Utopia\Storage\Device\S3; -use Utopia\Storage\Device\Wasabi; -use Utopia\Storage\Storage; use Utopia\System\System; -use Utopia\Validator\Hostname; -use Utopia\Validator\IP; -use Utopia\Validator\Range; -use Utopia\Validator\URL; -use Utopia\Validator\WhiteList; -use Utopia\VCS\Adapter\Git\GitHub as VcsGitHub; -const APP_NAME = 'Appwrite'; -const APP_DOMAIN = 'appwrite.io'; -const APP_EMAIL_TEAM = 'team@localhost.test'; // Default email address -const APP_EMAIL_SECURITY = ''; // Default security email address -const APP_USERAGENT = APP_NAME . '-Server v%s. Please report abuse at %s'; -const APP_MODE_DEFAULT = 'default'; -const APP_MODE_ADMIN = 'admin'; -const APP_PAGING_LIMIT = 12; -const APP_LIMIT_COUNT = 5000; -const APP_LIMIT_USERS = 10_000; -const APP_LIMIT_USER_PASSWORD_HISTORY = 20; -const APP_LIMIT_USER_SESSIONS_MAX = 100; -const APP_LIMIT_USER_SESSIONS_DEFAULT = 10; -const APP_LIMIT_ANTIVIRUS = 20_000_000; //20MB -const APP_LIMIT_ENCRYPTION = 20_000_000; //20MB -const APP_LIMIT_COMPRESSION = 20_000_000; //20MB -const APP_LIMIT_ARRAY_PARAMS_SIZE = 100; // Default maximum of how many elements can there be in API parameter that expects array value -const APP_LIMIT_ARRAY_LABELS_SIZE = 1000; // Default maximum of how many labels elements can there be in API parameter that expects array value -const APP_LIMIT_ARRAY_ELEMENT_SIZE = 4096; // Default maximum length of element in array parameter represented by maximum URL length. -const APP_LIMIT_SUBQUERY = 1000; -const APP_LIMIT_SUBSCRIBERS_SUBQUERY = 1_000_000; -const APP_LIMIT_WRITE_RATE_DEFAULT = 60; // Default maximum write rate per rate period -const APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT = 60; // Default maximum write rate period in seconds -const APP_LIMIT_LIST_DEFAULT = 25; // Default maximum number of items to return in list API calls -const APP_KEY_ACCESS = 24 * 60 * 60; // 24 hours -const APP_USER_ACCESS = 24 * 60 * 60; // 24 hours -const APP_PROJECT_ACCESS = 24 * 60 * 60; // 24 hours -const APP_CACHE_UPDATE = 24 * 60 * 60; // 24 hours -const APP_CACHE_BUSTER = 4318; -const APP_VERSION_STABLE = '1.6.0'; -const APP_DATABASE_ATTRIBUTE_EMAIL = 'email'; -const APP_DATABASE_ATTRIBUTE_ENUM = 'enum'; -const APP_DATABASE_ATTRIBUTE_IP = 'ip'; -const APP_DATABASE_ATTRIBUTE_DATETIME = 'datetime'; -const APP_DATABASE_ATTRIBUTE_URL = 'url'; -const APP_DATABASE_ATTRIBUTE_INT_RANGE = 'intRange'; -const APP_DATABASE_ATTRIBUTE_FLOAT_RANGE = 'floatRange'; -const APP_DATABASE_ATTRIBUTE_STRING_MAX_LENGTH = 1_073_741_824; // 2^32 bits / 4 bits per char -const APP_DATABASE_TIMEOUT_MILLISECONDS = 15_000; -const APP_STORAGE_UPLOADS = '/storage/uploads'; -const APP_STORAGE_FUNCTIONS = '/storage/functions'; -const APP_STORAGE_BUILDS = '/storage/builds'; -const APP_STORAGE_CACHE = '/storage/cache'; -const APP_STORAGE_CERTIFICATES = '/storage/certificates'; -const APP_STORAGE_CONFIG = '/storage/config'; -const APP_STORAGE_READ_BUFFER = 20 * (1000 * 1000); //20MB other names `APP_STORAGE_MEMORY_LIMIT`, `APP_STORAGE_MEMORY_BUFFER`, `APP_STORAGE_READ_LIMIT`, `APP_STORAGE_BUFFER_LIMIT` -const APP_SOCIAL_TWITTER = 'https://twitter.com/appwrite'; -const APP_SOCIAL_TWITTER_HANDLE = 'appwrite'; -const APP_SOCIAL_FACEBOOK = 'https://www.facebook.com/appwrite.io'; -const APP_SOCIAL_LINKEDIN = 'https://www.linkedin.com/company/appwrite'; -const APP_SOCIAL_INSTAGRAM = 'https://www.instagram.com/appwrite.io'; -const APP_SOCIAL_GITHUB = 'https://github.com/appwrite'; -const APP_SOCIAL_DISCORD = 'https://appwrite.io/discord'; -const APP_SOCIAL_DISCORD_CHANNEL = '564160730845151244'; -const APP_SOCIAL_DEV = 'https://dev.to/appwrite'; -const APP_SOCIAL_STACKSHARE = 'https://stackshare.io/appwrite'; -const APP_SOCIAL_YOUTUBE = 'https://www.youtube.com/c/appwrite?sub_confirmation=1'; -const APP_HOSTNAME_INTERNAL = 'appwrite'; -const APP_FUNCTION_SPECIFICATION_DEFAULT = Specification::S_05VCPU_512MB; -const APP_FUNCTION_CPUS_DEFAULT = 0.5; -const APP_FUNCTION_MEMORY_DEFAULT = 512; - -// Database Reconnect -const DATABASE_RECONNECT_SLEEP = 2; -const DATABASE_RECONNECT_MAX_ATTEMPTS = 10; - -// Database Worker Types -const DATABASE_TYPE_CREATE_ATTRIBUTE = 'createAttribute'; -const DATABASE_TYPE_CREATE_INDEX = 'createIndex'; -const DATABASE_TYPE_DELETE_ATTRIBUTE = 'deleteAttribute'; -const DATABASE_TYPE_DELETE_INDEX = 'deleteIndex'; -const DATABASE_TYPE_DELETE_COLLECTION = 'deleteCollection'; -const DATABASE_TYPE_DELETE_DATABASE = 'deleteDatabase'; - -// Build Worker Types -const BUILD_TYPE_DEPLOYMENT = 'deployment'; -const BUILD_TYPE_RETRY = 'retry'; - -// Deletion Types -const DELETE_TYPE_DATABASES = 'databases'; -const DELETE_TYPE_DOCUMENT = 'document'; -const DELETE_TYPE_COLLECTIONS = 'collections'; -const DELETE_TYPE_PROJECTS = 'projects'; -const DELETE_TYPE_FUNCTIONS = 'functions'; -const DELETE_TYPE_DEPLOYMENTS = 'deployments'; -const DELETE_TYPE_USERS = 'users'; -const DELETE_TYPE_TEAM_PROJECTS = 'teams_projects'; -const DELETE_TYPE_EXECUTIONS = 'executions'; -const DELETE_TYPE_AUDIT = 'audit'; -const DELETE_TYPE_ABUSE = 'abuse'; -const DELETE_TYPE_USAGE = 'usage'; -const DELETE_TYPE_REALTIME = 'realtime'; -const DELETE_TYPE_BUCKETS = 'buckets'; -const DELETE_TYPE_INSTALLATIONS = 'installations'; -const DELETE_TYPE_RULES = 'rules'; -const DELETE_TYPE_SESSIONS = 'sessions'; -const DELETE_TYPE_CACHE_BY_TIMESTAMP = 'cacheByTimeStamp'; -const DELETE_TYPE_CACHE_BY_RESOURCE = 'cacheByResource'; -const DELETE_TYPE_SCHEDULES = 'schedules'; -const DELETE_TYPE_TOPIC = 'topic'; -const DELETE_TYPE_TARGET = 'target'; -const DELETE_TYPE_EXPIRED_TARGETS = 'invalid_targets'; -const DELETE_TYPE_SESSION_TARGETS = 'session_targets'; - -// Message types -const MESSAGE_SEND_TYPE_INTERNAL = 'internal'; -const MESSAGE_SEND_TYPE_EXTERNAL = 'external'; -// Mail Types -const MAIL_TYPE_VERIFICATION = 'verification'; -const MAIL_TYPE_MAGIC_SESSION = 'magicSession'; -const MAIL_TYPE_RECOVERY = 'recovery'; -const MAIL_TYPE_INVITATION = 'invitation'; -const MAIL_TYPE_CERTIFICATE = 'certificate'; -// Auth Types -const APP_AUTH_TYPE_SESSION = 'Session'; -const APP_AUTH_TYPE_JWT = 'JWT'; -const APP_AUTH_TYPE_KEY = 'Key'; -const APP_AUTH_TYPE_ADMIN = 'Admin'; -// Response related -const MAX_OUTPUT_CHUNK_SIZE = 10 * 1024 * 1024; // 10MB -// Function headers -const FUNCTION_ALLOWLIST_HEADERS_REQUEST = ['content-type', 'agent', 'content-length', 'host']; -const FUNCTION_ALLOWLIST_HEADERS_RESPONSE = ['content-type', 'content-length']; -// Message types -const MESSAGE_TYPE_EMAIL = 'email'; -const MESSAGE_TYPE_SMS = 'sms'; -const MESSAGE_TYPE_PUSH = 'push'; -// API key types -const API_KEY_STANDARD = 'standard'; -const API_KEY_DYNAMIC = 'dynamic'; -// Usage metrics -const METRIC_TEAMS = 'teams'; -const METRIC_USERS = 'users'; - -const METRIC_AUTH_METHOD_PHONE = 'auth.method.phone'; -const METRIC_AUTH_METHOD_PHONE_COUNTRY_CODE = METRIC_AUTH_METHOD_PHONE . '.{countryCode}'; -const METRIC_MESSAGES = 'messages'; -const METRIC_MESSAGES_SENT = METRIC_MESSAGES . '.sent'; -const METRIC_MESSAGES_FAILED = METRIC_MESSAGES . '.failed'; -const METRIC_MESSAGES_TYPE = METRIC_MESSAGES . '.{type}'; -const METRIC_MESSAGES_TYPE_SENT = METRIC_MESSAGES . '.{type}.sent'; -const METRIC_MESSAGES_TYPE_FAILED = METRIC_MESSAGES . '.{type}.failed'; -const METRIC_MESSAGES_TYPE_PROVIDER = METRIC_MESSAGES . '.{type}.{provider}'; -const METRIC_MESSAGES_TYPE_PROVIDER_SENT = METRIC_MESSAGES . '.{type}.{provider}.sent'; -const METRIC_MESSAGES_TYPE_PROVIDER_FAILED = METRIC_MESSAGES . '.{type}.{provider}.failed'; -const METRIC_SESSIONS = 'sessions'; -const METRIC_DATABASES = 'databases'; -const METRIC_COLLECTIONS = 'collections'; -const METRIC_DATABASE_ID_COLLECTIONS = '{databaseInternalId}.collections'; -const METRIC_DOCUMENTS = 'documents'; -const METRIC_DATABASE_ID_DOCUMENTS = '{databaseInternalId}.documents'; -const METRIC_DATABASE_ID_COLLECTION_ID_DOCUMENTS = '{databaseInternalId}.{collectionInternalId}.documents'; -const METRIC_BUCKETS = 'buckets'; -const METRIC_FILES = 'files'; -const METRIC_FILES_STORAGE = 'files.storage'; -const METRIC_BUCKET_ID_FILES = '{bucketInternalId}.files'; -const METRIC_BUCKET_ID_FILES_STORAGE = '{bucketInternalId}.files.storage'; -const METRIC_FUNCTIONS = 'functions'; -const METRIC_DEPLOYMENTS = 'deployments'; -const METRIC_DEPLOYMENTS_STORAGE = 'deployments.storage'; -const METRIC_BUILDS = 'builds'; -const METRIC_BUILDS_SUCCESS = 'builds.success'; -const METRIC_BUILDS_FAILED = 'builds.failed'; -const METRIC_BUILDS_STORAGE = 'builds.storage'; -const METRIC_BUILDS_COMPUTE = 'builds.compute'; -const METRIC_BUILDS_COMPUTE_SUCCESS = 'builds.compute.success'; -const METRIC_BUILDS_COMPUTE_FAILED = 'builds.compute.failed'; -const METRIC_BUILDS_MB_SECONDS = 'builds.mbSeconds'; -const METRIC_FUNCTION_ID_BUILDS = '{functionInternalId}.builds'; -const METRIC_FUNCTION_ID_BUILDS_SUCCESS = '{functionInternalId}.builds.success'; -const METRIC_FUNCTION_ID_BUILDS_FAILED = '{functionInternalId}.builds.failed'; -const METRIC_FUNCTION_ID_BUILDS_STORAGE = '{functionInternalId}.builds.storage'; -const METRIC_FUNCTION_ID_BUILDS_COMPUTE = '{functionInternalId}.builds.compute'; -const METRIC_FUNCTION_ID_BUILDS_COMPUTE_SUCCESS = '{functionInternalId}.builds.compute.success'; -const METRIC_FUNCTION_ID_BUILDS_COMPUTE_FAILED = '{functionInternalId}.builds.compute.failed'; -const METRIC_FUNCTION_ID_DEPLOYMENTS = '{resourceType}.{resourceInternalId}.deployments'; -const METRIC_FUNCTION_ID_DEPLOYMENTS_STORAGE = '{resourceType}.{resourceInternalId}.deployments.storage'; -const METRIC_FUNCTION_ID_BUILDS_MB_SECONDS = '{functionInternalId}.builds.mbSeconds'; -const METRIC_EXECUTIONS = 'executions'; -const METRIC_EXECUTIONS_COMPUTE = 'executions.compute'; -const METRIC_EXECUTIONS_MB_SECONDS = 'executions.mbSeconds'; -const METRIC_FUNCTION_ID_EXECUTIONS = '{functionInternalId}.executions'; -const METRIC_FUNCTION_ID_EXECUTIONS_COMPUTE = '{functionInternalId}.executions.compute'; -const METRIC_FUNCTION_ID_EXECUTIONS_MB_SECONDS = '{functionInternalId}.executions.mbSeconds'; -const METRIC_NETWORK_REQUESTS = 'network.requests'; -const METRIC_NETWORK_INBOUND = 'network.inbound'; -const METRIC_NETWORK_OUTBOUND = 'network.outbound'; - -$register = new Registry(); - -App::setMode(System::getEnv('_APP_ENV', App::MODE_TYPE_PRODUCTION)); - -if (!App::isProduction()) { - // Allow specific domains to skip public domain validation in dev environment - // Useful for existing tests involving webhooks - PublicDomain::allow(['request-catcher']); -} - -/* - * ENV vars - */ -Config::load('events', __DIR__ . '/config/events.php'); -Config::load('auth', __DIR__ . '/config/auth.php'); -Config::load('apis', __DIR__ . '/config/apis.php'); // List of APIs -Config::load('errors', __DIR__ . '/config/errors.php'); -Config::load('oAuthProviders', __DIR__ . '/config/oAuthProviders.php'); -Config::load('platforms', __DIR__ . '/config/platforms.php'); -Config::load('collections', __DIR__ . '/config/collections.php'); -Config::load('runtimes', __DIR__ . '/config/runtimes.php'); -Config::load('runtimes-v2', __DIR__ . '/config/runtimes-v2.php'); -Config::load('usage', __DIR__ . '/config/usage.php'); -Config::load('roles', __DIR__ . '/config/roles.php'); // User roles and scopes -Config::load('scopes', __DIR__ . '/config/scopes.php'); // User roles and scopes -Config::load('services', __DIR__ . '/config/services.php'); // List of services -Config::load('variables', __DIR__ . '/config/variables.php'); // List of env variables -Config::load('regions', __DIR__ . '/config/regions.php'); // List of available regions -Config::load('avatar-browsers', __DIR__ . '/config/avatars/browsers.php'); -Config::load('avatar-credit-cards', __DIR__ . '/config/avatars/credit-cards.php'); -Config::load('avatar-flags', __DIR__ . '/config/avatars/flags.php'); -Config::load('locale-codes', __DIR__ . '/config/locale/codes.php'); -Config::load('locale-currencies', __DIR__ . '/config/locale/currencies.php'); -Config::load('locale-eu', __DIR__ . '/config/locale/eu.php'); -Config::load('locale-languages', __DIR__ . '/config/locale/languages.php'); -Config::load('locale-phones', __DIR__ . '/config/locale/phones.php'); -Config::load('locale-countries', __DIR__ . '/config/locale/countries.php'); -Config::load('locale-continents', __DIR__ . '/config/locale/continents.php'); -Config::load('locale-templates', __DIR__ . '/config/locale/templates.php'); -Config::load('storage-logos', __DIR__ . '/config/storage/logos.php'); -Config::load('storage-mimes', __DIR__ . '/config/storage/mimes.php'); -Config::load('storage-inputs', __DIR__ . '/config/storage/inputs.php'); -Config::load('storage-outputs', __DIR__ . '/config/storage/outputs.php'); -Config::load('runtime-specifications', __DIR__ . '/config/runtimes/specifications.php'); -Config::load('function-templates', __DIR__ . '/config/function-templates.php'); - -/** - * New DB Filters - */ -Database::addFilter( - 'casting', - function (mixed $value) { - return json_encode(['value' => $value], JSON_PRESERVE_ZERO_FRACTION); - }, - function (mixed $value) { - if (is_null($value)) { - return; - } - - return json_decode($value, true)['value']; - } -); - -Database::addFilter( - 'enum', - function (mixed $value, Document $attribute) { - if ($attribute->isSet('elements')) { - $attribute->removeAttribute('elements'); - } - - return $value; - }, - function (mixed $value, Document $attribute) { - $formatOptions = \json_decode($attribute->getAttribute('formatOptions', '[]'), true); - if (isset($formatOptions['elements'])) { - $attribute->setAttribute('elements', $formatOptions['elements']); - } - - return $value; - } -); - -Database::addFilter( - 'range', - function (mixed $value, Document $attribute) { - if ($attribute->isSet('min')) { - $attribute->removeAttribute('min'); - } - if ($attribute->isSet('max')) { - $attribute->removeAttribute('max'); - } - - return $value; - }, - function (mixed $value, Document $attribute) { - $formatOptions = json_decode($attribute->getAttribute('formatOptions', '[]'), true); - if (isset($formatOptions['min']) || isset($formatOptions['max'])) { - $attribute - ->setAttribute('min', $formatOptions['min']) - ->setAttribute('max', $formatOptions['max']); - } - - return $value; - } -); - -Database::addFilter( - 'subQueryAttributes', - function (mixed $value) { - return; - }, - function (mixed $value, Document $document, Database $database) { - $attributes = $database->find('attributes', [ - Query::equal('collectionInternalId', [$document->getInternalId()]), - Query::equal('databaseInternalId', [$document->getAttribute('databaseInternalId')]), - Query::limit($database->getLimitForAttributes()), - ]); - - foreach ($attributes as $attribute) { - if ($attribute->getAttribute('type') === Database::VAR_RELATIONSHIP) { - $options = $attribute->getAttribute('options'); - foreach ($options as $key => $value) { - $attribute->setAttribute($key, $value); - } - $attribute->removeAttribute('options'); - } - } - - return $attributes; - } -); - -Database::addFilter( - 'subQueryIndexes', - function (mixed $value) { - return; - }, - function (mixed $value, Document $document, Database $database) { - return $database - ->find('indexes', [ - Query::equal('collectionInternalId', [$document->getInternalId()]), - Query::equal('databaseInternalId', [$document->getAttribute('databaseInternalId')]), - Query::limit($database->getLimitForIndexes()), - ]); - } -); - -Database::addFilter( - 'subQueryPlatforms', - function (mixed $value) { - return; - }, - function (mixed $value, Document $document, Database $database) { - return $database - ->find('platforms', [ - Query::equal('projectInternalId', [$document->getInternalId()]), - Query::limit(APP_LIMIT_SUBQUERY), - ]); - } -); - -Database::addFilter( - 'subQueryKeys', - function (mixed $value) { - return; - }, - function (mixed $value, Document $document, Database $database) { - return $database - ->find('keys', [ - Query::equal('projectInternalId', [$document->getInternalId()]), - Query::limit(APP_LIMIT_SUBQUERY), - ]); - } -); - -Database::addFilter( - 'subQueryWebhooks', - function (mixed $value) { - return; - }, - function (mixed $value, Document $document, Database $database) { - return $database - ->find('webhooks', [ - Query::equal('projectInternalId', [$document->getInternalId()]), - Query::limit(APP_LIMIT_SUBQUERY), - ]); - } -); - -Database::addFilter( - 'subQuerySessions', - function (mixed $value) { - return; - }, - function (mixed $value, Document $document, Database $database) { - return Authorization::skip(fn () => $database->find('sessions', [ - Query::equal('userInternalId', [$document->getInternalId()]), - Query::limit(APP_LIMIT_SUBQUERY), - ])); - } -); - -Database::addFilter( - 'subQueryTokens', - function (mixed $value) { - return; - }, - function (mixed $value, Document $document, Database $database) { - return Authorization::skip(fn () => $database - ->find('tokens', [ - Query::equal('userInternalId', [$document->getInternalId()]), - Query::limit(APP_LIMIT_SUBQUERY), - ])); - } -); - -Database::addFilter( - 'subQueryChallenges', - function (mixed $value) { - return; - }, - function (mixed $value, Document $document, Database $database) { - return Authorization::skip(fn () => $database - ->find('challenges', [ - Query::equal('userInternalId', [$document->getInternalId()]), - Query::limit(APP_LIMIT_SUBQUERY), - ])); - } -); - -Database::addFilter( - 'subQueryAuthenticators', - function (mixed $value) { - return; - }, - function (mixed $value, Document $document, Database $database) { - return Authorization::skip(fn () => $database - ->find('authenticators', [ - Query::equal('userInternalId', [$document->getInternalId()]), - Query::limit(APP_LIMIT_SUBQUERY), - ])); - } -); - -Database::addFilter( - 'subQueryMemberships', - function (mixed $value) { - return; - }, - function (mixed $value, Document $document, Database $database) { - return Authorization::skip(fn () => $database - ->find('memberships', [ - Query::equal('userInternalId', [$document->getInternalId()]), - Query::limit(APP_LIMIT_SUBQUERY), - ])); - } -); - -Database::addFilter( - 'subQueryVariables', - function (mixed $value) { - return; - }, - function (mixed $value, Document $document, Database $database) { - return $database - ->find('variables', [ - Query::equal('resourceInternalId', [$document->getInternalId()]), - Query::equal('resourceType', ['function']), - Query::limit(APP_LIMIT_SUBQUERY), - ]); - } -); - -Database::addFilter( - 'encrypt', - function (mixed $value) { - $key = System::getEnv('_APP_OPENSSL_KEY_V1'); - $iv = OpenSSL::randomPseudoBytes(OpenSSL::cipherIVLength(OpenSSL::CIPHER_AES_128_GCM)); - $tag = null; - - return json_encode([ - 'data' => OpenSSL::encrypt($value, OpenSSL::CIPHER_AES_128_GCM, $key, 0, $iv, $tag), - 'method' => OpenSSL::CIPHER_AES_128_GCM, - 'iv' => \bin2hex($iv), - 'tag' => \bin2hex($tag ?? ''), - 'version' => '1', - ]); - }, - function (mixed $value) { - if (is_null($value)) { - return; - } - $value = json_decode($value, true); - $key = System::getEnv('_APP_OPENSSL_KEY_V' . $value['version']); - - return OpenSSL::decrypt($value['data'], $value['method'], $key, 0, hex2bin($value['iv']), hex2bin($value['tag'])); - } -); - -Database::addFilter( - 'subQueryProjectVariables', - function (mixed $value) { - return; - }, - function (mixed $value, Document $document, Database $database) { - return $database - ->find('variables', [ - Query::equal('resourceType', ['project']), - Query::limit(APP_LIMIT_SUBQUERY) - ]); - } -); - -Database::addFilter( - 'userSearch', - function (mixed $value, Document $user) { - $searchValues = [ - $user->getId(), - $user->getAttribute('email', ''), - $user->getAttribute('name', ''), - $user->getAttribute('phone', '') - ]; - - foreach ($user->getAttribute('labels', []) as $label) { - $searchValues[] = 'label:' . $label; - } - - $search = implode(' ', \array_filter($searchValues)); - - return $search; - }, - function (mixed $value) { - return $value; - } -); - -Database::addFilter( - 'subQueryTargets', - function (mixed $value) { - return; - }, - function (mixed $value, Document $document, Database $database) { - return Authorization::skip(fn () => $database - ->find('targets', [ - Query::equal('userInternalId', [$document->getInternalId()]), - Query::limit(APP_LIMIT_SUBQUERY) - ])); - } -); - -Database::addFilter( - 'subQueryTopicTargets', - function (mixed $value) { - return; - }, - function (mixed $value, Document $document, Database $database) { - $targetIds = Authorization::skip(fn () => \array_map( - fn ($document) => $document->getAttribute('targetInternalId'), - $database->find('subscribers', [ - Query::equal('topicInternalId', [$document->getInternalId()]), - Query::limit(APP_LIMIT_SUBSCRIBERS_SUBQUERY) - ]) - )); - if (\count($targetIds) > 0) { - return $database->skipValidation(fn () => $database->find('targets', [ - Query::equal('$internalId', $targetIds) - ])); - } - return []; - } -); - -Database::addFilter( - 'providerSearch', - function (mixed $value, Document $provider) { - $searchValues = [ - $provider->getId(), - $provider->getAttribute('name', ''), - $provider->getAttribute('provider', ''), - $provider->getAttribute('type', '') - ]; - - $search = \implode(' ', \array_filter($searchValues)); - - return $search; - }, - function (mixed $value) { - return $value; - } -); - -Database::addFilter( - 'topicSearch', - function (mixed $value, Document $topic) { - $searchValues = [ - $topic->getId(), - $topic->getAttribute('name', ''), - $topic->getAttribute('description', ''), - ]; - - $search = \implode(' ', \array_filter($searchValues)); - - return $search; - }, - function (mixed $value) { - return $value; - } -); - -Database::addFilter( - 'messageSearch', - function (mixed $value, Document $message) { - $searchValues = [ - $message->getId(), - $message->getAttribute('description', ''), - $message->getAttribute('status', ''), - ]; - - $data = \json_decode($message->getAttribute('data', []), true); - $providerType = $message->getAttribute('providerType', ''); - - if ($providerType === MESSAGE_TYPE_EMAIL) { - $searchValues = \array_merge($searchValues, [$data['subject'], MESSAGE_TYPE_EMAIL]); - } elseif ($providerType === MESSAGE_TYPE_SMS) { - $searchValues = \array_merge($searchValues, [$data['content'], MESSAGE_TYPE_SMS]); - } else { - $searchValues = \array_merge($searchValues, [$data['title'], MESSAGE_TYPE_PUSH]); - } - - $search = \implode(' ', \array_filter($searchValues)); - - return $search; - }, - function (mixed $value) { - return $value; - } -); - -/** - * DB Formats - */ -Structure::addFormat(APP_DATABASE_ATTRIBUTE_EMAIL, function () { - return new Email(); -}, Database::VAR_STRING); - -Structure::addFormat(APP_DATABASE_ATTRIBUTE_DATETIME, function () { - return new DatetimeValidator(); -}, Database::VAR_DATETIME); - -Structure::addFormat(APP_DATABASE_ATTRIBUTE_ENUM, function ($attribute) { - $elements = $attribute['formatOptions']['elements']; - return new WhiteList($elements, true); -}, Database::VAR_STRING); - -Structure::addFormat(APP_DATABASE_ATTRIBUTE_IP, function () { - return new IP(); -}, Database::VAR_STRING); - -Structure::addFormat(APP_DATABASE_ATTRIBUTE_URL, function () { - return new URL(); -}, Database::VAR_STRING); - -Structure::addFormat(APP_DATABASE_ATTRIBUTE_INT_RANGE, function ($attribute) { - $min = $attribute['formatOptions']['min'] ?? -INF; - $max = $attribute['formatOptions']['max'] ?? INF; - return new Range($min, $max, Range::TYPE_INTEGER); -}, Database::VAR_INTEGER); - -Structure::addFormat(APP_DATABASE_ATTRIBUTE_FLOAT_RANGE, function ($attribute) { - $min = $attribute['formatOptions']['min'] ?? -INF; - $max = $attribute['formatOptions']['max'] ?? INF; - return new Range($min, $max, Range::TYPE_FLOAT); -}, Database::VAR_FLOAT); - -/* - * Registry - */ -$register->set('logger', function () { - // Register error logger - $providerName = System::getEnv('_APP_LOGGING_PROVIDER', ''); - $providerConfig = System::getEnv('_APP_LOGGING_CONFIG', ''); - - try { - $loggingProvider = new DSN($providerConfig ?? ''); - - $providerName = $loggingProvider->getScheme(); - $providerConfig = match ($providerName) { - 'sentry' => ['key' => $loggingProvider->getPassword(), 'projectId' => $loggingProvider->getUser() ?? '', 'host' => 'https://' . $loggingProvider->getHost()], - 'logowl' => ['ticket' => $loggingProvider->getUser() ?? '', 'host' => $loggingProvider->getHost()], - default => ['key' => $loggingProvider->getHost()], - }; - } catch (Throwable $th) { - // Fallback for older Appwrite versions up to 1.5.x that use _APP_LOGGING_PROVIDER and _APP_LOGGING_CONFIG environment variables - Console::warning('Using deprecated logging configuration. Please update your configuration to use DSN format.' . $th->getMessage()); - $configChunks = \explode(";", $providerConfig); - - $providerConfig = match ($providerName) { - 'sentry' => [ 'key' => $configChunks[0], 'projectId' => $configChunks[1] ?? '', 'host' => '',], - 'logowl' => ['ticket' => $configChunks[0] ?? '', 'host' => ''], - default => ['key' => $providerConfig], - }; - } - - if (empty($providerName) || empty($providerConfig)) { - return; - } - - if (!Logger::hasProvider($providerName)) { - throw new Exception(Exception::GENERAL_SERVER_ERROR, "Logging provider not supported. Logging is disabled"); - } - - try { - $adapter = match ($providerName) { - 'sentry' => new Sentry($providerConfig['projectId'], $providerConfig['key'], $providerConfig['host']), - 'logowl' => new LogOwl($providerConfig['ticket'], $providerConfig['host']), - 'raygun' => new Raygun($providerConfig['key']), - 'appsignal' => new AppSignal($providerConfig['key']), - default => null - }; - } catch (Throwable $th) { - $adapter = null; - } - - if ($adapter === null) { - Console::error("Logging provider not supported. Logging is disabled"); - return; - } - - return new Logger($adapter); -}); - -$register->set('pools', function () { - $group = new Group(); - - $fallbackForDB = 'db_main=' . AppwriteURL::unparse([ - 'scheme' => 'mariadb', - 'host' => System::getEnv('_APP_DB_HOST', 'mariadb'), - 'port' => System::getEnv('_APP_DB_PORT', '3306'), - 'user' => System::getEnv('_APP_DB_USER', ''), - 'pass' => System::getEnv('_APP_DB_PASS', ''), - 'path' => System::getEnv('_APP_DB_SCHEMA', ''), - ]); - $fallbackForRedis = 'redis_main=' . AppwriteURL::unparse([ - 'scheme' => 'redis', - 'host' => System::getEnv('_APP_REDIS_HOST', 'redis'), - 'port' => System::getEnv('_APP_REDIS_PORT', '6379'), - 'user' => System::getEnv('_APP_REDIS_USER', ''), - 'pass' => System::getEnv('_APP_REDIS_PASS', ''), - ]); - - $connections = [ - 'console' => [ - 'type' => 'database', - 'dsns' => System::getEnv('_APP_CONNECTIONS_DB_CONSOLE', $fallbackForDB), - 'multiple' => false, - 'schemes' => ['mariadb', 'mysql'], - ], - 'database' => [ - 'type' => 'database', - 'dsns' => System::getEnv('_APP_CONNECTIONS_DB_PROJECT', $fallbackForDB), - 'multiple' => true, - 'schemes' => ['mariadb', 'mysql'], - ], - 'queue' => [ - 'type' => 'queue', - 'dsns' => System::getEnv('_APP_CONNECTIONS_QUEUE', $fallbackForRedis), - 'multiple' => false, - 'schemes' => ['redis'], - ], - 'pubsub' => [ - 'type' => 'pubsub', - 'dsns' => System::getEnv('_APP_CONNECTIONS_PUBSUB', $fallbackForRedis), - 'multiple' => false, - 'schemes' => ['redis'], - ], - 'cache' => [ - 'type' => 'cache', - 'dsns' => System::getEnv('_APP_CONNECTIONS_CACHE', $fallbackForRedis), - 'multiple' => true, - 'schemes' => ['redis'], - ], - ]; - - $maxConnections = System::getEnv('_APP_CONNECTIONS_MAX', 151); - $instanceConnections = $maxConnections / System::getEnv('_APP_POOL_CLIENTS', 14); - - $multiprocessing = System::getEnv('_APP_SERVER_MULTIPROCESS', 'disabled') === 'enabled'; - - if ($multiprocessing) { - $workerCount = swoole_cpu_num() * intval(System::getEnv('_APP_WORKER_PER_CORE', 6)); - } else { - $workerCount = 1; - } - - if ($workerCount > $instanceConnections) { - throw new \Exception('Pool size is too small. Increase the number of allowed database connections or decrease the number of workers.', 500); - } - - $poolSize = (int)($instanceConnections / $workerCount); - - foreach ($connections as $key => $connection) { - $type = $connection['type'] ?? ''; - $multiple = $connection['multiple'] ?? false; - $schemes = $connection['schemes'] ?? []; - $config = []; - $dsns = explode(',', $connection['dsns'] ?? ''); - foreach ($dsns as &$dsn) { - $dsn = explode('=', $dsn); - $name = ($multiple) ? $key . '_' . $dsn[0] : $key; - $dsn = $dsn[1] ?? ''; - $config[] = $name; - if (empty($dsn)) { - //throw new Exception(Exception::GENERAL_SERVER_ERROR, "Missing value for DSN connection in {$key}"); - continue; - } - - $dsn = new DSN($dsn); - $dsnHost = $dsn->getHost(); - $dsnPort = $dsn->getPort(); - $dsnUser = $dsn->getUser(); - $dsnPass = $dsn->getPassword(); - $dsnScheme = $dsn->getScheme(); - $dsnDatabase = $dsn->getPath(); - - if (!in_array($dsnScheme, $schemes)) { - throw new Exception(Exception::GENERAL_SERVER_ERROR, "Invalid console database scheme"); - } - - /** - * Get Resource - * - * Creation could be reused across connection types like database, cache, queue, etc. - * - * Resource assignment to an adapter will happen below. - */ - $resource = match ($dsnScheme) { - 'mysql', - 'mariadb' => function () use ($dsnHost, $dsnPort, $dsnUser, $dsnPass, $dsnDatabase) { - return new PDOProxy(function () use ($dsnHost, $dsnPort, $dsnUser, $dsnPass, $dsnDatabase) { - return new PDO("mysql:host={$dsnHost};port={$dsnPort};dbname={$dsnDatabase};charset=utf8mb4", $dsnUser, $dsnPass, array( - PDO::ATTR_TIMEOUT => 3, // Seconds - PDO::ATTR_PERSISTENT => true, - PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, - PDO::ATTR_EMULATE_PREPARES => true, - PDO::ATTR_STRINGIFY_FETCHES => true - )); - }); - }, - 'redis' => function () use ($dsnHost, $dsnPort, $dsnPass) { - $redis = new Redis(); - @$redis->pconnect($dsnHost, (int)$dsnPort); - if ($dsnPass) { - $redis->auth($dsnPass); - } - $redis->setOption(Redis::OPT_READ_TIMEOUT, -1); - - return $redis; - }, - default => throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Invalid scheme'), - }; - - $pool = new Pool($name, $poolSize, function () use ($type, $resource, $dsn) { - // Get Adapter - switch ($type) { - case 'database': - $adapter = match ($dsn->getScheme()) { - 'mariadb' => new MariaDB($resource()), - 'mysql' => new MySQL($resource()), - default => null - }; - - $adapter->setDatabase($dsn->getPath()); - break; - case 'pubsub': - $adapter = $resource(); - break; - case 'queue': - $adapter = match ($dsn->getScheme()) { - 'redis' => new Queue\Connection\Redis($dsn->getHost(), $dsn->getPort()), - default => null - }; - break; - case 'cache': - $adapter = match ($dsn->getScheme()) { - 'redis' => new RedisCache($resource()), - default => null - }; - break; - - default: - throw new Exception(Exception::GENERAL_SERVER_ERROR, "Server error: Missing adapter implementation."); - } - - return $adapter; - }); - - $group->add($pool); - } - - Config::setParam('pools-' . $key, $config); - } - - return $group; -}); - -$register->set('db', function () { - // This is usually for our workers or CLI commands scope - $dbHost = System::getEnv('_APP_DB_HOST', ''); - $dbPort = System::getEnv('_APP_DB_PORT', ''); - $dbUser = System::getEnv('_APP_DB_USER', ''); - $dbPass = System::getEnv('_APP_DB_PASS', ''); - $dbScheme = System::getEnv('_APP_DB_SCHEMA', ''); - - return new PDO( - "mysql:host={$dbHost};port={$dbPort};dbname={$dbScheme};charset=utf8mb4", - $dbUser, - $dbPass, - SQL::getPDOAttributes() - ); -}); - -$register->set('smtp', function () { - $mail = new PHPMailer(true); - - $mail->isSMTP(); - - $username = System::getEnv('_APP_SMTP_USERNAME'); - $password = System::getEnv('_APP_SMTP_PASSWORD'); - - $mail->XMailer = 'Appwrite Mailer'; - $mail->Host = System::getEnv('_APP_SMTP_HOST', 'smtp'); - $mail->Port = System::getEnv('_APP_SMTP_PORT', 25); - $mail->SMTPAuth = !empty($username) && !empty($password); - $mail->Username = $username; - $mail->Password = $password; - $mail->SMTPSecure = System::getEnv('_APP_SMTP_SECURE', ''); - $mail->SMTPAutoTLS = false; - $mail->CharSet = 'UTF-8'; - - $from = \urldecode(System::getEnv('_APP_SYSTEM_EMAIL_NAME', APP_NAME . ' Server')); - $email = System::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM); - - $mail->setFrom($email, $from); - $mail->addReplyTo($email, $from); - - $mail->isHTML(true); - - return $mail; -}); -$register->set('geodb', function () { - return new Reader(__DIR__ . '/assets/dbip/dbip-country-lite-2024-09.mmdb'); -}); -$register->set('passwordsDictionary', function () { - $content = \file_get_contents(__DIR__ . '/assets/security/10k-common-passwords'); - $content = explode("\n", $content); - $content = array_flip($content); - return $content; -}); -$register->set('promiseAdapter', function () { - return new Swoole(); -}); -$register->set('hooks', function () { - return new Hooks(); -}); -/* - * Localization - */ -Locale::$exceptions = false; - -$locales = Config::getParam('locale-codes', []); - -foreach ($locales as $locale) { - $code = $locale['code']; - - $path = __DIR__ . '/config/locale/translations/' . $code . '.json'; - - if (!\file_exists($path)) { - $path = __DIR__ . '/config/locale/translations/' . \substr($code, 0, 2) . '.json'; // if `ar-ae` doesn't exist, look for `ar` - if (!\file_exists($path)) { - $path = __DIR__ . '/config/locale/translations/en.json'; // if none translation exists, use default from `en.json` - } - } - - Locale::setLanguageFromJSON($code, $path); -} +require_once __DIR__ . '/init/constants.php'; +require_once __DIR__ . '/init/config.php'; +require_once __DIR__ . '/init/database/filters.php'; +require_once __DIR__ . '/init/database/formats.php'; +require_once __DIR__ . '/init/locale.php'; +require_once __DIR__ . '/init/registers.php'; +require_once __DIR__ . '/init/resources.php'; \stream_context_set_default([ // Set global user agent and http settings 'http' => [ @@ -1075,691 +39,3 @@ foreach ($locales as $locale) { 'timeout' => 2, ], ]); - -// Runtime Execution -App::setResource('log', fn () => new Log()); -App::setResource('logger', function ($register) { - return $register->get('logger'); -}, ['register']); - -App::setResource('hooks', function ($register) { - return $register->get('hooks'); -}, ['register']); - -App::setResource('register', fn () => $register); -App::setResource('locale', fn () => new Locale(System::getEnv('_APP_LOCALE', 'en'))); - -App::setResource('localeCodes', function () { - return array_map(fn ($locale) => $locale['code'], Config::getParam('locale-codes', [])); -}); - -// Queues -App::setResource('queue', function (Group $pools) { - return $pools->get('queue')->pop()->getResource(); -}, ['pools']); -App::setResource('queueForMessaging', function (Connection $queue) { - return new Messaging($queue); -}, ['queue']); -App::setResource('queueForMails', function (Connection $queue) { - return new Mail($queue); -}, ['queue']); -App::setResource('queueForBuilds', function (Connection $queue) { - return new Build($queue); -}, ['queue']); -App::setResource('queueForDatabase', function (Connection $queue) { - return new EventDatabase($queue); -}, ['queue']); -App::setResource('queueForDeletes', function (Connection $queue) { - return new Delete($queue); -}, ['queue']); -App::setResource('queueForEvents', function (Connection $queue) { - return new Event($queue); -}, ['queue']); -App::setResource('queueForAudits', function (Connection $queue) { - return new Audit($queue); -}, ['queue']); -App::setResource('queueForFunctions', function (Connection $queue) { - return new Func($queue); -}, ['queue']); -App::setResource('queueForUsage', function (Connection $queue) { - return new Usage($queue); -}, ['queue']); -App::setResource('queueForCertificates', function (Connection $queue) { - return new Certificate($queue); -}, ['queue']); -App::setResource('queueForMigrations', function (Connection $queue) { - return new Migration($queue); -}, ['queue']); -App::setResource('clients', function ($request, $console, $project) { - $console->setAttribute('platforms', [ // Always allow current host - '$collection' => ID::custom('platforms'), - 'name' => 'Current Host', - 'type' => Origin::CLIENT_TYPE_WEB, - 'hostname' => $request->getHostname(), - ], Document::SET_TYPE_APPEND); - - $hostnames = explode(',', System::getEnv('_APP_CONSOLE_HOSTNAMES', '')); - $validator = new Hostname(); - foreach ($hostnames as $hostname) { - $hostname = trim($hostname); - if (!$validator->isValid($hostname)) { - continue; - } - $console->setAttribute('platforms', [ - '$collection' => ID::custom('platforms'), - 'type' => Origin::CLIENT_TYPE_WEB, - 'name' => $hostname, - 'hostname' => $hostname, - ], Document::SET_TYPE_APPEND); - } - - /** - * Get All verified client URLs for both console and current projects - * + Filter for duplicated entries - */ - $clientsConsole = \array_map( - fn ($node) => $node['hostname'], - \array_filter( - $console->getAttribute('platforms', []), - fn ($node) => (isset($node['type']) && ($node['type'] === Origin::CLIENT_TYPE_WEB) && !empty($node['hostname'])) - ) - ); - - $clients = $clientsConsole; - $platforms = $project->getAttribute('platforms', []); - - foreach ($platforms as $node) { - if ( - isset($node['type']) && - ($node['type'] === Origin::CLIENT_TYPE_WEB || - $node['type'] === Origin::CLIENT_TYPE_FLUTTER_WEB) && - !empty($node['hostname']) - ) { - $clients[] = $node['hostname']; - } - } - - return \array_unique($clients); -}, ['request', 'console', 'project']); - -App::setResource('user', function ($mode, $project, $console, $request, $response, $dbForProject, $dbForConsole) { - /** @var Appwrite\Utopia\Request $request */ - /** @var Appwrite\Utopia\Response $response */ - /** @var Utopia\Database\Document $project */ - /** @var Utopia\Database\Database $dbForProject */ - /** @var Utopia\Database\Database $dbForConsole */ - /** @var string $mode */ - - Authorization::setDefaultStatus(true); - - Auth::setCookieName('a_session_' . $project->getId()); - - if (APP_MODE_ADMIN === $mode) { - Auth::setCookieName('a_session_' . $console->getId()); - } - - $session = Auth::decodeSession( - $request->getCookie( - Auth::$cookieName, // Get sessions - $request->getCookie(Auth::$cookieName . '_legacy', '') - ) - ); - - // Get session from header for SSR clients - if (empty($session['id']) && empty($session['secret'])) { - $sessionHeader = $request->getHeader('x-appwrite-session', ''); - - if (!empty($sessionHeader)) { - $session = Auth::decodeSession($sessionHeader); - } - } - - // Get fallback session from old clients (no SameSite support) or clients who block 3rd-party cookies - if ($response) { - $response->addHeader('X-Debug-Fallback', 'false'); - } - - if (empty($session['id']) && empty($session['secret'])) { - if ($response) { - $response->addHeader('X-Debug-Fallback', 'true'); - } - $fallback = $request->getHeader('x-fallback-cookies', ''); - $fallback = \json_decode($fallback, true); - $session = Auth::decodeSession(((isset($fallback[Auth::$cookieName])) ? $fallback[Auth::$cookieName] : '')); - } - - Auth::$unique = $session['id'] ?? ''; - Auth::$secret = $session['secret'] ?? ''; - - if (APP_MODE_ADMIN !== $mode) { - if ($project->isEmpty()) { - $user = new Document([]); - } else { - if ($project->getId() === 'console') { - $user = $dbForConsole->getDocument('users', Auth::$unique); - } else { - $user = $dbForProject->getDocument('users', Auth::$unique); - } - } - } else { - $user = $dbForConsole->getDocument('users', Auth::$unique); - } - - if ( - $user->isEmpty() // Check a document has been found in the DB - || !Auth::sessionVerify($user->getAttribute('sessions', []), Auth::$secret) - ) { // Validate user has valid login token - $user = new Document([]); - } - - if (APP_MODE_ADMIN === $mode) { - if ($user->find('teamInternalId', $project->getAttribute('teamInternalId'), 'memberships')) { - Authorization::setDefaultStatus(false); // Cancel security segmentation for admin users. - } else { - $user = new Document([]); - } - } - - $authJWT = $request->getHeader('x-appwrite-jwt', ''); - - if (!empty($authJWT) && !$project->isEmpty()) { // JWT authentication - $jwt = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 3600, 0); - - try { - $payload = $jwt->decode($authJWT); - } catch (JWTException $error) { - throw new Exception(Exception::USER_JWT_INVALID, 'Failed to verify JWT. ' . $error->getMessage()); - } - - $jwtUserId = $payload['userId'] ?? ''; - if (!empty($jwtUserId)) { - $user = $dbForProject->getDocument('users', $jwtUserId); - } - - $jwtSessionId = $payload['sessionId'] ?? ''; - if (!empty($jwtSessionId)) { - if (empty($user->find('$id', $jwtSessionId, 'sessions'))) { // Match JWT to active token - $user = new Document([]); - } - } - } - - $dbForProject->setMetadata('user', $user->getId()); - $dbForConsole->setMetadata('user', $user->getId()); - - return $user; -}, ['mode', 'project', 'console', 'request', 'response', 'dbForProject', 'dbForConsole']); - -App::setResource('project', function ($dbForConsole, $request, $console) { - /** @var Appwrite\Utopia\Request $request */ - /** @var Utopia\Database\Database $dbForConsole */ - /** @var Utopia\Database\Document $console */ - - $projectId = $request->getParam('project', $request->getHeader('x-appwrite-project', '')); - - if (empty($projectId) || $projectId === 'console') { - return $console; - } - - $project = Authorization::skip(fn () => $dbForConsole->getDocument('projects', $projectId)); - - return $project; -}, ['dbForConsole', 'request', 'console']); - -App::setResource('session', function (Document $user) { - if ($user->isEmpty()) { - return; - } - - $sessions = $user->getAttribute('sessions', []); - $sessionId = Auth::sessionVerify($user->getAttribute('sessions'), Auth::$secret); - - if (!$sessionId) { - return; - } - - foreach ($sessions as $session) {/** @var Document $session */ - if ($sessionId === $session->getId()) { - return $session; - } - } - - return; -}, ['user']); - -App::setResource('console', function () { - return new Document([ - '$id' => ID::custom('console'), - '$internalId' => ID::custom('console'), - 'name' => 'Appwrite', - '$collection' => ID::custom('projects'), - 'description' => 'Appwrite core engine', - 'logo' => '', - 'teamId' => -1, - 'webhooks' => [], - 'keys' => [], - 'platforms' => [ - [ - '$collection' => ID::custom('platforms'), - 'name' => 'Localhost', - 'type' => Origin::CLIENT_TYPE_WEB, - 'hostname' => 'localhost', - ], // Current host is added on app init - ], - 'legalName' => '', - 'legalCountry' => '', - 'legalState' => '', - 'legalCity' => '', - 'legalAddress' => '', - 'legalTaxId' => '', - 'auths' => [ - 'mockNumbers' => [], - 'invites' => System::getEnv('_APP_CONSOLE_INVITES', 'enabled') === 'enabled', - 'limit' => (System::getEnv('_APP_CONSOLE_WHITELIST_ROOT', 'enabled') === 'enabled') ? 1 : 0, // limit signup to 1 user - 'duration' => Auth::TOKEN_EXPIRATION_LOGIN_LONG, // 1 Year in seconds - 'sessionAlerts' => System::getEnv('_APP_CONSOLE_SESSION_ALERTS', 'disabled') === 'enabled' - ], - 'authWhitelistEmails' => (!empty(System::getEnv('_APP_CONSOLE_WHITELIST_EMAILS', null))) ? \explode(',', System::getEnv('_APP_CONSOLE_WHITELIST_EMAILS', null)) : [], - 'authWhitelistIPs' => (!empty(System::getEnv('_APP_CONSOLE_WHITELIST_IPS', null))) ? \explode(',', System::getEnv('_APP_CONSOLE_WHITELIST_IPS', null)) : [], - 'oAuthProviders' => [ - 'githubEnabled' => true, - 'githubSecret' => System::getEnv('_APP_CONSOLE_GITHUB_SECRET', ''), - 'githubAppid' => System::getEnv('_APP_CONSOLE_GITHUB_APP_ID', '') - ], - ]); -}, []); - -App::setResource('dbForProject', function (Group $pools, Database $dbForConsole, Cache $cache, Document $project) { - if ($project->isEmpty() || $project->getId() === 'console') { - return $dbForConsole; - } - - try { - $dsn = new DSN($project->getAttribute('database')); - } catch (\InvalidArgumentException) { - // TODO: Temporary until all projects are using shared tables - $dsn = new DSN('mysql://' . $project->getAttribute('database')); - } - - $dbAdapter = $pools - ->get($dsn->getHost()) - ->pop() - ->getResource(); - - $database = new Database($dbAdapter, $cache); - - $database - ->setMetadata('host', \gethostname()) - ->setMetadata('project', $project->getId()) - ->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS); - - try { - $dsn = new DSN($project->getAttribute('database')); - } catch (\InvalidArgumentException) { - // TODO: Temporary until all projects are using shared tables - $dsn = new DSN('mysql://' . $project->getAttribute('database')); - } - - if ($dsn->getHost() === System::getEnv('_APP_DATABASE_SHARED_TABLES', '')) { - $database - ->setSharedTables(true) - ->setTenant($project->getInternalId()) - ->setNamespace($dsn->getParam('namespace')); - } else { - $database - ->setSharedTables(false) - ->setTenant(null) - ->setNamespace('_' . $project->getInternalId()); - } - - return $database; -}, ['pools', 'dbForConsole', 'cache', 'project']); - -App::setResource('dbForConsole', function (Group $pools, Cache $cache) { - $dbAdapter = $pools - ->get('console') - ->pop() - ->getResource(); - - $database = new Database($dbAdapter, $cache); - - $database - ->setNamespace('_console') - ->setMetadata('host', \gethostname()) - ->setMetadata('project', 'console') - ->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS); - - return $database; -}, ['pools', 'cache']); - -App::setResource('getProjectDB', function (Group $pools, Database $dbForConsole, $cache) { - $databases = []; // TODO: @Meldiron This should probably be responsibility of utopia-php/pools - - return function (Document $project) use ($pools, $dbForConsole, $cache, &$databases) { - if ($project->isEmpty() || $project->getId() === 'console') { - return $dbForConsole; - } - - try { - $dsn = new DSN($project->getAttribute('database')); - } catch (\InvalidArgumentException) { - // TODO: Temporary until all projects are using shared tables - $dsn = new DSN('mysql://' . $project->getAttribute('database')); - } - - $configure = (function (Database $database) use ($project, $dsn) { - $database - ->setMetadata('host', \gethostname()) - ->setMetadata('project', $project->getId()) - ->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS); - - if ($dsn->getHost() === System::getEnv('_APP_DATABASE_SHARED_TABLES', '')) { - $database - ->setSharedTables(true) - ->setTenant($project->getInternalId()) - ->setNamespace($dsn->getParam('namespace')); - } else { - $database - ->setSharedTables(false) - ->setTenant(null) - ->setNamespace('_' . $project->getInternalId()); - } - }); - - if (isset($databases[$dsn->getHost()])) { - $database = $databases[$dsn->getHost()]; - $configure($database); - return $database; - } - - $dbAdapter = $pools - ->get($dsn->getHost()) - ->pop() - ->getResource(); - - $database = new Database($dbAdapter, $cache); - $databases[$dsn->getHost()] = $database; - $configure($database); - - return $database; - }; -}, ['pools', 'dbForConsole', 'cache']); - -App::setResource('cache', function (Group $pools) { - $list = Config::getParam('pools-cache', []); - $adapters = []; - - foreach ($list as $value) { - $adapters[] = $pools - ->get($value) - ->pop() - ->getResource() - ; - } - - return new Cache(new Sharding($adapters)); -}, ['pools']); - -App::setResource('deviceForLocal', function () { - return new Local(); -}); - -App::setResource('deviceForFiles', function ($project) { - return getDevice(APP_STORAGE_UPLOADS . '/app-' . $project->getId()); -}, ['project']); - -App::setResource('deviceForFunctions', function ($project) { - return getDevice(APP_STORAGE_FUNCTIONS . '/app-' . $project->getId()); -}, ['project']); - -App::setResource('deviceForBuilds', function ($project) { - return getDevice(APP_STORAGE_BUILDS . '/app-' . $project->getId()); -}, ['project']); - -function getDevice($root): Device -{ - $connection = System::getEnv('_APP_CONNECTIONS_STORAGE', ''); - - if (!empty($connection)) { - $acl = 'private'; - $device = Storage::DEVICE_LOCAL; - $accessKey = ''; - $accessSecret = ''; - $bucket = ''; - $region = ''; - - try { - $dsn = new DSN($connection); - $device = $dsn->getScheme(); - $accessKey = $dsn->getUser() ?? ''; - $accessSecret = $dsn->getPassword() ?? ''; - $bucket = $dsn->getPath() ?? ''; - $region = $dsn->getParam('region'); - } catch (\Throwable $e) { - Console::warning($e->getMessage() . 'Invalid DSN. Defaulting to Local device.'); - } - - switch ($device) { - case Storage::DEVICE_S3: - return new S3($root, $accessKey, $accessSecret, $bucket, $region, $acl); - case STORAGE::DEVICE_DO_SPACES: - $device = new DOSpaces($root, $accessKey, $accessSecret, $bucket, $region, $acl); - $device->setHttpVersion(S3::HTTP_VERSION_1_1); - return $device; - case Storage::DEVICE_BACKBLAZE: - return new Backblaze($root, $accessKey, $accessSecret, $bucket, $region, $acl); - case Storage::DEVICE_LINODE: - return new Linode($root, $accessKey, $accessSecret, $bucket, $region, $acl); - case Storage::DEVICE_WASABI: - return new Wasabi($root, $accessKey, $accessSecret, $bucket, $region, $acl); - case Storage::DEVICE_LOCAL: - default: - return new Local($root); - } - } else { - switch (strtolower(System::getEnv('_APP_STORAGE_DEVICE', Storage::DEVICE_LOCAL) ?? '')) { - case Storage::DEVICE_LOCAL: - default: - return new Local($root); - case Storage::DEVICE_S3: - $s3AccessKey = System::getEnv('_APP_STORAGE_S3_ACCESS_KEY', ''); - $s3SecretKey = System::getEnv('_APP_STORAGE_S3_SECRET', ''); - $s3Region = System::getEnv('_APP_STORAGE_S3_REGION', ''); - $s3Bucket = System::getEnv('_APP_STORAGE_S3_BUCKET', ''); - $s3Acl = 'private'; - return new S3($root, $s3AccessKey, $s3SecretKey, $s3Bucket, $s3Region, $s3Acl); - case Storage::DEVICE_DO_SPACES: - $doSpacesAccessKey = System::getEnv('_APP_STORAGE_DO_SPACES_ACCESS_KEY', ''); - $doSpacesSecretKey = System::getEnv('_APP_STORAGE_DO_SPACES_SECRET', ''); - $doSpacesRegion = System::getEnv('_APP_STORAGE_DO_SPACES_REGION', ''); - $doSpacesBucket = System::getEnv('_APP_STORAGE_DO_SPACES_BUCKET', ''); - $doSpacesAcl = 'private'; - $device = new DOSpaces($root, $doSpacesAccessKey, $doSpacesSecretKey, $doSpacesBucket, $doSpacesRegion, $doSpacesAcl); - $device->setHttpVersion(S3::HTTP_VERSION_1_1); - return $device; - case Storage::DEVICE_BACKBLAZE: - $backblazeAccessKey = System::getEnv('_APP_STORAGE_BACKBLAZE_ACCESS_KEY', ''); - $backblazeSecretKey = System::getEnv('_APP_STORAGE_BACKBLAZE_SECRET', ''); - $backblazeRegion = System::getEnv('_APP_STORAGE_BACKBLAZE_REGION', ''); - $backblazeBucket = System::getEnv('_APP_STORAGE_BACKBLAZE_BUCKET', ''); - $backblazeAcl = 'private'; - return new Backblaze($root, $backblazeAccessKey, $backblazeSecretKey, $backblazeBucket, $backblazeRegion, $backblazeAcl); - case Storage::DEVICE_LINODE: - $linodeAccessKey = System::getEnv('_APP_STORAGE_LINODE_ACCESS_KEY', ''); - $linodeSecretKey = System::getEnv('_APP_STORAGE_LINODE_SECRET', ''); - $linodeRegion = System::getEnv('_APP_STORAGE_LINODE_REGION', ''); - $linodeBucket = System::getEnv('_APP_STORAGE_LINODE_BUCKET', ''); - $linodeAcl = 'private'; - return new Linode($root, $linodeAccessKey, $linodeSecretKey, $linodeBucket, $linodeRegion, $linodeAcl); - case Storage::DEVICE_WASABI: - $wasabiAccessKey = System::getEnv('_APP_STORAGE_WASABI_ACCESS_KEY', ''); - $wasabiSecretKey = System::getEnv('_APP_STORAGE_WASABI_SECRET', ''); - $wasabiRegion = System::getEnv('_APP_STORAGE_WASABI_REGION', ''); - $wasabiBucket = System::getEnv('_APP_STORAGE_WASABI_BUCKET', ''); - $wasabiAcl = 'private'; - return new Wasabi($root, $wasabiAccessKey, $wasabiSecretKey, $wasabiBucket, $wasabiRegion, $wasabiAcl); - } - } -} - -App::setResource('mode', function ($request) { - /** @var Appwrite\Utopia\Request $request */ - - /** - * Defines the mode for the request: - * - 'default' => Requests for Client and Server Side - * - 'admin' => Request from the Console on non-console projects - */ - return $request->getParam('mode', $request->getHeader('x-appwrite-mode', APP_MODE_DEFAULT)); -}, ['request']); - -App::setResource('geodb', function ($register) { - /** @var Utopia\Registry\Registry $register */ - return $register->get('geodb'); -}, ['register']); - -App::setResource('passwordsDictionary', function ($register) { - /** @var Utopia\Registry\Registry $register */ - return $register->get('passwordsDictionary'); -}, ['register']); - - -App::setResource('servers', function () { - $platforms = Config::getParam('platforms'); - $server = $platforms[APP_PLATFORM_SERVER]; - - $languages = array_map(function ($language) { - return strtolower($language['name']); - }, $server['sdks']); - - return $languages; -}); - -App::setResource('promiseAdapter', function ($register) { - return $register->get('promiseAdapter'); -}, ['register']); - -App::setResource('schema', function ($utopia, $dbForProject) { - - $complexity = function (int $complexity, array $args) { - $queries = Query::parseQueries($args['queries'] ?? []); - $query = Query::getByType($queries, [Query::TYPE_LIMIT])[0] ?? null; - $limit = $query ? $query->getValue() : APP_LIMIT_LIST_DEFAULT; - - return $complexity * $limit; - }; - - $attributes = function (int $limit, int $offset) use ($dbForProject) { - $attrs = Authorization::skip(fn () => $dbForProject->find('attributes', [ - Query::limit($limit), - Query::offset($offset), - ])); - - return \array_map(function ($attr) { - return $attr->getArrayCopy(); - }, $attrs); - }; - - $urls = [ - 'list' => function (string $databaseId, string $collectionId, array $args) { - return "/v1/databases/$databaseId/collections/$collectionId/documents"; - }, - 'create' => function (string $databaseId, string $collectionId, array $args) { - return "/v1/databases/$databaseId/collections/$collectionId/documents"; - }, - 'read' => function (string $databaseId, string $collectionId, array $args) { - return "/v1/databases/$databaseId/collections/$collectionId/documents/{$args['documentId']}"; - }, - 'update' => function (string $databaseId, string $collectionId, array $args) { - return "/v1/databases/$databaseId/collections/$collectionId/documents/{$args['documentId']}"; - }, - 'delete' => function (string $databaseId, string $collectionId, array $args) { - return "/v1/databases/$databaseId/collections/$collectionId/documents/{$args['documentId']}"; - }, - ]; - - $params = [ - 'list' => function (string $databaseId, string $collectionId, array $args) { - return [ 'queries' => $args['queries']]; - }, - 'create' => function (string $databaseId, string $collectionId, array $args) { - $id = $args['id'] ?? 'unique()'; - $permissions = $args['permissions'] ?? null; - - unset($args['id']); - unset($args['permissions']); - - // Order must be the same as the route params - return [ - 'databaseId' => $databaseId, - 'documentId' => $id, - 'collectionId' => $collectionId, - 'data' => $args, - 'permissions' => $permissions, - ]; - }, - 'update' => function (string $databaseId, string $collectionId, array $args) { - $documentId = $args['id']; - $permissions = $args['permissions'] ?? null; - - unset($args['id']); - unset($args['permissions']); - - // Order must be the same as the route params - return [ - 'databaseId' => $databaseId, - 'collectionId' => $collectionId, - 'documentId' => $documentId, - 'data' => $args, - 'permissions' => $permissions, - ]; - }, - ]; - - return Schema::build( - $utopia, - $complexity, - $attributes, - $urls, - $params, - ); -}, ['utopia', 'dbForProject']); - -App::setResource('contributors', function () { - $path = 'app/config/contributors.json'; - $list = (file_exists($path)) ? json_decode(file_get_contents($path), true) : []; - return $list; -}); - -App::setResource('employees', function () { - $path = 'app/config/employees.json'; - $list = (file_exists($path)) ? json_decode(file_get_contents($path), true) : []; - return $list; -}); - -App::setResource('heroes', function () { - $path = 'app/config/heroes.json'; - $list = (file_exists($path)) ? json_decode(file_get_contents($path), true) : []; - return $list; -}); - -App::setResource('gitHub', function (Cache $cache) { - return new VcsGitHub($cache); -}, ['cache']); - -App::setResource('requestTimestamp', function ($request) { - //TODO: Move this to the Request class itself - $timestampHeader = $request->getHeader('x-appwrite-timestamp'); - $requestTimestamp = null; - if (!empty($timestampHeader)) { - try { - $requestTimestamp = new \DateTime($timestampHeader); - } catch (\Throwable $e) { - throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'Invalid X-Appwrite-Timestamp header value'); - } - } - return $requestTimestamp; -}, ['request']); -App::setResource('plan', function (array $plan = []) { - return []; -}); diff --git a/app/init/config.php b/app/init/config.php new file mode 100644 index 0000000000..72e0e5fd7f --- /dev/null +++ b/app/init/config.php @@ -0,0 +1,36 @@ + $value], JSON_PRESERVE_ZERO_FRACTION); + }, + function (mixed $value) { + if (is_null($value)) { + return; + } + + return json_decode($value, true)['value']; + } +); + +Database::addFilter( + 'enum', + function (mixed $value, Document $attribute) { + if ($attribute->isSet('elements')) { + $attribute->removeAttribute('elements'); + } + + return $value; + }, + function (mixed $value, Document $attribute) { + $formatOptions = \json_decode($attribute->getAttribute('formatOptions', '[]'), true); + if (isset($formatOptions['elements'])) { + $attribute->setAttribute('elements', $formatOptions['elements']); + } + + return $value; + } +); + +Database::addFilter( + 'range', + function (mixed $value, Document $attribute) { + if ($attribute->isSet('min')) { + $attribute->removeAttribute('min'); + } + if ($attribute->isSet('max')) { + $attribute->removeAttribute('max'); + } + + return $value; + }, + function (mixed $value, Document $attribute) { + $formatOptions = json_decode($attribute->getAttribute('formatOptions', '[]'), true); + if (isset($formatOptions['min']) || isset($formatOptions['max'])) { + $attribute + ->setAttribute('min', $formatOptions['min']) + ->setAttribute('max', $formatOptions['max']); + } + + return $value; + } +); + +Database::addFilter( + 'subQueryAttributes', + function (mixed $value) { + return; + }, + function (mixed $value, Document $document, Database $database) { + $attributes = $database->find('attributes', [ + Query::equal('collectionInternalId', [$document->getInternalId()]), + Query::equal('databaseInternalId', [$document->getAttribute('databaseInternalId')]), + Query::limit($database->getLimitForAttributes()), + ]); + + foreach ($attributes as $attribute) { + if ($attribute->getAttribute('type') === Database::VAR_RELATIONSHIP) { + $options = $attribute->getAttribute('options'); + foreach ($options as $key => $value) { + $attribute->setAttribute($key, $value); + } + $attribute->removeAttribute('options'); + } + } + + return $attributes; + } +); + +Database::addFilter( + 'subQueryIndexes', + function (mixed $value) { + return; + }, + function (mixed $value, Document $document, Database $database) { + return $database + ->find('indexes', [ + Query::equal('collectionInternalId', [$document->getInternalId()]), + Query::equal('databaseInternalId', [$document->getAttribute('databaseInternalId')]), + Query::limit($database->getLimitForIndexes()), + ]); + } +); + +Database::addFilter( + 'subQueryPlatforms', + function (mixed $value) { + return; + }, + function (mixed $value, Document $document, Database $database) { + return $database + ->find('platforms', [ + Query::equal('projectInternalId', [$document->getInternalId()]), + Query::limit(APP_LIMIT_SUBQUERY), + ]); + } +); + +Database::addFilter( + 'subQueryKeys', + function (mixed $value) { + return; + }, + function (mixed $value, Document $document, Database $database) { + return $database + ->find('keys', [ + Query::equal('projectInternalId', [$document->getInternalId()]), + Query::limit(APP_LIMIT_SUBQUERY), + ]); + } +); + +Database::addFilter( + 'subQueryWebhooks', + function (mixed $value) { + return; + }, + function (mixed $value, Document $document, Database $database) { + return $database + ->find('webhooks', [ + Query::equal('projectInternalId', [$document->getInternalId()]), + Query::limit(APP_LIMIT_SUBQUERY), + ]); + } +); + +Database::addFilter( + 'subQuerySessions', + function (mixed $value) { + return; + }, + function (mixed $value, Document $document, Database $database) { + return Authorization::skip(fn () => $database->find('sessions', [ + Query::equal('userInternalId', [$document->getInternalId()]), + Query::limit(APP_LIMIT_SUBQUERY), + ])); + } +); + +Database::addFilter( + 'subQueryTokens', + function (mixed $value) { + return; + }, + function (mixed $value, Document $document, Database $database) { + return Authorization::skip(fn () => $database + ->find('tokens', [ + Query::equal('userInternalId', [$document->getInternalId()]), + Query::limit(APP_LIMIT_SUBQUERY), + ])); + } +); + +Database::addFilter( + 'subQueryChallenges', + function (mixed $value) { + return; + }, + function (mixed $value, Document $document, Database $database) { + return Authorization::skip(fn () => $database + ->find('challenges', [ + Query::equal('userInternalId', [$document->getInternalId()]), + Query::limit(APP_LIMIT_SUBQUERY), + ])); + } +); + +Database::addFilter( + 'subQueryAuthenticators', + function (mixed $value) { + return; + }, + function (mixed $value, Document $document, Database $database) { + return Authorization::skip(fn () => $database + ->find('authenticators', [ + Query::equal('userInternalId', [$document->getInternalId()]), + Query::limit(APP_LIMIT_SUBQUERY), + ])); + } +); + +Database::addFilter( + 'subQueryMemberships', + function (mixed $value) { + return; + }, + function (mixed $value, Document $document, Database $database) { + return Authorization::skip(fn () => $database + ->find('memberships', [ + Query::equal('userInternalId', [$document->getInternalId()]), + Query::limit(APP_LIMIT_SUBQUERY), + ])); + } +); + +Database::addFilter( + 'subQueryVariables', + function (mixed $value) { + return; + }, + function (mixed $value, Document $document, Database $database) { + return $database + ->find('variables', [ + Query::equal('resourceInternalId', [$document->getInternalId()]), + Query::equal('resourceType', ['function']), + Query::limit(APP_LIMIT_SUBQUERY), + ]); + } +); + +Database::addFilter( + 'encrypt', + function (mixed $value) { + $key = System::getEnv('_APP_OPENSSL_KEY_V1'); + $iv = OpenSSL::randomPseudoBytes(OpenSSL::cipherIVLength(OpenSSL::CIPHER_AES_128_GCM)); + $tag = null; + + return json_encode([ + 'data' => OpenSSL::encrypt($value, OpenSSL::CIPHER_AES_128_GCM, $key, 0, $iv, $tag), + 'method' => OpenSSL::CIPHER_AES_128_GCM, + 'iv' => \bin2hex($iv), + 'tag' => \bin2hex($tag ?? ''), + 'version' => '1', + ]); + }, + function (mixed $value) { + if (is_null($value)) { + return; + } + $value = json_decode($value, true); + $key = System::getEnv('_APP_OPENSSL_KEY_V' . $value['version']); + + return OpenSSL::decrypt($value['data'], $value['method'], $key, 0, hex2bin($value['iv']), hex2bin($value['tag'])); + } +); + +Database::addFilter( + 'subQueryProjectVariables', + function (mixed $value) { + return; + }, + function (mixed $value, Document $document, Database $database) { + return $database + ->find('variables', [ + Query::equal('resourceType', ['project']), + Query::limit(APP_LIMIT_SUBQUERY) + ]); + } +); + +Database::addFilter( + 'userSearch', + function (mixed $value, Document $user) { + $searchValues = [ + $user->getId(), + $user->getAttribute('email', ''), + $user->getAttribute('name', ''), + $user->getAttribute('phone', '') + ]; + + foreach ($user->getAttribute('labels', []) as $label) { + $searchValues[] = 'label:' . $label; + } + + $search = implode(' ', \array_filter($searchValues)); + + return $search; + }, + function (mixed $value) { + return $value; + } +); + +Database::addFilter( + 'subQueryTargets', + function (mixed $value) { + return; + }, + function (mixed $value, Document $document, Database $database) { + return Authorization::skip(fn () => $database + ->find('targets', [ + Query::equal('userInternalId', [$document->getInternalId()]), + Query::limit(APP_LIMIT_SUBQUERY) + ])); + } +); + +Database::addFilter( + 'subQueryTopicTargets', + function (mixed $value) { + return; + }, + function (mixed $value, Document $document, Database $database) { + $targetIds = Authorization::skip(fn () => \array_map( + fn ($document) => $document->getAttribute('targetInternalId'), + $database->find('subscribers', [ + Query::equal('topicInternalId', [$document->getInternalId()]), + Query::limit(APP_LIMIT_SUBSCRIBERS_SUBQUERY) + ]) + )); + if (\count($targetIds) > 0) { + return $database->skipValidation(fn () => $database->find('targets', [ + Query::equal('$internalId', $targetIds) + ])); + } + return []; + } +); + +Database::addFilter( + 'providerSearch', + function (mixed $value, Document $provider) { + $searchValues = [ + $provider->getId(), + $provider->getAttribute('name', ''), + $provider->getAttribute('provider', ''), + $provider->getAttribute('type', '') + ]; + + $search = \implode(' ', \array_filter($searchValues)); + + return $search; + }, + function (mixed $value) { + return $value; + } +); + +Database::addFilter( + 'topicSearch', + function (mixed $value, Document $topic) { + $searchValues = [ + $topic->getId(), + $topic->getAttribute('name', ''), + $topic->getAttribute('description', ''), + ]; + + $search = \implode(' ', \array_filter($searchValues)); + + return $search; + }, + function (mixed $value) { + return $value; + } +); + +Database::addFilter( + 'messageSearch', + function (mixed $value, Document $message) { + $searchValues = [ + $message->getId(), + $message->getAttribute('description', ''), + $message->getAttribute('status', ''), + ]; + + $data = \json_decode($message->getAttribute('data', []), true); + $providerType = $message->getAttribute('providerType', ''); + + if ($providerType === MESSAGE_TYPE_EMAIL) { + $searchValues = \array_merge($searchValues, [$data['subject'], MESSAGE_TYPE_EMAIL]); + } elseif ($providerType === MESSAGE_TYPE_SMS) { + $searchValues = \array_merge($searchValues, [$data['content'], MESSAGE_TYPE_SMS]); + } else { + $searchValues = \array_merge($searchValues, [$data['title'], MESSAGE_TYPE_PUSH]); + } + + $search = \implode(' ', \array_filter($searchValues)); + + return $search; + }, + function (mixed $value) { + return $value; + } +); diff --git a/app/init/database/formats.php b/app/init/database/formats.php new file mode 100644 index 0000000000..9a22b9ed2c --- /dev/null +++ b/app/init/database/formats.php @@ -0,0 +1,43 @@ +set('logger', function () { + // Register error logger + $providerName = System::getEnv('_APP_LOGGING_PROVIDER', ''); + $providerConfig = System::getEnv('_APP_LOGGING_CONFIG', ''); + + try { + $loggingProvider = new DSN($providerConfig ?? ''); + + $providerName = $loggingProvider->getScheme(); + $providerConfig = match ($providerName) { + 'sentry' => ['key' => $loggingProvider->getPassword(), 'projectId' => $loggingProvider->getUser() ?? '', 'host' => 'https://' . $loggingProvider->getHost()], + 'logowl' => ['ticket' => $loggingProvider->getUser() ?? '', 'host' => $loggingProvider->getHost()], + default => ['key' => $loggingProvider->getHost()], + }; + } catch (Throwable $th) { + // Fallback for older Appwrite versions up to 1.5.x that use _APP_LOGGING_PROVIDER and _APP_LOGGING_CONFIG environment variables + Console::warning('Using deprecated logging configuration. Please update your configuration to use DSN format.' . $th->getMessage()); + $configChunks = \explode(";", $providerConfig); + + $providerConfig = match ($providerName) { + 'sentry' => [ 'key' => $configChunks[0], 'projectId' => $configChunks[1] ?? '', 'host' => '',], + 'logowl' => ['ticket' => $configChunks[0] ?? '', 'host' => ''], + default => ['key' => $providerConfig], + }; + } + + if (empty($providerName) || empty($providerConfig)) { + return; + } + + if (!Logger::hasProvider($providerName)) { + throw new Exception(Exception::GENERAL_SERVER_ERROR, "Logging provider not supported. Logging is disabled"); + } + + try { + $adapter = match ($providerName) { + 'sentry' => new Sentry($providerConfig['projectId'], $providerConfig['key'], $providerConfig['host']), + 'logowl' => new LogOwl($providerConfig['ticket'], $providerConfig['host']), + 'raygun' => new Raygun($providerConfig['key']), + 'appsignal' => new AppSignal($providerConfig['key']), + default => null + }; + } catch (Throwable $th) { + $adapter = null; + } + + if ($adapter === null) { + Console::error("Logging provider not supported. Logging is disabled"); + return; + } + + return new Logger($adapter); +}); + +$register->set('pools', function () { + $group = new Group(); + + $fallbackForDB = 'db_main=' . AppwriteURL::unparse([ + 'scheme' => 'mariadb', + 'host' => System::getEnv('_APP_DB_HOST', 'mariadb'), + 'port' => System::getEnv('_APP_DB_PORT', '3306'), + 'user' => System::getEnv('_APP_DB_USER', ''), + 'pass' => System::getEnv('_APP_DB_PASS', ''), + 'path' => System::getEnv('_APP_DB_SCHEMA', ''), + ]); + $fallbackForRedis = 'redis_main=' . AppwriteURL::unparse([ + 'scheme' => 'redis', + 'host' => System::getEnv('_APP_REDIS_HOST', 'redis'), + 'port' => System::getEnv('_APP_REDIS_PORT', '6379'), + 'user' => System::getEnv('_APP_REDIS_USER', ''), + 'pass' => System::getEnv('_APP_REDIS_PASS', ''), + ]); + + $connections = [ + 'console' => [ + 'type' => 'database', + 'dsns' => System::getEnv('_APP_CONNECTIONS_DB_CONSOLE', $fallbackForDB), + 'multiple' => false, + 'schemes' => ['mariadb', 'mysql'], + ], + 'database' => [ + 'type' => 'database', + 'dsns' => System::getEnv('_APP_CONNECTIONS_DB_PROJECT', $fallbackForDB), + 'multiple' => true, + 'schemes' => ['mariadb', 'mysql'], + ], + 'queue' => [ + 'type' => 'queue', + 'dsns' => System::getEnv('_APP_CONNECTIONS_QUEUE', $fallbackForRedis), + 'multiple' => false, + 'schemes' => ['redis'], + ], + 'pubsub' => [ + 'type' => 'pubsub', + 'dsns' => System::getEnv('_APP_CONNECTIONS_PUBSUB', $fallbackForRedis), + 'multiple' => false, + 'schemes' => ['redis'], + ], + 'cache' => [ + 'type' => 'cache', + 'dsns' => System::getEnv('_APP_CONNECTIONS_CACHE', $fallbackForRedis), + 'multiple' => true, + 'schemes' => ['redis'], + ], + ]; + + $maxConnections = System::getEnv('_APP_CONNECTIONS_MAX', 151); + $instanceConnections = $maxConnections / System::getEnv('_APP_POOL_CLIENTS', 14); + + $multiprocessing = System::getEnv('_APP_SERVER_MULTIPROCESS', 'disabled') === 'enabled'; + + if ($multiprocessing) { + $workerCount = swoole_cpu_num() * intval(System::getEnv('_APP_WORKER_PER_CORE', 6)); + } else { + $workerCount = 1; + } + + if ($workerCount > $instanceConnections) { + throw new \Exception('Pool size is too small. Increase the number of allowed database connections or decrease the number of workers.', 500); + } + + $poolSize = (int)($instanceConnections / $workerCount); + + foreach ($connections as $key => $connection) { + $type = $connection['type'] ?? ''; + $multiple = $connection['multiple'] ?? false; + $schemes = $connection['schemes'] ?? []; + $config = []; + $dsns = explode(',', $connection['dsns'] ?? ''); + foreach ($dsns as &$dsn) { + $dsn = explode('=', $dsn); + $name = ($multiple) ? $key . '_' . $dsn[0] : $key; + $dsn = $dsn[1] ?? ''; + $config[] = $name; + if (empty($dsn)) { + //throw new Exception(Exception::GENERAL_SERVER_ERROR, "Missing value for DSN connection in {$key}"); + continue; + } + + $dsn = new DSN($dsn); + $dsnHost = $dsn->getHost(); + $dsnPort = $dsn->getPort(); + $dsnUser = $dsn->getUser(); + $dsnPass = $dsn->getPassword(); + $dsnScheme = $dsn->getScheme(); + $dsnDatabase = $dsn->getPath(); + + if (!in_array($dsnScheme, $schemes)) { + throw new Exception(Exception::GENERAL_SERVER_ERROR, "Invalid console database scheme"); + } + + /** + * Get Resource + * + * Creation could be reused across connection types like database, cache, queue, etc. + * + * Resource assignment to an adapter will happen below. + */ + $resource = match ($dsnScheme) { + 'mysql', + 'mariadb' => function () use ($dsnHost, $dsnPort, $dsnUser, $dsnPass, $dsnDatabase) { + return new PDOProxy(function () use ($dsnHost, $dsnPort, $dsnUser, $dsnPass, $dsnDatabase) { + return new PDO("mysql:host={$dsnHost};port={$dsnPort};dbname={$dsnDatabase};charset=utf8mb4", $dsnUser, $dsnPass, array( + PDO::ATTR_TIMEOUT => 3, // Seconds + PDO::ATTR_PERSISTENT => true, + PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, + PDO::ATTR_EMULATE_PREPARES => true, + PDO::ATTR_STRINGIFY_FETCHES => true + )); + }); + }, + 'redis' => function () use ($dsnHost, $dsnPort, $dsnPass) { + $redis = new Redis(); + @$redis->pconnect($dsnHost, (int)$dsnPort); + if ($dsnPass) { + $redis->auth($dsnPass); + } + $redis->setOption(Redis::OPT_READ_TIMEOUT, -1); + + return $redis; + }, + default => throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Invalid scheme'), + }; + + $pool = new Pool($name, $poolSize, function () use ($type, $resource, $dsn) { + // Get Adapter + switch ($type) { + case 'database': + $adapter = match ($dsn->getScheme()) { + 'mariadb' => new MariaDB($resource()), + 'mysql' => new MySQL($resource()), + default => null + }; + + $adapter->setDatabase($dsn->getPath()); + break; + case 'pubsub': + $adapter = $resource(); + break; + case 'queue': + $adapter = match ($dsn->getScheme()) { + 'redis' => new Queue\Connection\Redis($dsn->getHost(), $dsn->getPort()), + default => null + }; + break; + case 'cache': + $adapter = match ($dsn->getScheme()) { + 'redis' => new RedisCache($resource()), + default => null + }; + break; + + default: + throw new Exception(Exception::GENERAL_SERVER_ERROR, "Server error: Missing adapter implementation."); + } + + return $adapter; + }); + + $group->add($pool); + } + + Config::setParam('pools-' . $key, $config); + } + + return $group; +}); + +$register->set('db', function () { + // This is usually for our workers or CLI commands scope + $dbHost = System::getEnv('_APP_DB_HOST', ''); + $dbPort = System::getEnv('_APP_DB_PORT', ''); + $dbUser = System::getEnv('_APP_DB_USER', ''); + $dbPass = System::getEnv('_APP_DB_PASS', ''); + $dbScheme = System::getEnv('_APP_DB_SCHEMA', ''); + + return new PDO( + "mysql:host={$dbHost};port={$dbPort};dbname={$dbScheme};charset=utf8mb4", + $dbUser, + $dbPass, + SQL::getPDOAttributes() + ); +}); + +$register->set('smtp', function () { + $mail = new PHPMailer(true); + + $mail->isSMTP(); + + $username = System::getEnv('_APP_SMTP_USERNAME'); + $password = System::getEnv('_APP_SMTP_PASSWORD'); + + $mail->XMailer = 'Appwrite Mailer'; + $mail->Host = System::getEnv('_APP_SMTP_HOST', 'smtp'); + $mail->Port = System::getEnv('_APP_SMTP_PORT', 25); + $mail->SMTPAuth = !empty($username) && !empty($password); + $mail->Username = $username; + $mail->Password = $password; + $mail->SMTPSecure = System::getEnv('_APP_SMTP_SECURE', ''); + $mail->SMTPAutoTLS = false; + $mail->CharSet = 'UTF-8'; + + $from = \urldecode(System::getEnv('_APP_SYSTEM_EMAIL_NAME', APP_NAME . ' Server')); + $email = System::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM); + + $mail->setFrom($email, $from); + $mail->addReplyTo($email, $from); + + $mail->isHTML(true); + + return $mail; +}); + +$register->set('geodb', function () { + return new Reader(__DIR__ . '/../assets/dbip/dbip-country-lite-2024-09.mmdb'); +}); + +$register->set('passwordsDictionary', function () { + $content = \file_get_contents(__DIR__ . '/../assets/security/10k-common-passwords'); + $content = explode("\n", $content); + $content = array_flip($content); + return $content; +}); + +$register->set('promiseAdapter', function () { + return new Swoole(); +}); + +$register->set('hooks', function () { + return new Hooks(); +}); diff --git a/app/init/resources.php b/app/init/resources.php new file mode 100644 index 0000000000..22a4daf333 --- /dev/null +++ b/app/init/resources.php @@ -0,0 +1,732 @@ + new Log()); +App::setResource('logger', function ($register) { + return $register->get('logger'); +}, ['register']); + +App::setResource('hooks', function ($register) { + return $register->get('hooks'); +}, ['register']); + +App::setResource('register', fn () => $register); +App::setResource('locale', fn () => new Locale(System::getEnv('_APP_LOCALE', 'en'))); + +App::setResource('localeCodes', function () { + return array_map(fn ($locale) => $locale['code'], Config::getParam('locale-codes', [])); +}); + +// Queues +App::setResource('queue', function (Group $pools) { + return $pools->get('queue')->pop()->getResource(); +}, ['pools']); +App::setResource('queueForMessaging', function (Connection $queue) { + return new Messaging($queue); +}, ['queue']); +App::setResource('queueForMails', function (Connection $queue) { + return new Mail($queue); +}, ['queue']); +App::setResource('queueForBuilds', function (Connection $queue) { + return new Build($queue); +}, ['queue']); +App::setResource('queueForDatabase', function (Connection $queue) { + return new EventDatabase($queue); +}, ['queue']); +App::setResource('queueForDeletes', function (Connection $queue) { + return new Delete($queue); +}, ['queue']); +App::setResource('queueForEvents', function (Connection $queue) { + return new Event($queue); +}, ['queue']); +App::setResource('queueForAudits', function (Connection $queue) { + return new Audit($queue); +}, ['queue']); +App::setResource('queueForFunctions', function (Connection $queue) { + return new Func($queue); +}, ['queue']); +App::setResource('queueForUsage', function (Connection $queue) { + return new Usage($queue); +}, ['queue']); +App::setResource('queueForCertificates', function (Connection $queue) { + return new Certificate($queue); +}, ['queue']); +App::setResource('queueForMigrations', function (Connection $queue) { + return new Migration($queue); +}, ['queue']); +App::setResource('clients', function ($request, $console, $project) { + $console->setAttribute('platforms', [ // Always allow current host + '$collection' => ID::custom('platforms'), + 'name' => 'Current Host', + 'type' => Origin::CLIENT_TYPE_WEB, + 'hostname' => $request->getHostname(), + ], Document::SET_TYPE_APPEND); + + $hostnames = explode(',', System::getEnv('_APP_CONSOLE_HOSTNAMES', '')); + $validator = new Hostname(); + foreach ($hostnames as $hostname) { + $hostname = trim($hostname); + if (!$validator->isValid($hostname)) { + continue; + } + $console->setAttribute('platforms', [ + '$collection' => ID::custom('platforms'), + 'type' => Origin::CLIENT_TYPE_WEB, + 'name' => $hostname, + 'hostname' => $hostname, + ], Document::SET_TYPE_APPEND); + } + + /** + * Get All verified client URLs for both console and current projects + * + Filter for duplicated entries + */ + $clientsConsole = \array_map( + fn ($node) => $node['hostname'], + \array_filter( + $console->getAttribute('platforms', []), + fn ($node) => (isset($node['type']) && ($node['type'] === Origin::CLIENT_TYPE_WEB) && !empty($node['hostname'])) + ) + ); + + $clients = $clientsConsole; + $platforms = $project->getAttribute('platforms', []); + + foreach ($platforms as $node) { + if ( + isset($node['type']) && + ($node['type'] === Origin::CLIENT_TYPE_WEB || + $node['type'] === Origin::CLIENT_TYPE_FLUTTER_WEB) && + !empty($node['hostname']) + ) { + $clients[] = $node['hostname']; + } + } + + return \array_unique($clients); +}, ['request', 'console', 'project']); + +App::setResource('user', function ($mode, $project, $console, $request, $response, $dbForProject, $dbForConsole) { + /** @var Appwrite\Utopia\Request $request */ + /** @var Appwrite\Utopia\Response $response */ + /** @var Utopia\Database\Document $project */ + /** @var Utopia\Database\Database $dbForProject */ + /** @var Utopia\Database\Database $dbForConsole */ + /** @var string $mode */ + + Authorization::setDefaultStatus(true); + + Auth::setCookieName('a_session_' . $project->getId()); + + if (APP_MODE_ADMIN === $mode) { + Auth::setCookieName('a_session_' . $console->getId()); + } + + $session = Auth::decodeSession( + $request->getCookie( + Auth::$cookieName, // Get sessions + $request->getCookie(Auth::$cookieName . '_legacy', '') + ) + ); + + // Get session from header for SSR clients + if (empty($session['id']) && empty($session['secret'])) { + $sessionHeader = $request->getHeader('x-appwrite-session', ''); + + if (!empty($sessionHeader)) { + $session = Auth::decodeSession($sessionHeader); + } + } + + // Get fallback session from old clients (no SameSite support) or clients who block 3rd-party cookies + if ($response) { + $response->addHeader('X-Debug-Fallback', 'false'); + } + + if (empty($session['id']) && empty($session['secret'])) { + if ($response) { + $response->addHeader('X-Debug-Fallback', 'true'); + } + $fallback = $request->getHeader('x-fallback-cookies', ''); + $fallback = \json_decode($fallback, true); + $session = Auth::decodeSession(((isset($fallback[Auth::$cookieName])) ? $fallback[Auth::$cookieName] : '')); + } + + Auth::$unique = $session['id'] ?? ''; + Auth::$secret = $session['secret'] ?? ''; + + if (APP_MODE_ADMIN !== $mode) { + if ($project->isEmpty()) { + $user = new Document([]); + } else { + if ($project->getId() === 'console') { + $user = $dbForConsole->getDocument('users', Auth::$unique); + } else { + $user = $dbForProject->getDocument('users', Auth::$unique); + } + } + } else { + $user = $dbForConsole->getDocument('users', Auth::$unique); + } + + if ( + $user->isEmpty() // Check a document has been found in the DB + || !Auth::sessionVerify($user->getAttribute('sessions', []), Auth::$secret) + ) { // Validate user has valid login token + $user = new Document([]); + } + + if (APP_MODE_ADMIN === $mode) { + if ($user->find('teamInternalId', $project->getAttribute('teamInternalId'), 'memberships')) { + Authorization::setDefaultStatus(false); // Cancel security segmentation for admin users. + } else { + $user = new Document([]); + } + } + + $authJWT = $request->getHeader('x-appwrite-jwt', ''); + + if (!empty($authJWT) && !$project->isEmpty()) { // JWT authentication + $jwt = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 3600, 0); + + try { + $payload = $jwt->decode($authJWT); + } catch (JWTException $error) { + throw new Exception(Exception::USER_JWT_INVALID, 'Failed to verify JWT. ' . $error->getMessage()); + } + + $jwtUserId = $payload['userId'] ?? ''; + if (!empty($jwtUserId)) { + $user = $dbForProject->getDocument('users', $jwtUserId); + } + + $jwtSessionId = $payload['sessionId'] ?? ''; + if (!empty($jwtSessionId)) { + if (empty($user->find('$id', $jwtSessionId, 'sessions'))) { // Match JWT to active token + $user = new Document([]); + } + } + } + + $dbForProject->setMetadata('user', $user->getId()); + $dbForConsole->setMetadata('user', $user->getId()); + + return $user; +}, ['mode', 'project', 'console', 'request', 'response', 'dbForProject', 'dbForConsole']); + +App::setResource('project', function ($dbForConsole, $request, $console) { + /** @var Appwrite\Utopia\Request $request */ + /** @var Utopia\Database\Database $dbForConsole */ + /** @var Utopia\Database\Document $console */ + + $projectId = $request->getParam('project', $request->getHeader('x-appwrite-project', '')); + + if (empty($projectId) || $projectId === 'console') { + return $console; + } + + $project = Authorization::skip(fn () => $dbForConsole->getDocument('projects', $projectId)); + + return $project; +}, ['dbForConsole', 'request', 'console']); + +App::setResource('session', function (Document $user) { + if ($user->isEmpty()) { + return; + } + + $sessions = $user->getAttribute('sessions', []); + $sessionId = Auth::sessionVerify($user->getAttribute('sessions'), Auth::$secret); + + if (!$sessionId) { + return; + } + + foreach ($sessions as $session) {/** @var Document $session */ + if ($sessionId === $session->getId()) { + return $session; + } + } + + return; +}, ['user']); + +App::setResource('console', function () { + return new Document([ + '$id' => ID::custom('console'), + '$internalId' => ID::custom('console'), + 'name' => 'Appwrite', + '$collection' => ID::custom('projects'), + 'description' => 'Appwrite core engine', + 'logo' => '', + 'teamId' => -1, + 'webhooks' => [], + 'keys' => [], + 'platforms' => [ + [ + '$collection' => ID::custom('platforms'), + 'name' => 'Localhost', + 'type' => Origin::CLIENT_TYPE_WEB, + 'hostname' => 'localhost', + ], // Current host is added on app init + ], + 'legalName' => '', + 'legalCountry' => '', + 'legalState' => '', + 'legalCity' => '', + 'legalAddress' => '', + 'legalTaxId' => '', + 'auths' => [ + 'mockNumbers' => [], + 'invites' => System::getEnv('_APP_CONSOLE_INVITES', 'enabled') === 'enabled', + 'limit' => (System::getEnv('_APP_CONSOLE_WHITELIST_ROOT', 'enabled') === 'enabled') ? 1 : 0, // limit signup to 1 user + 'duration' => Auth::TOKEN_EXPIRATION_LOGIN_LONG, // 1 Year in seconds + 'sessionAlerts' => System::getEnv('_APP_CONSOLE_SESSION_ALERTS', 'disabled') === 'enabled' + ], + 'authWhitelistEmails' => (!empty(System::getEnv('_APP_CONSOLE_WHITELIST_EMAILS', null))) ? \explode(',', System::getEnv('_APP_CONSOLE_WHITELIST_EMAILS', null)) : [], + 'authWhitelistIPs' => (!empty(System::getEnv('_APP_CONSOLE_WHITELIST_IPS', null))) ? \explode(',', System::getEnv('_APP_CONSOLE_WHITELIST_IPS', null)) : [], + 'oAuthProviders' => [ + 'githubEnabled' => true, + 'githubSecret' => System::getEnv('_APP_CONSOLE_GITHUB_SECRET', ''), + 'githubAppid' => System::getEnv('_APP_CONSOLE_GITHUB_APP_ID', '') + ], + ]); +}, []); + +App::setResource('dbForProject', function (Group $pools, Database $dbForConsole, Cache $cache, Document $project) { + if ($project->isEmpty() || $project->getId() === 'console') { + return $dbForConsole; + } + + try { + $dsn = new DSN($project->getAttribute('database')); + } catch (\InvalidArgumentException) { + // TODO: Temporary until all projects are using shared tables + $dsn = new DSN('mysql://' . $project->getAttribute('database')); + } + + $dbAdapter = $pools + ->get($dsn->getHost()) + ->pop() + ->getResource(); + + $database = new Database($dbAdapter, $cache); + + $database + ->setMetadata('host', \gethostname()) + ->setMetadata('project', $project->getId()) + ->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS); + + try { + $dsn = new DSN($project->getAttribute('database')); + } catch (\InvalidArgumentException) { + // TODO: Temporary until all projects are using shared tables + $dsn = new DSN('mysql://' . $project->getAttribute('database')); + } + + if ($dsn->getHost() === System::getEnv('_APP_DATABASE_SHARED_TABLES', '')) { + $database + ->setSharedTables(true) + ->setTenant($project->getInternalId()) + ->setNamespace($dsn->getParam('namespace')); + } else { + $database + ->setSharedTables(false) + ->setTenant(null) + ->setNamespace('_' . $project->getInternalId()); + } + + return $database; +}, ['pools', 'dbForConsole', 'cache', 'project']); + +App::setResource('dbForConsole', function (Group $pools, Cache $cache) { + $dbAdapter = $pools + ->get('console') + ->pop() + ->getResource(); + + $database = new Database($dbAdapter, $cache); + + $database + ->setNamespace('_console') + ->setMetadata('host', \gethostname()) + ->setMetadata('project', 'console') + ->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS); + + return $database; +}, ['pools', 'cache']); + +App::setResource('getProjectDB', function (Group $pools, Database $dbForConsole, $cache) { + $databases = []; // TODO: @Meldiron This should probably be responsibility of utopia-php/pools + + return function (Document $project) use ($pools, $dbForConsole, $cache, &$databases) { + if ($project->isEmpty() || $project->getId() === 'console') { + return $dbForConsole; + } + + try { + $dsn = new DSN($project->getAttribute('database')); + } catch (\InvalidArgumentException) { + // TODO: Temporary until all projects are using shared tables + $dsn = new DSN('mysql://' . $project->getAttribute('database')); + } + + $configure = (function (Database $database) use ($project, $dsn) { + $database + ->setMetadata('host', \gethostname()) + ->setMetadata('project', $project->getId()) + ->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS); + + if ($dsn->getHost() === System::getEnv('_APP_DATABASE_SHARED_TABLES', '')) { + $database + ->setSharedTables(true) + ->setTenant($project->getInternalId()) + ->setNamespace($dsn->getParam('namespace')); + } else { + $database + ->setSharedTables(false) + ->setTenant(null) + ->setNamespace('_' . $project->getInternalId()); + } + }); + + if (isset($databases[$dsn->getHost()])) { + $database = $databases[$dsn->getHost()]; + $configure($database); + return $database; + } + + $dbAdapter = $pools + ->get($dsn->getHost()) + ->pop() + ->getResource(); + + $database = new Database($dbAdapter, $cache); + $databases[$dsn->getHost()] = $database; + $configure($database); + + return $database; + }; +}, ['pools', 'dbForConsole', 'cache']); + +App::setResource('cache', function (Group $pools) { + $list = Config::getParam('pools-cache', []); + $adapters = []; + + foreach ($list as $value) { + $adapters[] = $pools + ->get($value) + ->pop() + ->getResource() + ; + } + + return new Cache(new Sharding($adapters)); +}, ['pools']); + +App::setResource('deviceForLocal', function () { + return new Local(); +}); + +App::setResource('deviceForFiles', function ($project) { + return getDevice(APP_STORAGE_UPLOADS . '/app-' . $project->getId()); +}, ['project']); + +App::setResource('deviceForFunctions', function ($project) { + return getDevice(APP_STORAGE_FUNCTIONS . '/app-' . $project->getId()); +}, ['project']); + +App::setResource('deviceForBuilds', function ($project) { + return getDevice(APP_STORAGE_BUILDS . '/app-' . $project->getId()); +}, ['project']); + +function getDevice($root): Device +{ + $connection = System::getEnv('_APP_CONNECTIONS_STORAGE', ''); + + if (!empty($connection)) { + $acl = 'private'; + $device = Storage::DEVICE_LOCAL; + $accessKey = ''; + $accessSecret = ''; + $bucket = ''; + $region = ''; + + try { + $dsn = new DSN($connection); + $device = $dsn->getScheme(); + $accessKey = $dsn->getUser() ?? ''; + $accessSecret = $dsn->getPassword() ?? ''; + $bucket = $dsn->getPath() ?? ''; + $region = $dsn->getParam('region'); + } catch (\Throwable $e) { + Console::warning($e->getMessage() . 'Invalid DSN. Defaulting to Local device.'); + } + + switch ($device) { + case Storage::DEVICE_S3: + return new S3($root, $accessKey, $accessSecret, $bucket, $region, $acl); + case STORAGE::DEVICE_DO_SPACES: + $device = new DOSpaces($root, $accessKey, $accessSecret, $bucket, $region, $acl); + $device->setHttpVersion(S3::HTTP_VERSION_1_1); + return $device; + case Storage::DEVICE_BACKBLAZE: + return new Backblaze($root, $accessKey, $accessSecret, $bucket, $region, $acl); + case Storage::DEVICE_LINODE: + return new Linode($root, $accessKey, $accessSecret, $bucket, $region, $acl); + case Storage::DEVICE_WASABI: + return new Wasabi($root, $accessKey, $accessSecret, $bucket, $region, $acl); + case Storage::DEVICE_LOCAL: + default: + return new Local($root); + } + } else { + switch (strtolower(System::getEnv('_APP_STORAGE_DEVICE', Storage::DEVICE_LOCAL) ?? '')) { + case Storage::DEVICE_LOCAL: + default: + return new Local($root); + case Storage::DEVICE_S3: + $s3AccessKey = System::getEnv('_APP_STORAGE_S3_ACCESS_KEY', ''); + $s3SecretKey = System::getEnv('_APP_STORAGE_S3_SECRET', ''); + $s3Region = System::getEnv('_APP_STORAGE_S3_REGION', ''); + $s3Bucket = System::getEnv('_APP_STORAGE_S3_BUCKET', ''); + $s3Acl = 'private'; + return new S3($root, $s3AccessKey, $s3SecretKey, $s3Bucket, $s3Region, $s3Acl); + case Storage::DEVICE_DO_SPACES: + $doSpacesAccessKey = System::getEnv('_APP_STORAGE_DO_SPACES_ACCESS_KEY', ''); + $doSpacesSecretKey = System::getEnv('_APP_STORAGE_DO_SPACES_SECRET', ''); + $doSpacesRegion = System::getEnv('_APP_STORAGE_DO_SPACES_REGION', ''); + $doSpacesBucket = System::getEnv('_APP_STORAGE_DO_SPACES_BUCKET', ''); + $doSpacesAcl = 'private'; + $device = new DOSpaces($root, $doSpacesAccessKey, $doSpacesSecretKey, $doSpacesBucket, $doSpacesRegion, $doSpacesAcl); + $device->setHttpVersion(S3::HTTP_VERSION_1_1); + return $device; + case Storage::DEVICE_BACKBLAZE: + $backblazeAccessKey = System::getEnv('_APP_STORAGE_BACKBLAZE_ACCESS_KEY', ''); + $backblazeSecretKey = System::getEnv('_APP_STORAGE_BACKBLAZE_SECRET', ''); + $backblazeRegion = System::getEnv('_APP_STORAGE_BACKBLAZE_REGION', ''); + $backblazeBucket = System::getEnv('_APP_STORAGE_BACKBLAZE_BUCKET', ''); + $backblazeAcl = 'private'; + return new Backblaze($root, $backblazeAccessKey, $backblazeSecretKey, $backblazeBucket, $backblazeRegion, $backblazeAcl); + case Storage::DEVICE_LINODE: + $linodeAccessKey = System::getEnv('_APP_STORAGE_LINODE_ACCESS_KEY', ''); + $linodeSecretKey = System::getEnv('_APP_STORAGE_LINODE_SECRET', ''); + $linodeRegion = System::getEnv('_APP_STORAGE_LINODE_REGION', ''); + $linodeBucket = System::getEnv('_APP_STORAGE_LINODE_BUCKET', ''); + $linodeAcl = 'private'; + return new Linode($root, $linodeAccessKey, $linodeSecretKey, $linodeBucket, $linodeRegion, $linodeAcl); + case Storage::DEVICE_WASABI: + $wasabiAccessKey = System::getEnv('_APP_STORAGE_WASABI_ACCESS_KEY', ''); + $wasabiSecretKey = System::getEnv('_APP_STORAGE_WASABI_SECRET', ''); + $wasabiRegion = System::getEnv('_APP_STORAGE_WASABI_REGION', ''); + $wasabiBucket = System::getEnv('_APP_STORAGE_WASABI_BUCKET', ''); + $wasabiAcl = 'private'; + return new Wasabi($root, $wasabiAccessKey, $wasabiSecretKey, $wasabiBucket, $wasabiRegion, $wasabiAcl); + } + } +} + +App::setResource('mode', function ($request) { + /** @var Appwrite\Utopia\Request $request */ + + /** + * Defines the mode for the request: + * - 'default' => Requests for Client and Server Side + * - 'admin' => Request from the Console on non-console projects + */ + return $request->getParam('mode', $request->getHeader('x-appwrite-mode', APP_MODE_DEFAULT)); +}, ['request']); + +App::setResource('geodb', function ($register) { + /** @var Utopia\Registry\Registry $register */ + return $register->get('geodb'); +}, ['register']); + +App::setResource('passwordsDictionary', function ($register) { + /** @var Utopia\Registry\Registry $register */ + return $register->get('passwordsDictionary'); +}, ['register']); + + +App::setResource('servers', function () { + $platforms = Config::getParam('platforms'); + $server = $platforms[APP_PLATFORM_SERVER]; + + $languages = array_map(function ($language) { + return strtolower($language['name']); + }, $server['sdks']); + + return $languages; +}); + +App::setResource('promiseAdapter', function ($register) { + return $register->get('promiseAdapter'); +}, ['register']); + +App::setResource('schema', function ($utopia, $dbForProject) { + + $complexity = function (int $complexity, array $args) { + $queries = Query::parseQueries($args['queries'] ?? []); + $query = Query::getByType($queries, [Query::TYPE_LIMIT])[0] ?? null; + $limit = $query ? $query->getValue() : APP_LIMIT_LIST_DEFAULT; + + return $complexity * $limit; + }; + + $attributes = function (int $limit, int $offset) use ($dbForProject) { + $attrs = Authorization::skip(fn () => $dbForProject->find('attributes', [ + Query::limit($limit), + Query::offset($offset), + ])); + + return \array_map(function ($attr) { + return $attr->getArrayCopy(); + }, $attrs); + }; + + $urls = [ + 'list' => function (string $databaseId, string $collectionId, array $args) { + return "/v1/databases/$databaseId/collections/$collectionId/documents"; + }, + 'create' => function (string $databaseId, string $collectionId, array $args) { + return "/v1/databases/$databaseId/collections/$collectionId/documents"; + }, + 'read' => function (string $databaseId, string $collectionId, array $args) { + return "/v1/databases/$databaseId/collections/$collectionId/documents/{$args['documentId']}"; + }, + 'update' => function (string $databaseId, string $collectionId, array $args) { + return "/v1/databases/$databaseId/collections/$collectionId/documents/{$args['documentId']}"; + }, + 'delete' => function (string $databaseId, string $collectionId, array $args) { + return "/v1/databases/$databaseId/collections/$collectionId/documents/{$args['documentId']}"; + }, + ]; + + $params = [ + 'list' => function (string $databaseId, string $collectionId, array $args) { + return [ 'queries' => $args['queries']]; + }, + 'create' => function (string $databaseId, string $collectionId, array $args) { + $id = $args['id'] ?? 'unique()'; + $permissions = $args['permissions'] ?? null; + + unset($args['id']); + unset($args['permissions']); + + // Order must be the same as the route params + return [ + 'databaseId' => $databaseId, + 'documentId' => $id, + 'collectionId' => $collectionId, + 'data' => $args, + 'permissions' => $permissions, + ]; + }, + 'update' => function (string $databaseId, string $collectionId, array $args) { + $documentId = $args['id']; + $permissions = $args['permissions'] ?? null; + + unset($args['id']); + unset($args['permissions']); + + // Order must be the same as the route params + return [ + 'databaseId' => $databaseId, + 'collectionId' => $collectionId, + 'documentId' => $documentId, + 'data' => $args, + 'permissions' => $permissions, + ]; + }, + ]; + + return Schema::build( + $utopia, + $complexity, + $attributes, + $urls, + $params, + ); +}, ['utopia', 'dbForProject']); + +App::setResource('contributors', function () { + $path = 'app/config/contributors.json'; + $list = (file_exists($path)) ? json_decode(file_get_contents($path), true) : []; + return $list; +}); + +App::setResource('employees', function () { + $path = 'app/config/employees.json'; + $list = (file_exists($path)) ? json_decode(file_get_contents($path), true) : []; + return $list; +}); + +App::setResource('heroes', function () { + $path = 'app/config/heroes.json'; + $list = (file_exists($path)) ? json_decode(file_get_contents($path), true) : []; + return $list; +}); + +App::setResource('gitHub', function (Cache $cache) { + return new VcsGitHub($cache); +}, ['cache']); + +App::setResource('requestTimestamp', function ($request) { + //TODO: Move this to the Request class itself + $timestampHeader = $request->getHeader('x-appwrite-timestamp'); + $requestTimestamp = null; + if (!empty($timestampHeader)) { + try { + $requestTimestamp = new \DateTime($timestampHeader); + } catch (\Throwable $e) { + throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'Invalid X-Appwrite-Timestamp header value'); + } + } + return $requestTimestamp; +}, ['request']); +App::setResource('plan', function (array $plan = []) { + return []; +}); diff --git a/composer.json b/composer.json index 38ead1fbcd..e09c260d5d 100644 --- a/composer.json +++ b/composer.json @@ -96,6 +96,10 @@ "config": { "platform": { "php": "8.3" + }, + "allow-plugins": { + "php-http/discovery": true, + "tbachert/spi": true } } } From 4f0a7de868704c6c336b3a8dff2fe6a0099389f8 Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Sat, 15 Mar 2025 12:34:49 +0100 Subject: [PATCH 128/143] Fixes --- app/init/config.php | 1 + app/init/constants.php | 2 ++ app/init/locale.php | 3 --- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/init/config.php b/app/init/config.php index 72e0e5fd7f..c329828098 100644 --- a/app/init/config.php +++ b/app/init/config.php @@ -1,5 +1,6 @@ Date: Sat, 15 Mar 2025 12:39:15 +0100 Subject: [PATCH 129/143] Code review fix --- app/init/database/formats.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/init/database/formats.php b/app/init/database/formats.php index 9a22b9ed2c..6c73877576 100644 --- a/app/init/database/formats.php +++ b/app/init/database/formats.php @@ -18,7 +18,7 @@ Structure::addFormat(APP_DATABASE_ATTRIBUTE_DATETIME, function () { }, Database::VAR_DATETIME); Structure::addFormat(APP_DATABASE_ATTRIBUTE_ENUM, function ($attribute) { - $elements = $attribute['formatOptions']['elements']; + $elements = $attribute['formatOptions']['elements'] ?? []; return new WhiteList($elements, true); }, Database::VAR_STRING); From 3b7aea22c2cbebcb452eb8e756e61ae5a6355526 Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Sat, 15 Mar 2025 12:42:34 +0100 Subject: [PATCH 130/143] Fixed test --- app/init/config.php | 1 - 1 file changed, 1 deletion(-) diff --git a/app/init/config.php b/app/init/config.php index c329828098..72e0e5fd7f 100644 --- a/app/init/config.php +++ b/app/init/config.php @@ -1,6 +1,5 @@ Date: Sat, 15 Mar 2025 12:58:41 +0100 Subject: [PATCH 131/143] Fixed DB test --- tests/e2e/Services/Databases/DatabasesBase.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/e2e/Services/Databases/DatabasesBase.php b/tests/e2e/Services/Databases/DatabasesBase.php index 04f2dbd8c8..3ad553a11d 100644 --- a/tests/e2e/Services/Databases/DatabasesBase.php +++ b/tests/e2e/Services/Databases/DatabasesBase.php @@ -1300,7 +1300,7 @@ trait DatabasesBase ]); $this->assertEquals(400, $unknown['headers']['status-code']); - $this->assertEquals('Unknown attribute: Unknown', $unknown['body']['message']); + $this->assertStringContainsString('Unknown attribute: Unknown', $unknown['body']['message']); $index1 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/indexes', array_merge([ 'content-type' => 'application/json', From 650627bfb4537605f2773c78a688531b898af77c Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Sun, 16 Mar 2025 01:01:13 +1300 Subject: [PATCH 132/143] Improve deletes queries --- composer.lock | 28 +++++++++++------------ src/Appwrite/Platform/Workers/Deletes.php | 13 ++++++++--- 2 files changed, 24 insertions(+), 17 deletions(-) diff --git a/composer.lock b/composer.lock index 23937694f8..ed19d10202 100644 --- a/composer.lock +++ b/composer.lock @@ -3705,16 +3705,16 @@ }, { "name": "utopia-php/database", - "version": "0.61.1", + "version": "0.61.2", "source": { "type": "git", "url": "https://github.com/utopia-php/database.git", - "reference": "2e0165bd14a570ec151f400ed381108e81d15b94" + "reference": "349fbdf4bc088f7775c7dfb8b80239a617a88436" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/database/zipball/2e0165bd14a570ec151f400ed381108e81d15b94", - "reference": "2e0165bd14a570ec151f400ed381108e81d15b94", + "url": "https://api.github.com/repos/utopia-php/database/zipball/349fbdf4bc088f7775c7dfb8b80239a617a88436", + "reference": "349fbdf4bc088f7775c7dfb8b80239a617a88436", "shasum": "" }, "require": { @@ -3755,9 +3755,9 @@ ], "support": { "issues": "https://github.com/utopia-php/database/issues", - "source": "https://github.com/utopia-php/database/tree/0.61.1" + "source": "https://github.com/utopia-php/database/tree/0.61.2" }, - "time": "2025-03-14T01:19:38+00:00" + "time": "2025-03-15T11:47:42+00:00" }, { "name": "utopia-php/domains", @@ -5317,16 +5317,16 @@ }, { "name": "laravel/pint", - "version": "v1.21.1", + "version": "v1.21.2", "source": { "type": "git", "url": "https://github.com/laravel/pint.git", - "reference": "c44bffbb2334e90fba560933c45948fa4a3f3e86" + "reference": "370772e7d9e9da087678a0edf2b11b6960e40558" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/pint/zipball/c44bffbb2334e90fba560933c45948fa4a3f3e86", - "reference": "c44bffbb2334e90fba560933c45948fa4a3f3e86", + "url": "https://api.github.com/repos/laravel/pint/zipball/370772e7d9e9da087678a0edf2b11b6960e40558", + "reference": "370772e7d9e9da087678a0edf2b11b6960e40558", "shasum": "" }, "require": { @@ -5337,9 +5337,9 @@ "php": "^8.2.0" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^3.70.2", - "illuminate/view": "^11.44.1", - "larastan/larastan": "^3.1.0", + "friendsofphp/php-cs-fixer": "^3.72.0", + "illuminate/view": "^11.44.2", + "larastan/larastan": "^3.2.0", "laravel-zero/framework": "^11.36.1", "mockery/mockery": "^1.6.12", "nunomaduro/termwind": "^2.3", @@ -5379,7 +5379,7 @@ "issues": "https://github.com/laravel/pint/issues", "source": "https://github.com/laravel/pint" }, - "time": "2025-03-11T03:22:21+00:00" + "time": "2025-03-14T22:31:42+00:00" }, { "name": "matthiasmullie/minify", diff --git a/src/Appwrite/Platform/Workers/Deletes.php b/src/Appwrite/Platform/Workers/Deletes.php index dd219765c2..9b0590181a 100644 --- a/src/Appwrite/Platform/Workers/Deletes.php +++ b/src/Appwrite/Platform/Workers/Deletes.php @@ -387,7 +387,8 @@ class Deletes extends Action $query = [ Query::lessThan('accessedAt', $datetime), - Query::orderDesc('accessedAt') + Query::orderDesc('accessedAt'), + Query::orderDesc('$internalId'), ]; $this->deleteByGroup( @@ -415,24 +416,26 @@ class Deletes extends Action */ private function deleteUsageStats(Document $project, callable $getProjectDB, callable $getLogsDB, string $hourlyUsageRetentionDatetime): void { - /** @var \Utopia\Database\Database $dbForProject*/ + /** @var Database $dbForProject*/ $dbForProject = $getProjectDB($project); // Delete Usage stats from projectDB $this->deleteByGroup('stats', [ Query::lessThan('time', $hourlyUsageRetentionDatetime), Query::orderDesc('time'), + Query::orderDesc('$internalId'), Query::equal('period', ['1h']), ], $dbForProject); if ($project->getId() !== 'console') { - /** @var \Utopia\Database\Database $dbForLogs*/ + /** @var Database $dbForLogs*/ $dbForLogs = call_user_func($getLogsDB, $project); // Delete Usage stats from logsDB $this->deleteByGroup('stats', [ Query::lessThan('time', $hourlyUsageRetentionDatetime), Query::orderDesc('time'), + Query::orderDesc('$internalId'), Query::equal('period', ['1h']), ], $dbForLogs); } @@ -696,6 +699,7 @@ class Deletes extends Action $this->deleteByGroup('executions', [ Query::lessThan('$createdAt', $datetime), Query::orderDesc('$createdAt'), + Query::orderDesc('$internalId'), ], $dbForProject); } @@ -715,6 +719,7 @@ class Deletes extends Action $this->deleteByGroup('sessions', [ Query::lessThan('$createdAt', $expired), Query::orderDesc('$createdAt'), + Query::orderDesc('$internalId'), ], $dbForProject); } @@ -730,6 +735,7 @@ class Deletes extends Action $this->deleteByGroup('realtime', [ Query::lessThan('timestamp', $datetime), Query::orderDesc('timestamp'), + Query::orderDesc('$internalId'), ], $dbForPlatform); } @@ -749,6 +755,7 @@ class Deletes extends Action $this->deleteByGroup(Audit::COLLECTION, [ Query::lessThan('time', $auditRetention), Query::orderDesc('time'), + Query::orderDesc('$internalId'), ], $dbForProject); } catch (DatabaseException $e) { Console::error('Failed to delete audit logs for project ' . $projectId . ': ' . $e->getMessage()); From 9dff192508a1c85cd8d2d58201e02550ddf9e7fe Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Sun, 16 Mar 2025 01:02:41 +1300 Subject: [PATCH 133/143] Revert "Improve deletes queries" This reverts commit 650627bfb4537605f2773c78a688531b898af77c. --- composer.lock | 28 +++++++++++------------ src/Appwrite/Platform/Workers/Deletes.php | 13 +++-------- 2 files changed, 17 insertions(+), 24 deletions(-) diff --git a/composer.lock b/composer.lock index ed19d10202..23937694f8 100644 --- a/composer.lock +++ b/composer.lock @@ -3705,16 +3705,16 @@ }, { "name": "utopia-php/database", - "version": "0.61.2", + "version": "0.61.1", "source": { "type": "git", "url": "https://github.com/utopia-php/database.git", - "reference": "349fbdf4bc088f7775c7dfb8b80239a617a88436" + "reference": "2e0165bd14a570ec151f400ed381108e81d15b94" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/database/zipball/349fbdf4bc088f7775c7dfb8b80239a617a88436", - "reference": "349fbdf4bc088f7775c7dfb8b80239a617a88436", + "url": "https://api.github.com/repos/utopia-php/database/zipball/2e0165bd14a570ec151f400ed381108e81d15b94", + "reference": "2e0165bd14a570ec151f400ed381108e81d15b94", "shasum": "" }, "require": { @@ -3755,9 +3755,9 @@ ], "support": { "issues": "https://github.com/utopia-php/database/issues", - "source": "https://github.com/utopia-php/database/tree/0.61.2" + "source": "https://github.com/utopia-php/database/tree/0.61.1" }, - "time": "2025-03-15T11:47:42+00:00" + "time": "2025-03-14T01:19:38+00:00" }, { "name": "utopia-php/domains", @@ -5317,16 +5317,16 @@ }, { "name": "laravel/pint", - "version": "v1.21.2", + "version": "v1.21.1", "source": { "type": "git", "url": "https://github.com/laravel/pint.git", - "reference": "370772e7d9e9da087678a0edf2b11b6960e40558" + "reference": "c44bffbb2334e90fba560933c45948fa4a3f3e86" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/pint/zipball/370772e7d9e9da087678a0edf2b11b6960e40558", - "reference": "370772e7d9e9da087678a0edf2b11b6960e40558", + "url": "https://api.github.com/repos/laravel/pint/zipball/c44bffbb2334e90fba560933c45948fa4a3f3e86", + "reference": "c44bffbb2334e90fba560933c45948fa4a3f3e86", "shasum": "" }, "require": { @@ -5337,9 +5337,9 @@ "php": "^8.2.0" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^3.72.0", - "illuminate/view": "^11.44.2", - "larastan/larastan": "^3.2.0", + "friendsofphp/php-cs-fixer": "^3.70.2", + "illuminate/view": "^11.44.1", + "larastan/larastan": "^3.1.0", "laravel-zero/framework": "^11.36.1", "mockery/mockery": "^1.6.12", "nunomaduro/termwind": "^2.3", @@ -5379,7 +5379,7 @@ "issues": "https://github.com/laravel/pint/issues", "source": "https://github.com/laravel/pint" }, - "time": "2025-03-14T22:31:42+00:00" + "time": "2025-03-11T03:22:21+00:00" }, { "name": "matthiasmullie/minify", diff --git a/src/Appwrite/Platform/Workers/Deletes.php b/src/Appwrite/Platform/Workers/Deletes.php index 9b0590181a..dd219765c2 100644 --- a/src/Appwrite/Platform/Workers/Deletes.php +++ b/src/Appwrite/Platform/Workers/Deletes.php @@ -387,8 +387,7 @@ class Deletes extends Action $query = [ Query::lessThan('accessedAt', $datetime), - Query::orderDesc('accessedAt'), - Query::orderDesc('$internalId'), + Query::orderDesc('accessedAt') ]; $this->deleteByGroup( @@ -416,26 +415,24 @@ class Deletes extends Action */ private function deleteUsageStats(Document $project, callable $getProjectDB, callable $getLogsDB, string $hourlyUsageRetentionDatetime): void { - /** @var Database $dbForProject*/ + /** @var \Utopia\Database\Database $dbForProject*/ $dbForProject = $getProjectDB($project); // Delete Usage stats from projectDB $this->deleteByGroup('stats', [ Query::lessThan('time', $hourlyUsageRetentionDatetime), Query::orderDesc('time'), - Query::orderDesc('$internalId'), Query::equal('period', ['1h']), ], $dbForProject); if ($project->getId() !== 'console') { - /** @var Database $dbForLogs*/ + /** @var \Utopia\Database\Database $dbForLogs*/ $dbForLogs = call_user_func($getLogsDB, $project); // Delete Usage stats from logsDB $this->deleteByGroup('stats', [ Query::lessThan('time', $hourlyUsageRetentionDatetime), Query::orderDesc('time'), - Query::orderDesc('$internalId'), Query::equal('period', ['1h']), ], $dbForLogs); } @@ -699,7 +696,6 @@ class Deletes extends Action $this->deleteByGroup('executions', [ Query::lessThan('$createdAt', $datetime), Query::orderDesc('$createdAt'), - Query::orderDesc('$internalId'), ], $dbForProject); } @@ -719,7 +715,6 @@ class Deletes extends Action $this->deleteByGroup('sessions', [ Query::lessThan('$createdAt', $expired), Query::orderDesc('$createdAt'), - Query::orderDesc('$internalId'), ], $dbForProject); } @@ -735,7 +730,6 @@ class Deletes extends Action $this->deleteByGroup('realtime', [ Query::lessThan('timestamp', $datetime), Query::orderDesc('timestamp'), - Query::orderDesc('$internalId'), ], $dbForPlatform); } @@ -755,7 +749,6 @@ class Deletes extends Action $this->deleteByGroup(Audit::COLLECTION, [ Query::lessThan('time', $auditRetention), Query::orderDesc('time'), - Query::orderDesc('$internalId'), ], $dbForProject); } catch (DatabaseException $e) { Console::error('Failed to delete audit logs for project ' . $projectId . ': ' . $e->getMessage()); From 12dbb1d50df24780d26c0a5e0662f886d7a6cfba Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Sun, 16 Mar 2025 01:02:44 +1300 Subject: [PATCH 134/143] Reapply "Improve deletes queries" This reverts commit 9dff192508a1c85cd8d2d58201e02550ddf9e7fe. --- composer.lock | 28 +++++++++++------------ src/Appwrite/Platform/Workers/Deletes.php | 13 ++++++++--- 2 files changed, 24 insertions(+), 17 deletions(-) diff --git a/composer.lock b/composer.lock index 23937694f8..ed19d10202 100644 --- a/composer.lock +++ b/composer.lock @@ -3705,16 +3705,16 @@ }, { "name": "utopia-php/database", - "version": "0.61.1", + "version": "0.61.2", "source": { "type": "git", "url": "https://github.com/utopia-php/database.git", - "reference": "2e0165bd14a570ec151f400ed381108e81d15b94" + "reference": "349fbdf4bc088f7775c7dfb8b80239a617a88436" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/database/zipball/2e0165bd14a570ec151f400ed381108e81d15b94", - "reference": "2e0165bd14a570ec151f400ed381108e81d15b94", + "url": "https://api.github.com/repos/utopia-php/database/zipball/349fbdf4bc088f7775c7dfb8b80239a617a88436", + "reference": "349fbdf4bc088f7775c7dfb8b80239a617a88436", "shasum": "" }, "require": { @@ -3755,9 +3755,9 @@ ], "support": { "issues": "https://github.com/utopia-php/database/issues", - "source": "https://github.com/utopia-php/database/tree/0.61.1" + "source": "https://github.com/utopia-php/database/tree/0.61.2" }, - "time": "2025-03-14T01:19:38+00:00" + "time": "2025-03-15T11:47:42+00:00" }, { "name": "utopia-php/domains", @@ -5317,16 +5317,16 @@ }, { "name": "laravel/pint", - "version": "v1.21.1", + "version": "v1.21.2", "source": { "type": "git", "url": "https://github.com/laravel/pint.git", - "reference": "c44bffbb2334e90fba560933c45948fa4a3f3e86" + "reference": "370772e7d9e9da087678a0edf2b11b6960e40558" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/pint/zipball/c44bffbb2334e90fba560933c45948fa4a3f3e86", - "reference": "c44bffbb2334e90fba560933c45948fa4a3f3e86", + "url": "https://api.github.com/repos/laravel/pint/zipball/370772e7d9e9da087678a0edf2b11b6960e40558", + "reference": "370772e7d9e9da087678a0edf2b11b6960e40558", "shasum": "" }, "require": { @@ -5337,9 +5337,9 @@ "php": "^8.2.0" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^3.70.2", - "illuminate/view": "^11.44.1", - "larastan/larastan": "^3.1.0", + "friendsofphp/php-cs-fixer": "^3.72.0", + "illuminate/view": "^11.44.2", + "larastan/larastan": "^3.2.0", "laravel-zero/framework": "^11.36.1", "mockery/mockery": "^1.6.12", "nunomaduro/termwind": "^2.3", @@ -5379,7 +5379,7 @@ "issues": "https://github.com/laravel/pint/issues", "source": "https://github.com/laravel/pint" }, - "time": "2025-03-11T03:22:21+00:00" + "time": "2025-03-14T22:31:42+00:00" }, { "name": "matthiasmullie/minify", diff --git a/src/Appwrite/Platform/Workers/Deletes.php b/src/Appwrite/Platform/Workers/Deletes.php index dd219765c2..9b0590181a 100644 --- a/src/Appwrite/Platform/Workers/Deletes.php +++ b/src/Appwrite/Platform/Workers/Deletes.php @@ -387,7 +387,8 @@ class Deletes extends Action $query = [ Query::lessThan('accessedAt', $datetime), - Query::orderDesc('accessedAt') + Query::orderDesc('accessedAt'), + Query::orderDesc('$internalId'), ]; $this->deleteByGroup( @@ -415,24 +416,26 @@ class Deletes extends Action */ private function deleteUsageStats(Document $project, callable $getProjectDB, callable $getLogsDB, string $hourlyUsageRetentionDatetime): void { - /** @var \Utopia\Database\Database $dbForProject*/ + /** @var Database $dbForProject*/ $dbForProject = $getProjectDB($project); // Delete Usage stats from projectDB $this->deleteByGroup('stats', [ Query::lessThan('time', $hourlyUsageRetentionDatetime), Query::orderDesc('time'), + Query::orderDesc('$internalId'), Query::equal('period', ['1h']), ], $dbForProject); if ($project->getId() !== 'console') { - /** @var \Utopia\Database\Database $dbForLogs*/ + /** @var Database $dbForLogs*/ $dbForLogs = call_user_func($getLogsDB, $project); // Delete Usage stats from logsDB $this->deleteByGroup('stats', [ Query::lessThan('time', $hourlyUsageRetentionDatetime), Query::orderDesc('time'), + Query::orderDesc('$internalId'), Query::equal('period', ['1h']), ], $dbForLogs); } @@ -696,6 +699,7 @@ class Deletes extends Action $this->deleteByGroup('executions', [ Query::lessThan('$createdAt', $datetime), Query::orderDesc('$createdAt'), + Query::orderDesc('$internalId'), ], $dbForProject); } @@ -715,6 +719,7 @@ class Deletes extends Action $this->deleteByGroup('sessions', [ Query::lessThan('$createdAt', $expired), Query::orderDesc('$createdAt'), + Query::orderDesc('$internalId'), ], $dbForProject); } @@ -730,6 +735,7 @@ class Deletes extends Action $this->deleteByGroup('realtime', [ Query::lessThan('timestamp', $datetime), Query::orderDesc('timestamp'), + Query::orderDesc('$internalId'), ], $dbForPlatform); } @@ -749,6 +755,7 @@ class Deletes extends Action $this->deleteByGroup(Audit::COLLECTION, [ Query::lessThan('time', $auditRetention), Query::orderDesc('time'), + Query::orderDesc('$internalId'), ], $dbForProject); } catch (DatabaseException $e) { Console::error('Failed to delete audit logs for project ' . $projectId . ': ' . $e->getMessage()); From 195edd19ac11b833b7a4f36e95d1aa1a97d6e0cf Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Sat, 15 Mar 2025 13:18:34 +0100 Subject: [PATCH 135/143] wip --- app/init/config.php | 65 +++---- app/init/constants.php | 55 +++++- app/init/database/filters.php | 19 +- app/init/locale.php | 6 +- app/init/registers.php | 71 ++++---- app/init/resources.php | 315 ++++++++++++++++++++++------------ 6 files changed, 340 insertions(+), 191 deletions(-) diff --git a/app/init/config.php b/app/init/config.php index 72e0e5fd7f..99baade018 100644 --- a/app/init/config.php +++ b/app/init/config.php @@ -2,35 +2,36 @@ use Utopia\Config\Config; -Config::load('events', __DIR__ . '/../config/events.php'); -Config::load('auth', __DIR__ . '/../config/auth.php'); -Config::load('apis', __DIR__ . '/../config/apis.php'); // List of APIs -Config::load('errors', __DIR__ . '/../config/errors.php'); -Config::load('oAuthProviders', __DIR__ . '/../config/oAuthProviders.php'); -Config::load('platforms', __DIR__ . '/../config/platforms.php'); -Config::load('collections', __DIR__ . '/../config/collections.php'); -Config::load('runtimes', __DIR__ . '/../config/runtimes.php'); -Config::load('runtimes-v2', __DIR__ . '/../config/runtimes-v2.php'); -Config::load('usage', __DIR__ . '/../config/usage.php'); -Config::load('roles', __DIR__ . '/../config/roles.php'); // User roles and scopes -Config::load('scopes', __DIR__ . '/../config/scopes.php'); // User roles and scopes -Config::load('services', __DIR__ . '/../config/services.php'); // List of services -Config::load('variables', __DIR__ . '/../config/variables.php'); // List of env variables -Config::load('regions', __DIR__ . '/../config/regions.php'); // List of available regions -Config::load('avatar-browsers', __DIR__ . '/../config/avatars/browsers.php'); -Config::load('avatar-credit-cards', __DIR__ . '/../config/avatars/credit-cards.php'); -Config::load('avatar-flags', __DIR__ . '/../config/avatars/flags.php'); -Config::load('locale-codes', __DIR__ . '/../config/locale/codes.php'); -Config::load('locale-currencies', __DIR__ . '/../config/locale/currencies.php'); -Config::load('locale-eu', __DIR__ . '/../config/locale/eu.php'); -Config::load('locale-languages', __DIR__ . '/../config/locale/languages.php'); -Config::load('locale-phones', __DIR__ . '/../config/locale/phones.php'); -Config::load('locale-countries', __DIR__ . '/../config/locale/countries.php'); -Config::load('locale-continents', __DIR__ . '/../config/locale/continents.php'); -Config::load('locale-templates', __DIR__ . '/../config/locale/templates.php'); -Config::load('storage-logos', __DIR__ . '/../config/storage/logos.php'); -Config::load('storage-mimes', __DIR__ . '/../config/storage/mimes.php'); -Config::load('storage-inputs', __DIR__ . '/../config/storage/inputs.php'); -Config::load('storage-outputs', __DIR__ . '/../config/storage/outputs.php'); -Config::load('runtime-specifications', __DIR__ . '/../config/runtimes/specifications.php'); -Config::load('function-templates', __DIR__ . '/../config/function-templates.php'); +Config::load('events', __DIR__ . '/config/events.php'); +Config::load('auth', __DIR__ . '/config/auth.php'); +Config::load('apis', __DIR__ . '/config/apis.php'); // List of APIs +Config::load('errors', __DIR__ . '/config/errors.php'); +Config::load('oAuthProviders', __DIR__ . '/config/oAuthProviders.php'); +Config::load('platforms', __DIR__ . '/config/platforms.php'); +Config::load('console', __DIR__ . '/config/console.php'); +Config::load('collections', __DIR__ . '/config/collections.php'); +Config::load('runtimes', __DIR__ . '/config/runtimes.php'); +Config::load('runtimes-v2', __DIR__ . '/config/runtimes-v2.php'); +Config::load('usage', __DIR__ . '/config/usage.php'); +Config::load('roles', __DIR__ . '/config/roles.php'); // User roles and scopes +Config::load('scopes', __DIR__ . '/config/scopes.php'); // User roles and scopes +Config::load('services', __DIR__ . '/config/services.php'); // List of services +Config::load('variables', __DIR__ . '/config/variables.php'); // List of env variables +Config::load('regions', __DIR__ . '/config/regions.php'); // List of available regions +Config::load('avatar-browsers', __DIR__ . '/config/avatars/browsers.php'); +Config::load('avatar-credit-cards', __DIR__ . '/config/avatars/credit-cards.php'); +Config::load('avatar-flags', __DIR__ . '/config/avatars/flags.php'); +Config::load('locale-codes', __DIR__ . '/config/locale/codes.php'); +Config::load('locale-currencies', __DIR__ . '/config/locale/currencies.php'); +Config::load('locale-eu', __DIR__ . '/config/locale/eu.php'); +Config::load('locale-languages', __DIR__ . '/config/locale/languages.php'); +Config::load('locale-phones', __DIR__ . '/config/locale/phones.php'); +Config::load('locale-countries', __DIR__ . '/config/locale/countries.php'); +Config::load('locale-continents', __DIR__ . '/config/locale/continents.php'); +Config::load('locale-templates', __DIR__ . '/config/locale/templates.php'); +Config::load('storage-logos', __DIR__ . '/config/storage/logos.php'); +Config::load('storage-mimes', __DIR__ . '/config/storage/mimes.php'); +Config::load('storage-inputs', __DIR__ . '/config/storage/inputs.php'); +Config::load('storage-outputs', __DIR__ . '/config/storage/outputs.php'); +Config::load('runtime-specifications', __DIR__ . '/config/runtimes/specifications.php'); +Config::load('function-templates', __DIR__ . '/config/function-templates.php'); diff --git a/app/init/constants.php b/app/init/constants.php index a1c4387d7a..3383b28f57 100644 --- a/app/init/constants.php +++ b/app/init/constants.php @@ -29,9 +29,10 @@ const APP_LIMIT_LIST_DEFAULT = 25; // Default maximum number of items to return const APP_KEY_ACCESS = 24 * 60 * 60; // 24 hours const APP_USER_ACCESS = 24 * 60 * 60; // 24 hours const APP_PROJECT_ACCESS = 24 * 60 * 60; // 24 hours +const APP_FILE_ACCESS = 24 * 60 * 60; // 24 hours const APP_CACHE_UPDATE = 24 * 60 * 60; // 24 hours const APP_CACHE_BUSTER = 4318; -const APP_VERSION_STABLE = '1.6.0'; +const APP_VERSION_STABLE = '1.6.2'; const APP_DATABASE_ATTRIBUTE_EMAIL = 'email'; const APP_DATABASE_ATTRIBUTE_ENUM = 'enum'; const APP_DATABASE_ATTRIBUTE_IP = 'ip'; @@ -40,7 +41,10 @@ const APP_DATABASE_ATTRIBUTE_URL = 'url'; const APP_DATABASE_ATTRIBUTE_INT_RANGE = 'intRange'; const APP_DATABASE_ATTRIBUTE_FLOAT_RANGE = 'floatRange'; const APP_DATABASE_ATTRIBUTE_STRING_MAX_LENGTH = 1_073_741_824; // 2^32 bits / 4 bits per char -const APP_DATABASE_TIMEOUT_MILLISECONDS = 15_000; +const APP_DATABASE_TIMEOUT_MILLISECONDS_API = 15 * 1000; // 15 seconds +const APP_DATABASE_TIMEOUT_MILLISECONDS_WORKER = 300 * 1000; // 5 minutes +const APP_DATABASE_TIMEOUT_MILLISECONDS_TASK = 300 * 1000; // 5 minutes +const APP_DATABASE_QUERY_MAX_VALUES = 500; const APP_STORAGE_UPLOADS = '/storage/uploads'; const APP_STORAGE_FUNCTIONS = '/storage/functions'; const APP_STORAGE_BUILDS = '/storage/builds'; @@ -60,9 +64,12 @@ const APP_SOCIAL_DEV = 'https://dev.to/appwrite'; const APP_SOCIAL_STACKSHARE = 'https://stackshare.io/appwrite'; const APP_SOCIAL_YOUTUBE = 'https://www.youtube.com/c/appwrite?sub_confirmation=1'; const APP_HOSTNAME_INTERNAL = 'appwrite'; -const APP_FUNCTION_SPECIFICATION_DEFAULT = Specification::S_05VCPU_512MB; +const APP_FUNCTION_SPECIFICATION_DEFAULT = Specification::S_1VCPU_512MB; const APP_FUNCTION_CPUS_DEFAULT = 0.5; const APP_FUNCTION_MEMORY_DEFAULT = 512; +const APP_PLATFORM_SERVER = 'server'; +const APP_PLATFORM_CLIENT = 'client'; +const APP_PLATFORM_CONSOLE = 'console'; // Database Reconnect const DATABASE_RECONNECT_SLEEP = 2; @@ -105,6 +112,7 @@ const DELETE_TYPE_TOPIC = 'topic'; const DELETE_TYPE_TARGET = 'target'; const DELETE_TYPE_EXPIRED_TARGETS = 'invalid_targets'; const DELETE_TYPE_SESSION_TARGETS = 'session_targets'; +const DELETE_TYPE_MAINTENANCE = 'maintenance'; // Message types const MESSAGE_SEND_TYPE_INTERNAL = 'internal'; @@ -135,7 +143,10 @@ const API_KEY_DYNAMIC = 'dynamic'; // Usage metrics const METRIC_TEAMS = 'teams'; const METRIC_USERS = 'users'; - +const METRIC_WEBHOOKS_SENT = 'webhooks.events.sent'; +const METRIC_WEBHOOKS_FAILED = 'webhooks.events.failed'; +const METRIC_WEBHOOK_ID_SENT = '{webhookInternalId}.webhooks.events.sent'; +const METRIC_WEBHOOK_ID_FAILED = '{webhookInternalId}.webhooks.events.failed'; const METRIC_AUTH_METHOD_PHONE = 'auth.method.phone'; const METRIC_AUTH_METHOD_PHONE_COUNTRY_CODE = METRIC_AUTH_METHOD_PHONE . '.{countryCode}'; const METRIC_MESSAGES = 'messages'; @@ -150,13 +161,24 @@ const METRIC_MESSAGES_TYPE_PROVIDER_FAILED = METRIC_MESSAGES . '.{type}.{provid const METRIC_SESSIONS = 'sessions'; const METRIC_DATABASES = 'databases'; const METRIC_COLLECTIONS = 'collections'; +const METRIC_DATABASES_STORAGE = 'databases.storage'; const METRIC_DATABASE_ID_COLLECTIONS = '{databaseInternalId}.collections'; +const METRIC_DATABASE_ID_STORAGE = '{databaseInternalId}.databases.storage'; const METRIC_DOCUMENTS = 'documents'; const METRIC_DATABASE_ID_DOCUMENTS = '{databaseInternalId}.documents'; const METRIC_DATABASE_ID_COLLECTION_ID_DOCUMENTS = '{databaseInternalId}.{collectionInternalId}.documents'; +const METRIC_DATABASE_ID_COLLECTION_ID_STORAGE = '{databaseInternalId}.{collectionInternalId}.databases.storage'; +const METRIC_DATABASES_OPERATIONS_READS = 'databases.operations.reads'; +const METRIC_DATABASE_ID_OPERATIONS_READS = '{databaseInternalId}.databases.operations.reads'; +const METRIC_DATABASES_OPERATIONS_WRITES = 'databases.operations.writes'; +const METRIC_DATABASE_ID_OPERATIONS_WRITES = '{databaseInternalId}.databases.operations.writes'; const METRIC_BUCKETS = 'buckets'; const METRIC_FILES = 'files'; const METRIC_FILES_STORAGE = 'files.storage'; +const METRIC_FILES_TRANSFORMATIONS = 'files.transformations'; +const METRIC_BUCKET_ID_FILES_TRANSFORMATIONS = '{bucketInternalId}.files.transformations'; +const METRIC_FILES_IMAGES_TRANSFORMED = 'files.imagesTransformed'; +const METRIC_BUCKET_ID_FILES_IMAGES_TRANSFORMED = '{bucketInternalId}.files.imagesTransformed'; const METRIC_BUCKET_ID_FILES = '{bucketInternalId}.files'; const METRIC_BUCKET_ID_FILES_STORAGE = '{bucketInternalId}.files.storage'; const METRIC_FUNCTIONS = 'functions'; @@ -189,3 +211,28 @@ const METRIC_FUNCTION_ID_EXECUTIONS_MB_SECONDS = '{functionInternalId}.execution const METRIC_NETWORK_REQUESTS = 'network.requests'; const METRIC_NETWORK_INBOUND = 'network.inbound'; const METRIC_NETWORK_OUTBOUND = 'network.outbound'; +const METRIC_MAU = 'users.mau'; +const METRIC_DAU = 'users.dau'; +const METRIC_WAU = 'users.wau'; +const METRIC_WEBHOOKS = 'webhooks'; +const METRIC_PLATFORMS = 'platforms'; +const METRIC_PROVIDERS = 'providers'; +const METRIC_TOPICS = 'topics'; +const METRIC_TARGETS = 'targets'; +const METRIC_PROVIDER_TYPE_TARGETS = '{providerType}.targets'; +const METRIC_KEYS = 'keys'; +const METRIC_RESOURCE_TYPE_ID_BUILDS = '{resourceType}.{resourceInternalId}.builds'; +const METRIC_RESOURCE_TYPE_ID_BUILDS_STORAGE = '{resourceType}.{resourceInternalId}.builds.storage'; +const METRIC_RESOURCE_TYPE_ID_DEPLOYMENTS = '{resourceType}.{resourceInternalId}.deployments'; +const METRIC_RESOURCE_TYPE_ID_DEPLOYMENTS_STORAGE = '{resourceType}.{resourceInternalId}.deployments.storage'; + +// Resource types + +const RESOURCE_TYPE_PROJECTS = 'projects'; +const RESOURCE_TYPE_FUNCTIONS = 'functions'; +const RESOURCE_TYPE_DATABASES = 'databases'; +const RESOURCE_TYPE_BUCKETS = 'buckets'; +const RESOURCE_TYPE_PROVIDERS = 'providers'; +const RESOURCE_TYPE_TOPICS = 'topics'; +const RESOURCE_TYPE_SUBSCRIBERS = 'subscribers'; +const RESOURCE_TYPE_MESSAGES = 'messages'; diff --git a/app/init/database/filters.php b/app/init/database/filters.php index 241914f9ff..8223b1c677 100644 --- a/app/init/database/filters.php +++ b/app/init/database/filters.php @@ -379,12 +379,19 @@ Database::addFilter( $data = \json_decode($message->getAttribute('data', []), true); $providerType = $message->getAttribute('providerType', ''); - if ($providerType === MESSAGE_TYPE_EMAIL) { - $searchValues = \array_merge($searchValues, [$data['subject'], MESSAGE_TYPE_EMAIL]); - } elseif ($providerType === MESSAGE_TYPE_SMS) { - $searchValues = \array_merge($searchValues, [$data['content'], MESSAGE_TYPE_SMS]); - } else { - $searchValues = \array_merge($searchValues, [$data['title'], MESSAGE_TYPE_PUSH]); + switch ($providerType) { + case MESSAGE_TYPE_EMAIL: + $searchValues[] = $data['subject']; + $searchValues[] = MESSAGE_TYPE_EMAIL; + break; + case MESSAGE_TYPE_SMS: + $searchValues[] = $data['content']; + $searchValues[] = MESSAGE_TYPE_SMS; + break; + case MESSAGE_TYPE_PUSH: + $searchValues[] = $data['title'] ?? ''; + $searchValues[] = MESSAGE_TYPE_PUSH; + break; } $search = \implode(' ', \array_filter($searchValues)); diff --git a/app/init/locale.php b/app/init/locale.php index 122dc89692..333dd106e3 100644 --- a/app/init/locale.php +++ b/app/init/locale.php @@ -10,12 +10,12 @@ $locales = Config::getParam('locale-codes', []); foreach ($locales as $locale) { $code = $locale['code']; - $path = __DIR__ . '/../config/locale/translations/' . $code . '.json'; + $path = __DIR__ . '/config/locale/translations/' . $code . '.json'; if (!\file_exists($path)) { - $path = __DIR__ . '/../config/locale/translations/' . \substr($code, 0, 2) . '.json'; // if `ar-ae` doesn't exist, look for `ar` + $path = __DIR__ . '/config/locale/translations/' . \substr($code, 0, 2) . '.json'; // if `ar-ae` doesn't exist, look for `ar` if (!\file_exists($path)) { - $path = __DIR__ . '/../config/locale/translations/en.json'; // if none translation exists, use default from `en.json` + $path = __DIR__ . '/config/locale/translations/en.json'; // if none translation exists, use default from `en.json` } } diff --git a/app/init/registers.php b/app/init/registers.php index 35a309f98c..4ce8adcefd 100644 --- a/app/init/registers.php +++ b/app/init/registers.php @@ -3,19 +3,18 @@ use Appwrite\Extend\Exception; use Appwrite\GraphQL\Promises\Adapter\Swoole; use Appwrite\Hooks\Hooks; +use Appwrite\PubSub\Adapter\Redis as PubSub; use Appwrite\URL\URL as AppwriteURL; use MaxMind\Db\Reader; use PHPMailer\PHPMailer\PHPMailer; use Swoole\Database\PDOProxy; use Utopia\App; use Utopia\Cache\Adapter\Redis as RedisCache; -use Utopia\Cache\Cache; use Utopia\CLI\Console; use Utopia\Config\Config; use Utopia\Database\Adapter\MariaDB; use Utopia\Database\Adapter\MySQL; use Utopia\Database\Adapter\SQL; -use Utopia\Database\Database; use Utopia\Domains\Validator\PublicDomain; use Utopia\DSN\DSN; use Utopia\Logger\Adapter\AppSignal; @@ -26,7 +25,6 @@ use Utopia\Logger\Logger; use Utopia\Pools\Group; use Utopia\Pools\Pool; use Utopia\Queue; -use Utopia\Queue\Connection; use Utopia\Registry\Registry; use Utopia\System\System; @@ -39,12 +37,15 @@ if (!App::isProduction()) { // Useful for existing tests involving webhooks PublicDomain::allow(['request-catcher']); } - $register->set('logger', function () { // Register error logger $providerName = System::getEnv('_APP_LOGGING_PROVIDER', ''); $providerConfig = System::getEnv('_APP_LOGGING_CONFIG', ''); + if (empty($providerConfig)) { + return; + } + try { $loggingProvider = new DSN($providerConfig ?? ''); @@ -116,31 +117,43 @@ $register->set('pools', function () { $connections = [ 'console' => [ 'type' => 'database', - 'dsns' => System::getEnv('_APP_CONNECTIONS_DB_CONSOLE', $fallbackForDB), + 'dsns' => $fallbackForDB, 'multiple' => false, 'schemes' => ['mariadb', 'mysql'], ], 'database' => [ 'type' => 'database', - 'dsns' => System::getEnv('_APP_CONNECTIONS_DB_PROJECT', $fallbackForDB), + 'dsns' => $fallbackForDB, 'multiple' => true, 'schemes' => ['mariadb', 'mysql'], ], - 'queue' => [ - 'type' => 'queue', - 'dsns' => System::getEnv('_APP_CONNECTIONS_QUEUE', $fallbackForRedis), + 'logs' => [ + 'type' => 'database', + 'dsns' => System::getEnv('_APP_CONNECTIONS_DB_LOGS', $fallbackForDB), + 'multiple' => false, + 'schemes' => ['mariadb', 'mysql'], + ], + 'publisher' => [ + 'type' => 'publisher', + 'dsns' => $fallbackForRedis, + 'multiple' => false, + 'schemes' => ['redis'], + ], + 'consumer' => [ + 'type' => 'consumer', + 'dsns' => $fallbackForRedis, 'multiple' => false, 'schemes' => ['redis'], ], 'pubsub' => [ 'type' => 'pubsub', - 'dsns' => System::getEnv('_APP_CONNECTIONS_PUBSUB', $fallbackForRedis), + 'dsns' => $fallbackForRedis, 'multiple' => false, 'schemes' => ['redis'], ], 'cache' => [ 'type' => 'cache', - 'dsns' => System::getEnv('_APP_CONNECTIONS_CACHE', $fallbackForRedis), + 'dsns' => $fallbackForRedis, 'multiple' => true, 'schemes' => ['redis'], ], @@ -152,7 +165,7 @@ $register->set('pools', function () { $multiprocessing = System::getEnv('_APP_SERVER_MULTIPROCESS', 'disabled') === 'enabled'; if ($multiprocessing) { - $workerCount = swoole_cpu_num() * intval(System::getEnv('_APP_WORKER_PER_CORE', 6)); + $workerCount = intval(System::getEnv('_APP_CPU_NUM', swoole_cpu_num())) * intval(System::getEnv('_APP_WORKER_PER_CORE', 6)); } else { $workerCount = 1; } @@ -212,12 +225,12 @@ $register->set('pools', function () { }); }, 'redis' => function () use ($dsnHost, $dsnPort, $dsnPass) { - $redis = new Redis(); + $redis = new \Redis(); @$redis->pconnect($dsnHost, (int)$dsnPort); if ($dsnPass) { $redis->auth($dsnPass); } - $redis->setOption(Redis::OPT_READ_TIMEOUT, -1); + $redis->setOption(\Redis::OPT_READ_TIMEOUT, -1); return $redis; }, @@ -235,28 +248,26 @@ $register->set('pools', function () { }; $adapter->setDatabase($dsn->getPath()); - break; + return $adapter; case 'pubsub': - $adapter = $resource(); - break; - case 'queue': - $adapter = match ($dsn->getScheme()) { - 'redis' => new Queue\Connection\Redis($dsn->getHost(), $dsn->getPort()), + return match ($dsn->getScheme()) { + 'redis' => new PubSub($resource()), + default => null + }; + case 'publisher': + case 'consumer': + return match ($dsn->getScheme()) { + 'redis' => new Queue\Broker\Redis(new Queue\Connection\Redis($dsn->getHost(), $dsn->getPort())), default => null }; - break; case 'cache': - $adapter = match ($dsn->getScheme()) { + return match ($dsn->getScheme()) { 'redis' => new RedisCache($resource()), default => null }; - break; - default: throw new Exception(Exception::GENERAL_SERVER_ERROR, "Server error: Missing adapter implementation."); } - - return $adapter; }); $group->add($pool); @@ -312,22 +323,18 @@ $register->set('smtp', function () { return $mail; }); - $register->set('geodb', function () { - return new Reader(__DIR__ . '/../assets/dbip/dbip-country-lite-2024-09.mmdb'); + return new Reader(__DIR__ . '/assets/dbip/dbip-country-lite-2024-09.mmdb'); }); - $register->set('passwordsDictionary', function () { - $content = \file_get_contents(__DIR__ . '/../assets/security/10k-common-passwords'); + $content = \file_get_contents(__DIR__ . '/assets/security/10k-common-passwords'); $content = explode("\n", $content); $content = array_flip($content); return $content; }); - $register->set('promiseAdapter', function () { return new Swoole(); }); - $register->set('hooks', function () { return new Hooks(); }); diff --git a/app/init/resources.php b/app/init/resources.php index 22a4daf333..0895ef32a5 100644 --- a/app/init/resources.php +++ b/app/init/resources.php @@ -44,6 +44,7 @@ use Utopia\System\System; use Utopia\Validator\Hostname; use Utopia\VCS\Adapter\Git\GitHub as VcsGitHub; +// Runtime Execution App::setResource('log', fn () => new Log()); App::setResource('logger', function ($register) { return $register->get('logger'); @@ -61,42 +62,51 @@ App::setResource('localeCodes', function () { }); // Queues -App::setResource('queue', function (Group $pools) { - return $pools->get('queue')->pop()->getResource(); +App::setResource('publisher', function (Group $pools) { + return $pools->get('publisher')->pop()->getResource(); }, ['pools']); -App::setResource('queueForMessaging', function (Connection $queue) { - return new Messaging($queue); -}, ['queue']); -App::setResource('queueForMails', function (Connection $queue) { - return new Mail($queue); -}, ['queue']); -App::setResource('queueForBuilds', function (Connection $queue) { - return new Build($queue); -}, ['queue']); -App::setResource('queueForDatabase', function (Connection $queue) { - return new EventDatabase($queue); -}, ['queue']); -App::setResource('queueForDeletes', function (Connection $queue) { - return new Delete($queue); -}, ['queue']); -App::setResource('queueForEvents', function (Connection $queue) { - return new Event($queue); -}, ['queue']); -App::setResource('queueForAudits', function (Connection $queue) { - return new Audit($queue); -}, ['queue']); -App::setResource('queueForFunctions', function (Connection $queue) { - return new Func($queue); -}, ['queue']); -App::setResource('queueForUsage', function (Connection $queue) { - return new Usage($queue); -}, ['queue']); -App::setResource('queueForCertificates', function (Connection $queue) { - return new Certificate($queue); -}, ['queue']); -App::setResource('queueForMigrations', function (Connection $queue) { - return new Migration($queue); -}, ['queue']); +App::setResource('consumer', function (Group $pools) { + return $pools->get('consumer')->pop()->getResource(); +}, ['pools']); +App::setResource('queueForMessaging', function (Queue\Publisher $publisher) { + return new Messaging($publisher); +}, ['publisher']); +App::setResource('queueForMails', function (Queue\Publisher $publisher) { + return new Mail($publisher); +}, ['publisher']); +App::setResource('queueForBuilds', function (Queue\Publisher $publisher) { + return new Build($publisher); +}, ['publisher']); +App::setResource('queueForDatabase', function (Queue\Publisher $publisher) { + return new EventDatabase($publisher); +}, ['publisher']); +App::setResource('queueForDeletes', function (Queue\Publisher $publisher) { + return new Delete($publisher); +}, ['publisher']); +App::setResource('queueForEvents', function (Queue\Publisher $publisher) { + return new Event($publisher); +}, ['publisher']); +App::setResource('queueForWebhooks', function (Queue\Publisher $publisher) { + return new Webhook($publisher); +}, ['publisher']); +App::setResource('queueForRealtime', function () { + return new Realtime(); +}, []); +App::setResource('queueForStatsUsage', function (Queue\Publisher $publisher) { + return new StatsUsage($publisher); +}, ['publisher']); +App::setResource('queueForAudits', function (Queue\Publisher $publisher) { + return new Audit($publisher); +}, ['publisher']); +App::setResource('queueForFunctions', function (Queue\Publisher $publisher) { + return new Func($publisher); +}, ['publisher']); +App::setResource('queueForCertificates', function (Queue\Publisher $publisher) { + return new Certificate($publisher); +}, ['publisher']); +App::setResource('queueForMigrations', function (Queue\Publisher $publisher) { + return new Migration($publisher); +}, ['publisher']); App::setResource('clients', function ($request, $console, $project) { $console->setAttribute('platforms', [ // Always allow current host '$collection' => ID::custom('platforms'), @@ -149,12 +159,12 @@ App::setResource('clients', function ($request, $console, $project) { return \array_unique($clients); }, ['request', 'console', 'project']); -App::setResource('user', function ($mode, $project, $console, $request, $response, $dbForProject, $dbForConsole) { +App::setResource('user', function ($mode, $project, $console, $request, $response, $dbForProject, $dbForPlatform) { /** @var Appwrite\Utopia\Request $request */ /** @var Appwrite\Utopia\Response $response */ /** @var Utopia\Database\Document $project */ /** @var Utopia\Database\Database $dbForProject */ - /** @var Utopia\Database\Database $dbForConsole */ + /** @var Utopia\Database\Database $dbForPlatform */ /** @var string $mode */ Authorization::setDefaultStatus(true); @@ -203,13 +213,13 @@ App::setResource('user', function ($mode, $project, $console, $request, $respons $user = new Document([]); } else { if ($project->getId() === 'console') { - $user = $dbForConsole->getDocument('users', Auth::$unique); + $user = $dbForPlatform->getDocument('users', Auth::$unique); } else { $user = $dbForProject->getDocument('users', Auth::$unique); } } } else { - $user = $dbForConsole->getDocument('users', Auth::$unique); + $user = $dbForPlatform->getDocument('users', Auth::$unique); } if ( @@ -219,13 +229,13 @@ App::setResource('user', function ($mode, $project, $console, $request, $respons $user = new Document([]); } - if (APP_MODE_ADMIN === $mode) { - if ($user->find('teamInternalId', $project->getAttribute('teamInternalId'), 'memberships')) { - Authorization::setDefaultStatus(false); // Cancel security segmentation for admin users. - } else { - $user = new Document([]); - } - } + // if (APP_MODE_ADMIN === $mode) { + // if ($user->find('teamInternalId', $project->getAttribute('teamInternalId'), 'memberships')) { + // Authorization::setDefaultStatus(false); // Cancel security segmentation for admin users. + // } else { + // $user = new Document([]); + // } + // } $authJWT = $request->getHeader('x-appwrite-jwt', ''); @@ -252,14 +262,14 @@ App::setResource('user', function ($mode, $project, $console, $request, $respons } $dbForProject->setMetadata('user', $user->getId()); - $dbForConsole->setMetadata('user', $user->getId()); + $dbForPlatform->setMetadata('user', $user->getId()); return $user; -}, ['mode', 'project', 'console', 'request', 'response', 'dbForProject', 'dbForConsole']); +}, ['mode', 'project', 'console', 'request', 'response', 'dbForProject', 'dbForPlatform']); -App::setResource('project', function ($dbForConsole, $request, $console) { +App::setResource('project', function ($dbForPlatform, $request, $console) { /** @var Appwrite\Utopia\Request $request */ - /** @var Utopia\Database\Database $dbForConsole */ + /** @var Utopia\Database\Database $dbForPlatform */ /** @var Utopia\Database\Document $console */ $projectId = $request->getParam('project', $request->getHeader('x-appwrite-project', '')); @@ -268,10 +278,10 @@ App::setResource('project', function ($dbForConsole, $request, $console) { return $console; } - $project = Authorization::skip(fn () => $dbForConsole->getDocument('projects', $projectId)); + $project = Authorization::skip(fn () => $dbForPlatform->getDocument('projects', $projectId)); return $project; -}, ['dbForConsole', 'request', 'console']); +}, ['dbForPlatform', 'request', 'console']); App::setResource('session', function (Document $user) { if ($user->isEmpty()) { @@ -295,50 +305,12 @@ App::setResource('session', function (Document $user) { }, ['user']); App::setResource('console', function () { - return new Document([ - '$id' => ID::custom('console'), - '$internalId' => ID::custom('console'), - 'name' => 'Appwrite', - '$collection' => ID::custom('projects'), - 'description' => 'Appwrite core engine', - 'logo' => '', - 'teamId' => -1, - 'webhooks' => [], - 'keys' => [], - 'platforms' => [ - [ - '$collection' => ID::custom('platforms'), - 'name' => 'Localhost', - 'type' => Origin::CLIENT_TYPE_WEB, - 'hostname' => 'localhost', - ], // Current host is added on app init - ], - 'legalName' => '', - 'legalCountry' => '', - 'legalState' => '', - 'legalCity' => '', - 'legalAddress' => '', - 'legalTaxId' => '', - 'auths' => [ - 'mockNumbers' => [], - 'invites' => System::getEnv('_APP_CONSOLE_INVITES', 'enabled') === 'enabled', - 'limit' => (System::getEnv('_APP_CONSOLE_WHITELIST_ROOT', 'enabled') === 'enabled') ? 1 : 0, // limit signup to 1 user - 'duration' => Auth::TOKEN_EXPIRATION_LOGIN_LONG, // 1 Year in seconds - 'sessionAlerts' => System::getEnv('_APP_CONSOLE_SESSION_ALERTS', 'disabled') === 'enabled' - ], - 'authWhitelistEmails' => (!empty(System::getEnv('_APP_CONSOLE_WHITELIST_EMAILS', null))) ? \explode(',', System::getEnv('_APP_CONSOLE_WHITELIST_EMAILS', null)) : [], - 'authWhitelistIPs' => (!empty(System::getEnv('_APP_CONSOLE_WHITELIST_IPS', null))) ? \explode(',', System::getEnv('_APP_CONSOLE_WHITELIST_IPS', null)) : [], - 'oAuthProviders' => [ - 'githubEnabled' => true, - 'githubSecret' => System::getEnv('_APP_CONSOLE_GITHUB_SECRET', ''), - 'githubAppid' => System::getEnv('_APP_CONSOLE_GITHUB_APP_ID', '') - ], - ]); + return new Document(Config::getParam('console')); }, []); -App::setResource('dbForProject', function (Group $pools, Database $dbForConsole, Cache $cache, Document $project) { +App::setResource('dbForProject', function (Group $pools, Database $dbForPlatform, Cache $cache, Document $project) { if ($project->isEmpty() || $project->getId() === 'console') { - return $dbForConsole; + return $dbForPlatform; } try { @@ -358,16 +330,12 @@ App::setResource('dbForProject', function (Group $pools, Database $dbForConsole, $database ->setMetadata('host', \gethostname()) ->setMetadata('project', $project->getId()) - ->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS); + ->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_API) + ->setMaxQueryValues(APP_DATABASE_QUERY_MAX_VALUES); - try { - $dsn = new DSN($project->getAttribute('database')); - } catch (\InvalidArgumentException) { - // TODO: Temporary until all projects are using shared tables - $dsn = new DSN('mysql://' . $project->getAttribute('database')); - } + $sharedTables = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', '')); - if ($dsn->getHost() === System::getEnv('_APP_DATABASE_SHARED_TABLES', '')) { + if (\in_array($dsn->getHost(), $sharedTables)) { $database ->setSharedTables(true) ->setTenant($project->getInternalId()) @@ -380,9 +348,9 @@ App::setResource('dbForProject', function (Group $pools, Database $dbForConsole, } return $database; -}, ['pools', 'dbForConsole', 'cache', 'project']); +}, ['pools', 'dbForPlatform', 'cache', 'project']); -App::setResource('dbForConsole', function (Group $pools, Cache $cache) { +App::setResource('dbForPlatform', function (Group $pools, Cache $cache) { $dbAdapter = $pools ->get('console') ->pop() @@ -394,17 +362,18 @@ App::setResource('dbForConsole', function (Group $pools, Cache $cache) { ->setNamespace('_console') ->setMetadata('host', \gethostname()) ->setMetadata('project', 'console') - ->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS); + ->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_API) + ->setMaxQueryValues(APP_DATABASE_QUERY_MAX_VALUES); return $database; }, ['pools', 'cache']); -App::setResource('getProjectDB', function (Group $pools, Database $dbForConsole, $cache) { +App::setResource('getProjectDB', function (Group $pools, Database $dbForPlatform, $cache) { $databases = []; // TODO: @Meldiron This should probably be responsibility of utopia-php/pools - return function (Document $project) use ($pools, $dbForConsole, $cache, &$databases) { + return function (Document $project) use ($pools, $dbForPlatform, $cache, &$databases) { if ($project->isEmpty() || $project->getId() === 'console') { - return $dbForConsole; + return $dbForPlatform; } try { @@ -418,9 +387,12 @@ App::setResource('getProjectDB', function (Group $pools, Database $dbForConsole, $database ->setMetadata('host', \gethostname()) ->setMetadata('project', $project->getId()) - ->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS); + ->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_API) + ->setMaxQueryValues(APP_DATABASE_QUERY_MAX_VALUES); - if ($dsn->getHost() === System::getEnv('_APP_DATABASE_SHARED_TABLES', '')) { + $sharedTables = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', '')); + + if (\in_array($dsn->getHost(), $sharedTables)) { $database ->setSharedTables(true) ->setTenant($project->getInternalId()) @@ -450,7 +422,40 @@ App::setResource('getProjectDB', function (Group $pools, Database $dbForConsole, return $database; }; -}, ['pools', 'dbForConsole', 'cache']); +}, ['pools', 'dbForPlatform', 'cache']); + +App::setResource('getLogsDB', function (Group $pools, Cache $cache) { + $database = null; + return function (?Document $project = null) use ($pools, $cache, $database) { + if ($database !== null && $project !== null && !$project->isEmpty() && $project->getId() !== 'console') { + $database->setTenant($project->getInternalId()); + return $database; + } + + $dbAdapter = $pools + ->get('logs') + ->pop() + ->getResource(); + + $database = new Database( + $dbAdapter, + $cache + ); + + $database + ->setSharedTables(true) + ->setNamespace('logsV1') + ->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_API) + ->setMaxQueryValues(APP_DATABASE_QUERY_MAX_VALUES); + + // set tenant + if ($project !== null && !$project->isEmpty() && $project->getId() !== 'console') { + $database->setTenant($project->getInternalId()); + } + + return $database; + }; +}, ['pools', 'cache']); App::setResource('cache', function (Group $pools) { $list = Config::getParam('pools-cache', []); @@ -467,6 +472,27 @@ App::setResource('cache', function (Group $pools) { return new Cache(new Sharding($adapters)); }, ['pools']); +App::setResource('redis', function () { + $host = System::getEnv('_APP_REDIS_HOST', 'localhost'); + $port = System::getEnv('_APP_REDIS_PORT', 6379); + $pass = System::getEnv('_APP_REDIS_PASS', ''); + + $redis = new \Redis(); + @$redis->pconnect($host, (int)$port); + if ($pass) { + $redis->auth($pass); + } + $redis->setOption(\Redis::OPT_READ_TIMEOUT, -1); + + return $redis; +}); + +App::setResource('timelimit', function (\Redis $redis) { + return function (string $key, int $limit, int $time) use ($redis) { + return new TimeLimitRedis($key, $limit, $time, $redis); + }; +}, ['redis']); + App::setResource('deviceForLocal', function () { return new Local(); }); @@ -483,9 +509,9 @@ App::setResource('deviceForBuilds', function ($project) { return getDevice(APP_STORAGE_BUILDS . '/app-' . $project->getId()); }, ['project']); -function getDevice($root): Device +function getDevice(string $root, string $connection = ''): Device { - $connection = System::getEnv('_APP_CONNECTIONS_STORAGE', ''); + $connection = !empty($connection) ? $connection : System::getEnv('_APP_CONNECTIONS_STORAGE', ''); if (!empty($connection)) { $acl = 'private'; @@ -494,6 +520,7 @@ function getDevice($root): Device $accessSecret = ''; $bucket = ''; $region = ''; + $url = App::getEnv('_APP_STORAGE_S3_ENDPOINT', ''); try { $dsn = new DSN($connection); @@ -508,7 +535,7 @@ function getDevice($root): Device switch ($device) { case Storage::DEVICE_S3: - return new S3($root, $accessKey, $accessSecret, $bucket, $region, $acl); + return new S3($root, $accessKey, $accessSecret, $bucket, $region, $acl, $url); case STORAGE::DEVICE_DO_SPACES: $device = new DOSpaces($root, $accessKey, $accessSecret, $bucket, $region, $acl); $device->setHttpVersion(S3::HTTP_VERSION_1_1); @@ -534,7 +561,8 @@ function getDevice($root): Device $s3Region = System::getEnv('_APP_STORAGE_S3_REGION', ''); $s3Bucket = System::getEnv('_APP_STORAGE_S3_BUCKET', ''); $s3Acl = 'private'; - return new S3($root, $s3AccessKey, $s3SecretKey, $s3Bucket, $s3Region, $s3Acl); + $s3EndpointUrl = App::getEnv('_APP_STORAGE_S3_ENDPOINT', ''); + return new S3($root, $s3AccessKey, $s3SecretKey, $s3Bucket, $s3Region, $s3Acl, $s3EndpointUrl); case Storage::DEVICE_DO_SPACES: $doSpacesAccessKey = System::getEnv('_APP_STORAGE_DO_SPACES_ACCESS_KEY', ''); $doSpacesSecretKey = System::getEnv('_APP_STORAGE_DO_SPACES_SECRET', ''); @@ -727,6 +755,65 @@ App::setResource('requestTimestamp', function ($request) { } return $requestTimestamp; }, ['request']); + App::setResource('plan', function (array $plan = []) { return []; }); + +App::setResource('smsRates', function () { + return []; +}); + +App::setResource('team', function (Document $project, Database $dbForPlatform, App $utopia, Request $request) { + $teamInternalId = ''; + if ($project->getId() !== 'console') { + $teamInternalId = $project->getAttribute('teamInternalId', ''); + } else { + $route = $utopia->match($request); + $path = $route->getPath(); + if (str_starts_with($path, '/v1/projects/:projectId')) { + $uri = $request->getURI(); + $pid = explode('/', $uri)[3]; + $p = Authorization::skip(fn () => $dbForPlatform->getDocument('projects', $pid)); + $teamInternalId = $p->getAttribute('teamInternalId', ''); + } elseif ($path === '/v1/projects') { + $teamId = $request->getParam('teamId', ''); + $team = Authorization::skip(fn () => $dbForPlatform->getDocument('teams', $teamId)); + return $team; + } + } + + $team = Authorization::skip(function () use ($dbForPlatform, $teamInternalId) { + return $dbForPlatform->findOne('teams', [ + Query::equal('$internalId', [$teamInternalId]), + ]); + }); + + return $team; +}, ['project', 'dbForPlatform', 'utopia', 'request']); + +App::setResource( + 'isResourceBlocked', + fn () => fn (Document $project, string $resourceType, ?string $resourceId) => false +); + +App::setResource('previewHostname', function (Request $request) { + if (App::isDevelopment()) { + $host = $request->getQuery('appwrite-hostname') ?? ''; + if (!empty($host)) { + return $host; + } + } + + return ''; +}, ['request']); + +App::setResource('apiKey', function (Request $request, Document $project): ?Key { + $key = $request->getHeader('x-appwrite-key'); + + if (empty($key)) { + return null; + } + + return Key::decode($project, $key); +}, ['request', 'project']); From a2a0a1405f71582ba3a9bb8d5476e378587f0b0a Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Sat, 15 Mar 2025 13:23:02 +0100 Subject: [PATCH 136/143] plurals --- app/init.php | 4 ++-- app/init/{config.php => configs.php} | 0 app/init/{locale.php => locales.php} | 0 app/init/resources.php | 2 -- 4 files changed, 2 insertions(+), 4 deletions(-) rename app/init/{config.php => configs.php} (100%) rename app/init/{locale.php => locales.php} (100%) diff --git a/app/init.php b/app/init.php index 15ef1f6361..fd1a67b28d 100644 --- a/app/init.php +++ b/app/init.php @@ -19,10 +19,10 @@ if (\file_exists(__DIR__ . '/../vendor/autoload.php')) { \error_reporting(E_ALL); require_once __DIR__ . '/init/constants.php'; -require_once __DIR__ . '/init/config.php'; +require_once __DIR__ . '/init/configs.php'; require_once __DIR__ . '/init/database/filters.php'; require_once __DIR__ . '/init/database/formats.php'; -require_once __DIR__ . '/init/locale.php'; +require_once __DIR__ . '/init/locales.php'; require_once __DIR__ . '/init/registers.php'; require_once __DIR__ . '/init/resources.php'; diff --git a/app/init/config.php b/app/init/configs.php similarity index 100% rename from app/init/config.php rename to app/init/configs.php diff --git a/app/init/locale.php b/app/init/locales.php similarity index 100% rename from app/init/locale.php rename to app/init/locales.php diff --git a/app/init/resources.php b/app/init/resources.php index 0895ef32a5..0764ed6e76 100644 --- a/app/init/resources.php +++ b/app/init/resources.php @@ -13,7 +13,6 @@ use Appwrite\Event\Func; use Appwrite\Event\Mail; use Appwrite\Event\Messaging; use Appwrite\Event\Migration; -use Appwrite\Event\Usage; use Appwrite\Extend\Exception; use Appwrite\GraphQL\Schema; use Appwrite\Network\Validator\Origin; @@ -31,7 +30,6 @@ use Utopia\DSN\DSN; use Utopia\Locale\Locale; use Utopia\Logger\Log; use Utopia\Pools\Group; -use Utopia\Queue\Connection; use Utopia\Storage\Device; use Utopia\Storage\Device\Backblaze; use Utopia\Storage\Device\DOSpaces; From 07926534bf04dced17d0576c32199a21550d17b3 Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Sat, 15 Mar 2025 13:24:56 +0100 Subject: [PATCH 137/143] Fixed namespace --- app/init.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/init.php b/app/init.php index fd1a67b28d..c32f1eb9a8 100644 --- a/app/init.php +++ b/app/init.php @@ -8,6 +8,8 @@ * */ +use Utopia\System\System; + if (\file_exists(__DIR__ . '/../vendor/autoload.php')) { require_once __DIR__ . '/../vendor/autoload.php'; } From 875a60fe0797c60a4d692c01bb9e99126a883df9 Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Sat, 15 Mar 2025 13:53:00 +0100 Subject: [PATCH 138/143] fixed paths --- app/init/configs.php | 66 ++++++++++++++++++++++---------------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/app/init/configs.php b/app/init/configs.php index 99baade018..7d2d858351 100644 --- a/app/init/configs.php +++ b/app/init/configs.php @@ -2,36 +2,36 @@ use Utopia\Config\Config; -Config::load('events', __DIR__ . '/config/events.php'); -Config::load('auth', __DIR__ . '/config/auth.php'); -Config::load('apis', __DIR__ . '/config/apis.php'); // List of APIs -Config::load('errors', __DIR__ . '/config/errors.php'); -Config::load('oAuthProviders', __DIR__ . '/config/oAuthProviders.php'); -Config::load('platforms', __DIR__ . '/config/platforms.php'); -Config::load('console', __DIR__ . '/config/console.php'); -Config::load('collections', __DIR__ . '/config/collections.php'); -Config::load('runtimes', __DIR__ . '/config/runtimes.php'); -Config::load('runtimes-v2', __DIR__ . '/config/runtimes-v2.php'); -Config::load('usage', __DIR__ . '/config/usage.php'); -Config::load('roles', __DIR__ . '/config/roles.php'); // User roles and scopes -Config::load('scopes', __DIR__ . '/config/scopes.php'); // User roles and scopes -Config::load('services', __DIR__ . '/config/services.php'); // List of services -Config::load('variables', __DIR__ . '/config/variables.php'); // List of env variables -Config::load('regions', __DIR__ . '/config/regions.php'); // List of available regions -Config::load('avatar-browsers', __DIR__ . '/config/avatars/browsers.php'); -Config::load('avatar-credit-cards', __DIR__ . '/config/avatars/credit-cards.php'); -Config::load('avatar-flags', __DIR__ . '/config/avatars/flags.php'); -Config::load('locale-codes', __DIR__ . '/config/locale/codes.php'); -Config::load('locale-currencies', __DIR__ . '/config/locale/currencies.php'); -Config::load('locale-eu', __DIR__ . '/config/locale/eu.php'); -Config::load('locale-languages', __DIR__ . '/config/locale/languages.php'); -Config::load('locale-phones', __DIR__ . '/config/locale/phones.php'); -Config::load('locale-countries', __DIR__ . '/config/locale/countries.php'); -Config::load('locale-continents', __DIR__ . '/config/locale/continents.php'); -Config::load('locale-templates', __DIR__ . '/config/locale/templates.php'); -Config::load('storage-logos', __DIR__ . '/config/storage/logos.php'); -Config::load('storage-mimes', __DIR__ . '/config/storage/mimes.php'); -Config::load('storage-inputs', __DIR__ . '/config/storage/inputs.php'); -Config::load('storage-outputs', __DIR__ . '/config/storage/outputs.php'); -Config::load('runtime-specifications', __DIR__ . '/config/runtimes/specifications.php'); -Config::load('function-templates', __DIR__ . '/config/function-templates.php'); +Config::load('events',__DIR__ . '/../config/events.php'); +Config::load('auth',__DIR__ . '/../config/auth.php'); +Config::load('apis',__DIR__ . '/../config/apis.php'); // List of APIs +Config::load('errors',__DIR__ . '/../config/errors.php'); +Config::load('oAuthProviders',__DIR__ . '/../config/oAuthProviders.php'); +Config::load('platforms',__DIR__ . '/../config/platforms.php'); +Config::load('console',__DIR__ . '/../config/console.php'); +Config::load('collections',__DIR__ . '/../config/collections.php'); +Config::load('runtimes',__DIR__ . '/../config/runtimes.php'); +Config::load('runtimes-v2',__DIR__ . '/../config/runtimes-v2.php'); +Config::load('usage',__DIR__ . '/../config/usage.php'); +Config::load('roles',__DIR__ . '/../config/roles.php'); // User roles and scopes +Config::load('scopes',__DIR__ . '/../config/scopes.php'); // User roles and scopes +Config::load('services',__DIR__ . '/../config/services.php'); // List of services +Config::load('variables',__DIR__ . '/../config/variables.php'); // List of env variables +Config::load('regions',__DIR__ . '/../config/regions.php'); // List of available regions +Config::load('avatar-browsers',__DIR__ . '/../config/avatars/browsers.php'); +Config::load('avatar-credit-cards',__DIR__ . '/../config/avatars/credit-cards.php'); +Config::load('avatar-flags',__DIR__ . '/../config/avatars/flags.php'); +Config::load('locale-codes',__DIR__ . '/../config/locale/codes.php'); +Config::load('locale-currencies',__DIR__ . '/../config/locale/currencies.php'); +Config::load('locale-eu',__DIR__ . '/../config/locale/eu.php'); +Config::load('locale-languages',__DIR__ . '/../config/locale/languages.php'); +Config::load('locale-phones',__DIR__ . '/../config/locale/phones.php'); +Config::load('locale-countries',__DIR__ . '/../config/locale/countries.php'); +Config::load('locale-continents',__DIR__ . '/../config/locale/continents.php'); +Config::load('locale-templates',__DIR__ . '/../config/locale/templates.php'); +Config::load('storage-logos',__DIR__ . '/../config/storage/logos.php'); +Config::load('storage-mimes',__DIR__ . '/../config/storage/mimes.php'); +Config::load('storage-inputs',__DIR__ . '/../config/storage/inputs.php'); +Config::load('storage-outputs',__DIR__ . '/../config/storage/outputs.php'); +Config::load('runtime-specifications',__DIR__ . '/../config/runtimes/specifications.php'); +Config::load('function-templates',__DIR__ . '/../config/function-templates.php'); From 56aa503146f160923c6bef57ae7ad12d54db7e4a Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Sat, 15 Mar 2025 13:57:12 +0100 Subject: [PATCH 139/143] Fixed paths --- app/init/locales.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/init/locales.php b/app/init/locales.php index 333dd106e3..dce95729e4 100644 --- a/app/init/locales.php +++ b/app/init/locales.php @@ -10,12 +10,12 @@ $locales = Config::getParam('locale-codes', []); foreach ($locales as $locale) { $code = $locale['code']; - $path = __DIR__ . '/config/locale/translations/' . $code . '.json'; + $path =__DIR__ . '/../config/locale/translations/' . $code . '.json'; if (!\file_exists($path)) { - $path = __DIR__ . '/config/locale/translations/' . \substr($code, 0, 2) . '.json'; // if `ar-ae` doesn't exist, look for `ar` + $path =__DIR__ . '/../config/locale/translations/' . \substr($code, 0, 2) . '.json'; // if `ar-ae` doesn't exist, look for `ar` if (!\file_exists($path)) { - $path = __DIR__ . '/config/locale/translations/en.json'; // if none translation exists, use default from `en.json` + $path =__DIR__ . '/../config/locale/translations/en.json'; // if none translation exists, use default from `en.json` } } From aac90ea8b8f4ad9ed4a2511565d98a097e5e735f Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Sat, 15 Mar 2025 14:17:47 +0100 Subject: [PATCH 140/143] Fixed format --- app/init/configs.php | 62 ++++++++++++++++++++++---------------------- app/init/locales.php | 6 ++--- 2 files changed, 34 insertions(+), 34 deletions(-) diff --git a/app/init/configs.php b/app/init/configs.php index 7d2d858351..bc50777df2 100644 --- a/app/init/configs.php +++ b/app/init/configs.php @@ -2,36 +2,36 @@ use Utopia\Config\Config; -Config::load('events',__DIR__ . '/../config/events.php'); -Config::load('auth',__DIR__ . '/../config/auth.php'); -Config::load('apis',__DIR__ . '/../config/apis.php'); // List of APIs -Config::load('errors',__DIR__ . '/../config/errors.php'); -Config::load('oAuthProviders',__DIR__ . '/../config/oAuthProviders.php'); -Config::load('platforms',__DIR__ . '/../config/platforms.php'); -Config::load('console',__DIR__ . '/../config/console.php'); -Config::load('collections',__DIR__ . '/../config/collections.php'); -Config::load('runtimes',__DIR__ . '/../config/runtimes.php'); -Config::load('runtimes-v2',__DIR__ . '/../config/runtimes-v2.php'); -Config::load('usage',__DIR__ . '/../config/usage.php'); -Config::load('roles',__DIR__ . '/../config/roles.php'); // User roles and scopes -Config::load('scopes',__DIR__ . '/../config/scopes.php'); // User roles and scopes -Config::load('services',__DIR__ . '/../config/services.php'); // List of services -Config::load('variables',__DIR__ . '/../config/variables.php'); // List of env variables -Config::load('regions',__DIR__ . '/../config/regions.php'); // List of available regions -Config::load('avatar-browsers',__DIR__ . '/../config/avatars/browsers.php'); -Config::load('avatar-credit-cards',__DIR__ . '/../config/avatars/credit-cards.php'); -Config::load('avatar-flags',__DIR__ . '/../config/avatars/flags.php'); -Config::load('locale-codes',__DIR__ . '/../config/locale/codes.php'); -Config::load('locale-currencies',__DIR__ . '/../config/locale/currencies.php'); -Config::load('locale-eu',__DIR__ . '/../config/locale/eu.php'); -Config::load('locale-languages',__DIR__ . '/../config/locale/languages.php'); -Config::load('locale-phones',__DIR__ . '/../config/locale/phones.php'); -Config::load('locale-countries',__DIR__ . '/../config/locale/countries.php'); -Config::load('locale-continents',__DIR__ . '/../config/locale/continents.php'); -Config::load('locale-templates',__DIR__ . '/../config/locale/templates.php'); -Config::load('storage-logos',__DIR__ . '/../config/storage/logos.php'); -Config::load('storage-mimes',__DIR__ . '/../config/storage/mimes.php'); -Config::load('storage-inputs',__DIR__ . '/../config/storage/inputs.php'); -Config::load('storage-outputs',__DIR__ . '/../config/storage/outputs.php'); +Config::load('events', __DIR__ . '/../config/events.php'); +Config::load('auth', __DIR__ . '/../config/auth.php'); +Config::load('apis', __DIR__ . '/../config/apis.php'); // List of APIs +Config::load('errors', __DIR__ . '/../config/errors.php'); +Config::load('oAuthProviders', __DIR__ . '/../config/oAuthProviders.php'); +Config::load('platforms', __DIR__ . '/../config/platforms.php'); +Config::load('console', __DIR__ . '/../config/console.php'); +Config::load('collections', __DIR__ . '/../config/collections.php'); +Config::load('runtimes', __DIR__ . '/../config/runtimes.php'); +Config::load('runtimes-v2', __DIR__ . '/../config/runtimes-v2.php'); +Config::load('usage', __DIR__ . '/../config/usage.php'); +Config::load('roles', __DIR__ . '/../config/roles.php'); // User roles and scopes +Config::load('scopes', __DIR__ . '/../config/scopes.php'); // User roles and scopes +Config::load('services', __DIR__ . '/../config/services.php'); // List of services +Config::load('variables', __DIR__ . '/../config/variables.php'); // List of env variables +Config::load('regions', __DIR__ . '/../config/regions.php'); // List of available regions +Config::load('avatar-browsers', __DIR__ . '/../config/avatars/browsers.php'); +Config::load('avatar-credit-cards', __DIR__ . '/../config/avatars/credit-cards.php'); +Config::load('avatar-flags', __DIR__ . '/../config/avatars/flags.php'); +Config::load('locale-codes', __DIR__ . '/../config/locale/codes.php'); +Config::load('locale-currencies', __DIR__ . '/../config/locale/currencies.php'); +Config::load('locale-eu', __DIR__ . '/../config/locale/eu.php'); +Config::load('locale-languages', __DIR__ . '/../config/locale/languages.php'); +Config::load('locale-phones', __DIR__ . '/../config/locale/phones.php'); +Config::load('locale-countries', __DIR__ . '/../config/locale/countries.php'); +Config::load('locale-continents', __DIR__ . '/../config/locale/continents.php'); +Config::load('locale-templates', __DIR__ . '/../config/locale/templates.php'); +Config::load('storage-logos', __DIR__ . '/../config/storage/logos.php'); +Config::load('storage-mimes', __DIR__ . '/../config/storage/mimes.php'); +Config::load('storage-inputs', __DIR__ . '/../config/storage/inputs.php'); +Config::load('storage-outputs', __DIR__ . '/../config/storage/outputs.php'); Config::load('runtime-specifications',__DIR__ . '/../config/runtimes/specifications.php'); Config::load('function-templates',__DIR__ . '/../config/function-templates.php'); diff --git a/app/init/locales.php b/app/init/locales.php index dce95729e4..122dc89692 100644 --- a/app/init/locales.php +++ b/app/init/locales.php @@ -10,12 +10,12 @@ $locales = Config::getParam('locale-codes', []); foreach ($locales as $locale) { $code = $locale['code']; - $path =__DIR__ . '/../config/locale/translations/' . $code . '.json'; + $path = __DIR__ . '/../config/locale/translations/' . $code . '.json'; if (!\file_exists($path)) { - $path =__DIR__ . '/../config/locale/translations/' . \substr($code, 0, 2) . '.json'; // if `ar-ae` doesn't exist, look for `ar` + $path = __DIR__ . '/../config/locale/translations/' . \substr($code, 0, 2) . '.json'; // if `ar-ae` doesn't exist, look for `ar` if (!\file_exists($path)) { - $path =__DIR__ . '/../config/locale/translations/en.json'; // if none translation exists, use default from `en.json` + $path = __DIR__ . '/../config/locale/translations/en.json'; // if none translation exists, use default from `en.json` } } From 3d69a35d5ae2b440bc2a6c102d3eaddeadcbada7 Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Sat, 15 Mar 2025 14:32:18 +0100 Subject: [PATCH 141/143] Fixed tests --- app/init/registers.php | 4 ++-- app/init/resources.php | 31 +++++++++++++++++++------------ 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/app/init/registers.php b/app/init/registers.php index 4ce8adcefd..9c90eb54d2 100644 --- a/app/init/registers.php +++ b/app/init/registers.php @@ -324,10 +324,10 @@ $register->set('smtp', function () { return $mail; }); $register->set('geodb', function () { - return new Reader(__DIR__ . '/assets/dbip/dbip-country-lite-2024-09.mmdb'); + return new Reader(__DIR__ . '/../assets/dbip/dbip-country-lite-2024-09.mmdb'); }); $register->set('passwordsDictionary', function () { - $content = \file_get_contents(__DIR__ . '/assets/security/10k-common-passwords'); + $content = \file_get_contents(__DIR__ . '/../assets/security/10k-common-passwords'); $content = explode("\n", $content); $content = array_flip($content); return $content; diff --git a/app/init/resources.php b/app/init/resources.php index 0764ed6e76..9211320fdf 100644 --- a/app/init/resources.php +++ b/app/init/resources.php @@ -3,6 +3,7 @@ use Ahc\Jwt\JWT; use Ahc\Jwt\JWTException; use Appwrite\Auth\Auth; +use Appwrite\Auth\Key; use Appwrite\Event\Audit; use Appwrite\Event\Build; use Appwrite\Event\Certificate; @@ -13,9 +14,14 @@ use Appwrite\Event\Func; use Appwrite\Event\Mail; use Appwrite\Event\Messaging; use Appwrite\Event\Migration; +use Appwrite\Event\Realtime; +use Appwrite\Event\StatsUsage; +use Appwrite\Event\Webhook; use Appwrite\Extend\Exception; use Appwrite\GraphQL\Schema; use Appwrite\Network\Validator\Origin; +use Appwrite\Utopia\Request; +use Utopia\Abuse\Adapters\TimeLimit as TimeLimitRedis; use Utopia\App; use Utopia\Cache\Adapter\Sharding; use Utopia\Cache\Cache; @@ -30,6 +36,7 @@ use Utopia\DSN\DSN; use Utopia\Locale\Locale; use Utopia\Logger\Log; use Utopia\Pools\Group; +use Utopia\Queue\Publisher; use Utopia\Storage\Device; use Utopia\Storage\Device\Backblaze; use Utopia\Storage\Device\DOSpaces; @@ -66,43 +73,43 @@ App::setResource('publisher', function (Group $pools) { App::setResource('consumer', function (Group $pools) { return $pools->get('consumer')->pop()->getResource(); }, ['pools']); -App::setResource('queueForMessaging', function (Queue\Publisher $publisher) { +App::setResource('queueForMessaging', function (Publisher $publisher) { return new Messaging($publisher); }, ['publisher']); -App::setResource('queueForMails', function (Queue\Publisher $publisher) { +App::setResource('queueForMails', function (Publisher $publisher) { return new Mail($publisher); }, ['publisher']); -App::setResource('queueForBuilds', function (Queue\Publisher $publisher) { +App::setResource('queueForBuilds', function (Publisher $publisher) { return new Build($publisher); }, ['publisher']); -App::setResource('queueForDatabase', function (Queue\Publisher $publisher) { +App::setResource('queueForDatabase', function (Publisher $publisher) { return new EventDatabase($publisher); }, ['publisher']); -App::setResource('queueForDeletes', function (Queue\Publisher $publisher) { +App::setResource('queueForDeletes', function (Publisher $publisher) { return new Delete($publisher); }, ['publisher']); -App::setResource('queueForEvents', function (Queue\Publisher $publisher) { +App::setResource('queueForEvents', function (Publisher $publisher) { return new Event($publisher); }, ['publisher']); -App::setResource('queueForWebhooks', function (Queue\Publisher $publisher) { +App::setResource('queueForWebhooks', function (Publisher $publisher) { return new Webhook($publisher); }, ['publisher']); App::setResource('queueForRealtime', function () { return new Realtime(); }, []); -App::setResource('queueForStatsUsage', function (Queue\Publisher $publisher) { +App::setResource('queueForStatsUsage', function (Publisher $publisher) { return new StatsUsage($publisher); }, ['publisher']); -App::setResource('queueForAudits', function (Queue\Publisher $publisher) { +App::setResource('queueForAudits', function (Publisher $publisher) { return new Audit($publisher); }, ['publisher']); -App::setResource('queueForFunctions', function (Queue\Publisher $publisher) { +App::setResource('queueForFunctions', function (Publisher $publisher) { return new Func($publisher); }, ['publisher']); -App::setResource('queueForCertificates', function (Queue\Publisher $publisher) { +App::setResource('queueForCertificates', function (Publisher $publisher) { return new Certificate($publisher); }, ['publisher']); -App::setResource('queueForMigrations', function (Queue\Publisher $publisher) { +App::setResource('queueForMigrations', function (Publisher $publisher) { return new Migration($publisher); }, ['publisher']); App::setResource('clients', function ($request, $console, $project) { From 167011ce872521a85888cb6df15c5be59b10fa1f Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Sat, 15 Mar 2025 14:38:01 +0100 Subject: [PATCH 142/143] Fixed timelimit --- app/init/resources.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/init/resources.php b/app/init/resources.php index 9211320fdf..96c52a2350 100644 --- a/app/init/resources.php +++ b/app/init/resources.php @@ -21,7 +21,7 @@ use Appwrite\Extend\Exception; use Appwrite\GraphQL\Schema; use Appwrite\Network\Validator\Origin; use Appwrite\Utopia\Request; -use Utopia\Abuse\Adapters\TimeLimit as TimeLimitRedis; +use Utopia\Abuse\Adapters\TimeLimit\Redis as TimeLimitRedis; use Utopia\App; use Utopia\Cache\Adapter\Sharding; use Utopia\Cache\Cache; From 418185b9147c387bf0c6aed79c94cf938eee05c4 Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Sat, 15 Mar 2025 14:43:51 +0100 Subject: [PATCH 143/143] fixed format --- app/init/configs.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/init/configs.php b/app/init/configs.php index bc50777df2..1a5dbced1b 100644 --- a/app/init/configs.php +++ b/app/init/configs.php @@ -33,5 +33,5 @@ Config::load('storage-logos', __DIR__ . '/../config/storage/logos.php'); Config::load('storage-mimes', __DIR__ . '/../config/storage/mimes.php'); Config::load('storage-inputs', __DIR__ . '/../config/storage/inputs.php'); Config::load('storage-outputs', __DIR__ . '/../config/storage/outputs.php'); -Config::load('runtime-specifications',__DIR__ . '/../config/runtimes/specifications.php'); -Config::load('function-templates',__DIR__ . '/../config/function-templates.php'); +Config::load('runtime-specifications', __DIR__ . '/../config/runtimes/specifications.php'); +Config::load('function-templates', __DIR__ . '/../config/function-templates.php');