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
diff --git a/Dockerfile b/Dockerfile
index 2bb9f80d9e..88d5ed030b 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.1 AS final
LABEL maintainer="team@appwrite.io"
@@ -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/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..c3585dbb68 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://apwr.dev/appcloud)
@@ -24,11 +24,9 @@
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).
+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).
@@ -52,7 +50,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)
@@ -79,7 +77,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 +89,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 +99,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/cli.php b/app/cli.php
index 0b2cb884e6..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));
@@ -99,6 +98,10 @@ CLI::setResource('dbForPlatform', function ($pools, $cache) {
return $dbForPlatform;
}, ['pools', 'cache']);
+CLI::setResource('console', function () {
+ return new Document(Config::getParam('console'));
+}, []);
+
CLI::setResource('getProjectDB', function (Group $pools, Database $dbForPlatform, $cache) {
$databases = []; // TODO: @Meldiron This should probably be responsibility of utopia-php/pools
@@ -183,7 +186,7 @@ CLI::setResource('getLogsDB', function (Group $pools, Cache $cache) {
$database
->setSharedTables(true)
->setNamespace('logsV1')
- ->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS)
+ ->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_TASK)
->setMaxQueryValues(APP_DATABASE_QUERY_MAX_VALUES);
// set tenant
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/config/console.php b/app/config/console.php
new file mode 100644
index 0000000000..e37c9b7836
--- /dev/null
+++ b/app/config/console.php
@@ -0,0 +1,53 @@
+ 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
+ ],
+ 'region' => 'fra',
+ '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/config/platforms.php b/app/config/platforms.php
index 92e325bd44..86f0a756c9 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' => '15.0.0',
'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,
@@ -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,
@@ -217,7 +217,7 @@ return [
[
'key' => 'cli',
'name' => 'Command Line',
- 'version' => '6.2.0',
+ 'version' => '6.2.2',
'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,
@@ -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,
@@ -371,7 +371,7 @@ return [
[
'key' => 'dart',
'name' => 'Dart',
- 'version' => '12.2.0',
+ 'version' => '14.0.0',
'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/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-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..54161c4262 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"
@@ -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-latest-server.json b/app/config/specs/open-api3-latest-server.json
index 68d408762a..3d32d3e978 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"
@@ -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/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-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..8fc7e7daf3 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"
},
@@ -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-latest-server.json b/app/config/specs/swagger2-latest-server.json
index e38495629c..83757c94f4 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"
},
@@ -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/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')
diff --git a/app/controllers/api/databases.php b/app/controllers/api/databases.php
index 013e4639cc..d8d496089c 100644
--- a/app/controllers/api/databases.php
+++ b/app/controllers/api/databases.php
@@ -243,8 +243,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,
@@ -300,6 +300,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');
}
@@ -1560,8 +1563,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');
@@ -1637,8 +1640,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');
@@ -2349,8 +2352,8 @@ 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')
@@ -2408,8 +2411,8 @@ 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')
diff --git a/app/controllers/api/functions.php b/app/controllers/api/functions.php
index cdaf062788..88e1e2ae7b 100644
--- a/app/controllers/api/functions.php
+++ b/app/controllers/api/functions.php
@@ -6,13 +6,14 @@ 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\Event\Webhook;
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 +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, 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
@@ -383,54 +387,34 @@ App::post('/v1/functions')
'resourceInternalId' => $function->getInternalId(),
'status' => 'verified',
'certificateId' => '',
+ 'owner' => 'Appwrite',
+ 'region' => $project->getAttribute('region')
]))
);
- /** 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())));
- $ruleCreate
- ->setProject($project)
- ->setEvent('rules.[ruleId].create')
- ->setParam('ruleId', $rule->getId())
- ->setPayload($rule->getArrayCopy(array_keys($ruleModel->getRules())))
+ /** Trigger Webhook */
+ $queueForWebhooks
+ ->from($ruleCreate)
->trigger();
/** Trigger Functions */
- $ruleCreate
- ->setClass(Event::FUNCTIONS_CLASS_NAME)
- ->setQueue(Event::FUNCTIONS_QUEUE_NAME)
+ $queueForFunctions
+ ->from($ruleCreate)
->trigger();
- /** Trigger realtime event */
- $allEvents = Event::generateEvents('rules.[ruleId].create', [
- '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($ruleCreate)
+ ->setSubscribers(['console', $project->getId()])
+ ->trigger();
}
$queueForEvents->setParam('functionId', $function->getId());
diff --git a/app/controllers/api/project.php b/app/controllers/api/project.php
index ea2cd4436d..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,
@@ -63,6 +67,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,
@@ -75,6 +80,7 @@ App::get('/v1/project/usage')
METRIC_BUILDS_MB_SECONDS,
METRIC_DATABASES_OPERATIONS_READS,
METRIC_DATABASES_OPERATIONS_WRITES,
+ METRIC_FILES_IMAGES_TRANSFORMED,
]
];
@@ -93,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'])
]);
@@ -103,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),
@@ -363,6 +373,8 @@ App::get('/v1/project/usage')
'authPhoneTotal' => $authPhoneTotal,
'authPhoneEstimate' => $authPhoneEstimate,
'authPhoneCountryBreakdown' => $authPhoneCountryBreakdown,
+ 'imageTransformations' => $usage[METRIC_FILES_IMAGES_TRANSFORMED],
+ 'imageTransformationsTotal' => $total[METRIC_FILES_IMAGES_TRANSFORMED],
]), Response::MODEL_USAGE_PROJECT);
});
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/api/storage.php b/app/controllers/api/storage.php
index f180c22acf..f13c9703c5 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;
@@ -935,15 +934,11 @@ 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('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, Database $dbForProject, Device $deviceForFiles, Device $deviceForLocal) {
if (!\extension_loaded('imagick')) {
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Imagick extension is missing');
@@ -1071,22 +1066,19 @@ 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
->addHeader('Cache-Control', 'private, max-age=2592000') // 30 days
->setContentType($contentType)
- ->file($data)
- ;
+ ->file($data);
unset($image);
});
@@ -1879,9 +1871,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,12 +1889,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_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'])
]);
@@ -1907,7 +1906,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),
@@ -1948,5 +1947,7 @@ App::get('/v1/storage/:bucketId/usage')
'filesStorageTotal' => $usage[$metrics[1]]['total'],
'files' => $usage[$metrics[0]]['data'],
'storage' => $usage[$metrics[1]]['data'],
+ 'imageTransformations' => $usage[$metrics[2]]['data'],
+ 'imageTransformationsTotal' => $usage[$metrics[2]]['total'],
]), Response::MODEL_USAGE_BUCKETS);
});
diff --git a/app/controllers/general.php b/app/controllers/general.php
index 6db4a8b28d..a374aa3c39 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
@@ -589,8 +595,10 @@ App::init()
'domain' => $domain->get(),
'resourceType' => 'api',
'status' => 'verifying',
- 'projectId' => 'console',
- 'projectInternalId' => 'console'
+ 'projectId' => $console->getId(),
+ 'projectInternalId' => $console->getInternalId(),
+ 'owner' => $owner,
+ 'region' => $console->getAttribute('region')
]);
$domainDocument = $dbForPlatform->createDocument('rules', $domainDocument);
@@ -737,7 +745,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 +769,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()
@@ -848,11 +867,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();
@@ -874,7 +891,10 @@ App::error()
}
}
- if ($publish && $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');
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));
+ }
}
}
diff --git a/app/init.php b/app/init.php
index c0713ba327..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';
}
@@ -18,1118 +20,13 @@ 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\Auth\Key;
-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\Realtime;
-use Appwrite\Event\StatsUsage;
-use Appwrite\Event\Webhook;
-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\PubSub\Adapter\Redis as PubSub;
-use Appwrite\URL\URL as AppwriteURL;
-use Appwrite\Utopia\Request;
-use MaxMind\Db\Reader;
-use PHPMailer\PHPMailer\PHPMailer;
-use Swoole\Database\PDOProxy;
-use Utopia\Abuse\Adapters\TimeLimit\Redis as TimeLimitRedis;
-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\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_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_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_DATABASE_QUERY_MAX_VALUES = 500;
-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_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;
-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';
-const DELETE_TYPE_MAINTENANCE = 'maintenance';
-
-// 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_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';
-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_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';
-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';
-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';
-
-$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', '');
-
- 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));
-
- 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' => $fallbackForDB,
- 'multiple' => false,
- 'schemes' => ['mariadb', 'mysql'],
- ],
- 'database' => [
- 'type' => 'database',
- 'dsns' => $fallbackForDB,
- 'multiple' => true,
- 'schemes' => ['mariadb', 'mysql'],
- ],
- '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' => $fallbackForRedis,
- 'multiple' => false,
- 'schemes' => ['redis'],
- ],
- 'cache' => [
- 'type' => 'cache',
- 'dsns' => $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 = intval(System::getEnv('_APP_CPU_NUM', 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());
- return $adapter;
- case 'pubsub':
- 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
- };
- case 'cache':
- return match ($dsn->getScheme()) {
- 'redis' => new RedisCache($resource()),
- default => null
- };
- default:
- throw new Exception(Exception::GENERAL_SERVER_ERROR, "Server error: Missing adapter implementation.");
- }
- });
-
- $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/configs.php';
+require_once __DIR__ . '/init/database/filters.php';
+require_once __DIR__ . '/init/database/formats.php';
+require_once __DIR__ . '/init/locales.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' => [
@@ -1142,818 +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('publisher', function (Group $pools) {
- return $pools->get('publisher')->pop()->getResource();
-}, ['pools']);
-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('queueForUsage', function (Queue\Publisher $publisher) {
- return new Usage($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'),
- '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, $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 $dbForPlatform */
- /** @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 = $dbForPlatform->getDocument('users', Auth::$unique);
- } else {
- $user = $dbForProject->getDocument('users', Auth::$unique);
- }
- }
- } else {
- $user = $dbForPlatform->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());
- $dbForPlatform->setMetadata('user', $user->getId());
-
- return $user;
-}, ['mode', 'project', 'console', 'request', 'response', 'dbForProject', 'dbForPlatform']);
-
-App::setResource('project', function ($dbForPlatform, $request, $console) {
- /** @var Appwrite\Utopia\Request $request */
- /** @var Utopia\Database\Database $dbForPlatform */
- /** @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 () => $dbForPlatform->getDocument('projects', $projectId));
-
- return $project;
-}, ['dbForPlatform', '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' => 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', '')
- ],
- ]);
-}, []);
-
-App::setResource('dbForProject', function (Group $pools, Database $dbForPlatform, Cache $cache, Document $project) {
- if ($project->isEmpty() || $project->getId() === 'console') {
- return $dbForPlatform;
- }
-
- 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)
- ->setMaxQueryValues(APP_DATABASE_QUERY_MAX_VALUES);
-
- $sharedTables = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', ''));
-
- if (\in_array($dsn->getHost(), $sharedTables)) {
- $database
- ->setSharedTables(true)
- ->setTenant($project->getInternalId())
- ->setNamespace($dsn->getParam('namespace'));
- } else {
- $database
- ->setSharedTables(false)
- ->setTenant(null)
- ->setNamespace('_' . $project->getInternalId());
- }
-
- return $database;
-}, ['pools', 'dbForPlatform', 'cache', 'project']);
-
-App::setResource('dbForPlatform', 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)
- ->setMaxQueryValues(APP_DATABASE_QUERY_MAX_VALUES);
-
- return $database;
-}, ['pools', '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, $dbForPlatform, $cache, &$databases) {
- if ($project->isEmpty() || $project->getId() === 'console') {
- return $dbForPlatform;
- }
-
- 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)
- ->setMaxQueryValues(APP_DATABASE_QUERY_MAX_VALUES);
-
- $sharedTables = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', ''));
-
- if (\in_array($dsn->getHost(), $sharedTables)) {
- $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', '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)
- ->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', []);
- $adapters = [];
-
- foreach ($list as $value) {
- $adapters[] = $pools
- ->get($value)
- ->pop()
- ->getResource()
- ;
- }
-
- 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();
-});
-
-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(string $root, string $connection = ''): Device
-{
- $connection = !empty($connection) ? $connection : System::getEnv('_APP_CONNECTIONS_STORAGE', '');
-
- if (!empty($connection)) {
- $acl = 'private';
- $device = Storage::DEVICE_LOCAL;
- $accessKey = '';
- $accessSecret = '';
- $bucket = '';
- $region = '';
- $url = App::getEnv('_APP_STORAGE_S3_ENDPOINT', '');
-
- 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, $url);
- 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';
- $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', '');
- $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 [];
-});
-
-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']);
diff --git a/app/init/configs.php b/app/init/configs.php
new file mode 100644
index 0000000000..1a5dbced1b
--- /dev/null
+++ b/app/init/configs.php
@@ -0,0 +1,37 @@
+ $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', '');
+
+ 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));
+
+ 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..6c73877576
--- /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', '');
+
+ if (empty($providerConfig)) {
+ return;
+ }
+
+ 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' => $fallbackForDB,
+ 'multiple' => false,
+ 'schemes' => ['mariadb', 'mysql'],
+ ],
+ 'database' => [
+ 'type' => 'database',
+ 'dsns' => $fallbackForDB,
+ 'multiple' => true,
+ 'schemes' => ['mariadb', 'mysql'],
+ ],
+ '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' => $fallbackForRedis,
+ 'multiple' => false,
+ 'schemes' => ['redis'],
+ ],
+ 'cache' => [
+ 'type' => 'cache',
+ 'dsns' => $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 = intval(System::getEnv('_APP_CPU_NUM', 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());
+ return $adapter;
+ case 'pubsub':
+ 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
+ };
+ case 'cache':
+ return match ($dsn->getScheme()) {
+ 'redis' => new RedisCache($resource()),
+ default => null
+ };
+ default:
+ throw new Exception(Exception::GENERAL_SERVER_ERROR, "Server error: Missing adapter implementation.");
+ }
+ });
+
+ $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..96c52a2350
--- /dev/null
+++ b/app/init/resources.php
@@ -0,0 +1,824 @@
+ 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('publisher', function (Group $pools) {
+ return $pools->get('publisher')->pop()->getResource();
+}, ['pools']);
+App::setResource('consumer', function (Group $pools) {
+ return $pools->get('consumer')->pop()->getResource();
+}, ['pools']);
+App::setResource('queueForMessaging', function (Publisher $publisher) {
+ return new Messaging($publisher);
+}, ['publisher']);
+App::setResource('queueForMails', function (Publisher $publisher) {
+ return new Mail($publisher);
+}, ['publisher']);
+App::setResource('queueForBuilds', function (Publisher $publisher) {
+ return new Build($publisher);
+}, ['publisher']);
+App::setResource('queueForDatabase', function (Publisher $publisher) {
+ return new EventDatabase($publisher);
+}, ['publisher']);
+App::setResource('queueForDeletes', function (Publisher $publisher) {
+ return new Delete($publisher);
+}, ['publisher']);
+App::setResource('queueForEvents', function (Publisher $publisher) {
+ return new Event($publisher);
+}, ['publisher']);
+App::setResource('queueForWebhooks', function (Publisher $publisher) {
+ return new Webhook($publisher);
+}, ['publisher']);
+App::setResource('queueForRealtime', function () {
+ return new Realtime();
+}, []);
+App::setResource('queueForStatsUsage', function (Publisher $publisher) {
+ return new StatsUsage($publisher);
+}, ['publisher']);
+App::setResource('queueForAudits', function (Publisher $publisher) {
+ return new Audit($publisher);
+}, ['publisher']);
+App::setResource('queueForFunctions', function (Publisher $publisher) {
+ return new Func($publisher);
+}, ['publisher']);
+App::setResource('queueForCertificates', function (Publisher $publisher) {
+ return new Certificate($publisher);
+}, ['publisher']);
+App::setResource('queueForMigrations', function (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'),
+ '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, $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 $dbForPlatform */
+ /** @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 = $dbForPlatform->getDocument('users', Auth::$unique);
+ } else {
+ $user = $dbForProject->getDocument('users', Auth::$unique);
+ }
+ }
+ } else {
+ $user = $dbForPlatform->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());
+ $dbForPlatform->setMetadata('user', $user->getId());
+
+ return $user;
+}, ['mode', 'project', 'console', 'request', 'response', 'dbForProject', 'dbForPlatform']);
+
+App::setResource('project', function ($dbForPlatform, $request, $console) {
+ /** @var Appwrite\Utopia\Request $request */
+ /** @var Utopia\Database\Database $dbForPlatform */
+ /** @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 () => $dbForPlatform->getDocument('projects', $projectId));
+
+ return $project;
+}, ['dbForPlatform', '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(Config::getParam('console'));
+}, []);
+
+App::setResource('dbForProject', function (Group $pools, Database $dbForPlatform, Cache $cache, Document $project) {
+ if ($project->isEmpty() || $project->getId() === 'console') {
+ return $dbForPlatform;
+ }
+
+ 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_API)
+ ->setMaxQueryValues(APP_DATABASE_QUERY_MAX_VALUES);
+
+ $sharedTables = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', ''));
+
+ if (\in_array($dsn->getHost(), $sharedTables)) {
+ $database
+ ->setSharedTables(true)
+ ->setTenant($project->getInternalId())
+ ->setNamespace($dsn->getParam('namespace'));
+ } else {
+ $database
+ ->setSharedTables(false)
+ ->setTenant(null)
+ ->setNamespace('_' . $project->getInternalId());
+ }
+
+ return $database;
+}, ['pools', 'dbForPlatform', 'cache', 'project']);
+
+App::setResource('dbForPlatform', 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_API)
+ ->setMaxQueryValues(APP_DATABASE_QUERY_MAX_VALUES);
+
+ return $database;
+}, ['pools', '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, $dbForPlatform, $cache, &$databases) {
+ if ($project->isEmpty() || $project->getId() === 'console') {
+ return $dbForPlatform;
+ }
+
+ 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_API)
+ ->setMaxQueryValues(APP_DATABASE_QUERY_MAX_VALUES);
+
+ $sharedTables = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', ''));
+
+ if (\in_array($dsn->getHost(), $sharedTables)) {
+ $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', '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', []);
+ $adapters = [];
+
+ foreach ($list as $value) {
+ $adapters[] = $pools
+ ->get($value)
+ ->pop()
+ ->getResource()
+ ;
+ }
+
+ 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();
+});
+
+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(string $root, string $connection = ''): Device
+{
+ $connection = !empty($connection) ? $connection : System::getEnv('_APP_CONNECTIONS_STORAGE', '');
+
+ if (!empty($connection)) {
+ $acl = 'private';
+ $device = Storage::DEVICE_LOCAL;
+ $accessKey = '';
+ $accessSecret = '';
+ $bucket = '';
+ $region = '';
+ $url = App::getEnv('_APP_STORAGE_S3_ENDPOINT', '');
+
+ 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, $url);
+ 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';
+ $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', '');
+ $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 [];
+});
+
+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']);
diff --git a/app/views/install/compose.phtml b/app/views/install/compose.phtml
index 62fcd03624..4d118071cc 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
@@ -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: /:
diff --git a/app/worker.php b/app/worker.php
index 605474e9f1..eeefe80000 100644
--- a/app/worker.php
+++ b/app/worker.php
@@ -13,12 +13,12 @@ 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 */
-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;
@@ -112,6 +112,8 @@ Server::setResource('dbForProject', function (Cache $cache, Registry $register,
->setNamespace('_' . $project->getInternalId());
}
+ $database->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_WORKER);
+
return $database;
}, ['cache', 'register', 'message', 'project', 'dbForPlatform']);
@@ -173,6 +175,8 @@ Server::setResource('getProjectDB', function (Group $pools, Database $dbForPlatf
->setNamespace('_' . $project->getInternalId());
}
+ $database->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_WORKER);
+
return $database;
};
}, ['pools', 'dbForPlatform', 'cache']);
@@ -198,7 +202,7 @@ Server::setResource('getLogsDB', function (Group $pools, Cache $cache) {
$database
->setSharedTables(true)
->setNamespace('logsV1')
- ->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS)
+ ->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_WORKER)
->setMaxQueryValues(APP_DATABASE_QUERY_MAX_VALUES);
// set tenant
@@ -269,14 +273,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']);
@@ -313,10 +309,18 @@ 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']);
+Server::setResource('queueForRealtime', function () {
+ return new Realtime();
+}, []);
+
Server::setResource('queueForCertificates', function (Publisher $publisher) {
return new Certificate($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/composer.json b/composer.json
index 6180773b24..53c2a6d482 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/cache": "0.11.*",
+ "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.*",
@@ -59,19 +59,19 @@
"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",
+ "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.*",
"utopia-php/system": "0.9.*",
"utopia-php/telemetry": "0.1.*",
- "utopia-php/vcs": "0.8.*",
+ "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 841d5b51c5..ed19d10202 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": "44c6436ced36b0b026139edba252052e",
"packages": [
{
"name": "adhocore/jwt",
@@ -279,16 +279,16 @@
},
{
"name": "brick/math",
- "version": "0.12.1",
+ "version": "0.12.3",
"source": {
"type": "git",
"url": "https://github.com/brick/math.git",
- "reference": "f510c0a40911935b77b86859eb5223d58d660df1"
+ "reference": "866551da34e9a618e64a819ee1e01c20d8a588ba"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/brick/math/zipball/f510c0a40911935b77b86859eb5223d58d660df1",
- "reference": "f510c0a40911935b77b86859eb5223d58d660df1",
+ "url": "https://api.github.com/repos/brick/math/zipball/866551da34e9a618e64a819ee1e01c20d8a588ba",
+ "reference": "866551da34e9a618e64a819ee1e01c20d8a588ba",
"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.3"
},
"funding": [
{
@@ -335,7 +335,7 @@
"type": "github"
}
],
- "time": "2023-11-29T23:19:16+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.1",
"source": {
"type": "git",
"url": "https://github.com/protocolbuffers/protobuf-php.git",
- "reference": "ab5077c2cfdd1f415f42d11fdbdf903ba8e3d9b7"
+ "reference": "f29ba8a30dfd940efb3a8a75dc44446539101f24"
},
"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/f29ba8a30dfd940efb3a8a75dc44446539101f24",
+ "reference": "f29ba8a30dfd940efb3a8a75dc44446539101f24",
"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.1"
},
- "time": "2025-01-08T21:00:13+00:00"
+ "time": "2025-03-13T21:08:17+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",
@@ -2694,16 +2681,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 +2756,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 +2772,7 @@
"type": "tidelift"
}
],
- "time": "2025-01-28T15:51:35+00:00"
+ "time": "2025-02-13T10:27:23+00:00"
},
{
"name": "symfony/http-client-contracts",
@@ -3377,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": {
@@ -3394,7 +3381,7 @@
"ext-pdo": "*",
"ext-redis": "*",
"php": ">=8.0",
- "utopia-php/database": "0.60.*"
+ "utopia-php/database": "0.*.*"
},
"require-dev": {
"laravel/pint": "1.*",
@@ -3422,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",
@@ -3474,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.*",
@@ -3515,29 +3502,30 @@
],
"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",
- "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 +3553,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 +3705,23 @@
},
{
"name": "utopia-php/database",
- "version": "0.60.3",
+ "version": "0.61.2",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/database.git",
- "reference": "c4bc4af3f09a91aea76aac75b4b78fa06598c61d"
+ "reference": "349fbdf4bc088f7775c7dfb8b80239a617a88436"
},
"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/349fbdf4bc088f7775c7dfb8b80239a617a88436",
+ "reference": "349fbdf4bc088f7775c7dfb8b80239a617a88436",
"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 +3755,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.61.2"
},
- "time": "2025-02-17T12:46:59+00:00"
+ "time": "2025-03-15T11:47:42+00:00"
},
{
"name": "utopia-php/domains",
@@ -3880,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": {
@@ -3913,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": {
@@ -3960,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",
@@ -4120,16 +4108,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,22 +4153,22 @@
],
"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",
- "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": {
@@ -4188,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.*"
@@ -4221,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",
@@ -4337,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": {
@@ -4355,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.*",
@@ -4381,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",
@@ -4491,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": {
@@ -4550,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",
@@ -4608,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": "*",
@@ -4657,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",
@@ -4820,23 +4810,24 @@
},
{
"name": "utopia-php/vcs",
- "version": "0.8.6",
+ "version": "0.9.4",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/vcs.git",
- "reference": "b10225f54d5670f09f83e82e09de9d820ada6931"
+ "reference": "1a8d280b176acc99ea8d9e7364b8767cbb206b4a"
},
"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/1a8d280b176acc99ea8d9e7364b8767cbb206b4a",
+ "reference": "1a8d280b176acc99ea8d9e7364b8767cbb206b4a",
"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 +4854,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.4"
},
- "time": "2024-12-10T13:13:23+00:00"
+ "time": "2025-03-13T10:09:45+00:00"
},
{
"name": "utopia-php/websocket",
@@ -5052,16 +5043,16 @@
"packages-dev": [
{
"name": "appwrite/sdk-generator",
- "version": "0.40.0",
+ "version": "0.40.7",
"source": {
"type": "git",
"url": "https://github.com/appwrite/sdk-generator.git",
- "reference": "d2880132c900f64108d3e4484a6c1ed1bed2303c"
+ "reference": "9e89b0bc4d8e6c81817d27096629f34a149fa873"
},
"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/9e89b0bc4d8e6c81817d27096629f34a149fa873",
+ "reference": "9e89b0bc4d8e6c81817d27096629f34a149fa873",
"shasum": ""
},
"require": {
@@ -5097,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.0"
+ "source": "https://github.com/appwrite/sdk-generator/tree/0.40.7"
},
- "time": "2025-02-04T12:47:33+00:00"
+ "time": "2025-03-12T08:43:55+00:00"
},
{
"name": "doctrine/annotations",
@@ -5326,16 +5317,16 @@
},
{
"name": "laravel/pint",
- "version": "v1.21.0",
+ "version": "v1.21.2",
"source": {
"type": "git",
"url": "https://github.com/laravel/pint.git",
- "reference": "531fa0871fbde719c51b12afa3a443b8f4e4b425"
+ "reference": "370772e7d9e9da087678a0edf2b11b6960e40558"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/laravel/pint/zipball/531fa0871fbde719c51b12afa3a443b8f4e4b425",
- "reference": "531fa0871fbde719c51b12afa3a443b8f4e4b425",
+ "url": "https://api.github.com/repos/laravel/pint/zipball/370772e7d9e9da087678a0edf2b11b6960e40558",
+ "reference": "370772e7d9e9da087678a0edf2b11b6960e40558",
"shasum": ""
},
"require": {
@@ -5346,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.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",
@@ -5388,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-14T22:31:42+00:00"
},
{
"name": "matthiasmullie/minify",
@@ -5803,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": {
@@ -5889,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": [
{
@@ -5897,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",
@@ -8035,16 +8026,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 +8067,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 +8083,7 @@
"type": "tidelift"
}
],
- "time": "2024-11-06T14:24:19+00:00"
+ "time": "2025-02-05T08:33:46+00:00"
},
{
"name": "symfony/string",
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
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
)
diff --git a/docs/sdks/cli/GETTING_STARTED.md b/docs/sdks/cli/GETTING_STARTED.md
index 564fb4d5f9..1cadb1bbda 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"}' --permissions 'read("any")' 'read("team:abc")'
```
### 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
+```
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
diff --git a/src/Appwrite/Event/Realtime.php b/src/Appwrite/Event/Realtime.php
index f4f00b59d4..28a1bb6a6d 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 $subscribers = [];
+
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 subscribers for this realtime event.
+ *
+ * @param array $subscribers
+ * @return array
+ */
+ public function setSubscribers(array $subscribers): self
+ {
+ $this->subscribers = $subscribers;
+ return $this;
+ }
+
+ /**
+ * Get subscribers for this realtime event.
+ *
+ * @return array
+ */
+ public function getSubscribers(): array
+ {
+ return $this->subscribers;
+ }
+
/**
* Execute Event.
*
@@ -53,17 +82,23 @@ class Realtime extends Event
bucket: $bucket,
);
- 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->getSubscribers())
+ ? $this->getSubscribers()
+ : [$target['projectId'] ?? $this->getProject()->getId()];
+
+ 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;
}
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,
- ];
- }
-}
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/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);
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
*/
diff --git a/src/Appwrite/Platform/Workers/Audits.php b/src/Appwrite/Platform/Workers/Audits.php
index 977aff3cfb..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;
@@ -17,15 +16,16 @@ 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;
private array $logs = [];
- private function getBatchSize(): int
+
+ protected function getBatchSize(): int
{
return System::getEnv('_APP_ENV', 'development') === 'development'
? self::BATCH_SIZE_DEVELOPMENT
@@ -101,7 +101,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()])) {
diff --git a/src/Appwrite/Platform/Workers/Builds.php b/src/Appwrite/Platform/Workers/Builds.php
index c9833adcfb..6f26e9a80c 100644
--- a/src/Appwrite/Platform/Workers/Builds.php
+++ b/src/Appwrite/Platform/Workers/Builds.php
@@ -5,8 +5,9 @@ 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\Event\Webhook;
use Appwrite\Utopia\Response\Model\Deployment;
use Appwrite\Vcs\Comment;
use Exception;
@@ -49,15 +50,17 @@ class Builds extends Action
->inject('project')
->inject('dbForPlatform')
->inject('queueForEvents')
+ ->inject('queueForWebhooks')
->inject('queueForFunctions')
+ ->inject('queueForRealtime')
->inject('queueForStatsUsage')
->inject('cache')
->inject('dbForProject')
->inject('deviceForFunctions')
->inject('isResourceBlocked')
->inject('log')
- ->callback(fn ($message, Document $project, Database $dbForPlatform, Event $queueForEvents, Func $queueForFunctions, StatsUsage $usage, Cache $cache, Database $dbForProject, Device $deviceForFunctions, callable $isResourceBlocked, Log $log) =>
- $this->action($message, $project, $dbForPlatform, $queueForEvents, $queueForFunctions, $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));
}
/**
@@ -65,7 +68,9 @@ 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
* @param Cache $cache
* @param Database $dbForProject
@@ -74,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, 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() ?? [];
@@ -95,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, $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:
@@ -105,7 +110,9 @@ class Builds extends Action
/**
* @param Device $deviceForFunctions
+ * @param Webhook $queueForWebhooks
* @param Func $queueForFunctions
+ * @param Realtime $queueForRealtime
* @param Event $queueForEvents
* @param StatsUsage $queueForStatsUsage
* @param Database $dbForPlatform
@@ -120,7 +127,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, 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'));
@@ -158,10 +165,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);
@@ -375,21 +379,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)
+ ->setSubscribers(['console'])
+ ->setEvent($event)
+ ->setParam('functionId', $function->getId())
+ ->setParam('deploymentId', $deployment->getId())
+ ->setPayload($build->getArrayCopy())
+ ->trigger();
}
$tmpPath = '/tmp/builds/' . $buildId;
@@ -436,40 +435,34 @@ 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
->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)
+ ->setSubscribers(['console'])
+ ->setEvent($event)
+ ->setParam('functionId', $function->getId())
+ ->setParam('deploymentId', $deployment->getId())
+ ->setPayload($build->getArrayCopy())
+ ->trigger();
$vars = [];
@@ -562,12 +555,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;
}
@@ -592,21 +585,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)
+ ->setSubscribers(['console'])
+ ->setEvent($event)
+ ->setParam('functionId', $function->getId())
+ ->setParam('deploymentId', $deployment->getId())
+ ->setPayload($build->getArrayCopy())
+ ->trigger();
}
}
);
@@ -694,21 +682,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)
+ ->setSubscribers(['console'])
+ ->setEvent($event)
+ ->setParam('functionId', $function->getId())
+ ->setParam('deploymentId', $deployment->getId())
+ ->setPayload($build->getArrayCopy())
+ ->trigger();
/** Trigger usage queue */
if ($build->getAttribute('status') === 'ready') {
diff --git a/src/Appwrite/Platform/Workers/Certificates.php b/src/Appwrite/Platform/Workers/Certificates.php
index 7e220b2734..093e6fda5a 100644
--- a/src/Appwrite/Platform/Workers/Certificates.php
+++ b/src/Appwrite/Platform/Workers/Certificates.php
@@ -6,7 +6,8 @@ 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\Event\Webhook;
use Appwrite\Network\Validator\CNAME;
use Appwrite\Template\Template;
use Appwrite\Utopia\Response\Model\Rule;
@@ -46,12 +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, Log $log, CertificatesAdapter $certificates) =>
- $this->action($message, $dbForPlatform, $queueForMails, $queueForEvents, $queueForFunctions, $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)
);
}
@@ -60,14 +63,16 @@ 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
* @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, Webhook $queueForWebhooks, Func $queueForFunctions, Realtime $queueForRealtime, Log $log, CertificatesAdapter $certificates): void
{
$payload = $message->getPayload() ?? [];
@@ -81,7 +86,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, $queueForWebhooks, $queueForFunctions, $queueForRealtime, $log, $certificates, $skipRenewCheck);
}
/**
@@ -90,13 +95,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, Webhook $queueForWebhooks, Func $queueForFunctions, Realtime $queueForRealtime, Log $log, CertificatesAdapter $certificates, bool $skipRenewCheck = false): void
{
/**
* 1. Read arguments and validate domain
@@ -186,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);
+ $this->saveCertificateDocument($domain->get(), $certificate, $success, $dbForPlatform, $queueForEvents, $queueForWebhooks, $queueForFunctions, $queueForRealtime);
}
}
@@ -199,13 +205,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, Webhook $queueForWebhooks, Func $queueForFunctions, Realtime $queueForRealtime): void
{
// Check if update or insert required
$certificateDocument = $dbForPlatform->findOne('certificates', [Query::equal('domain', [$domain])]);
@@ -219,7 +226,7 @@ class Certificates extends Action
}
$certificateId = $certificate->getId();
- $this->updateDomainDocuments($certificateId, $domain, $success, $dbForPlatform, $queueForEvents, $queueForFunctions);
+ $this->updateDomainDocuments($certificateId, $domain, $success, $dbForPlatform, $queueForEvents, $queueForWebhooks, $queueForFunctions, $queueForRealtime);
}
/**
@@ -338,7 +345,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, 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') {
@@ -367,50 +374,28 @@ class Certificates extends Action
return;
}
- /** Trigger Webhook */
$ruleModel = new Rule();
$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();
+ ->setPayload($rule->getArrayCopy(array_keys($ruleModel->getRules())));
+ /** Trigger Webhook */
+ $queueForWebhooks
+ ->from($queueForEvents)
+ ->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)
+ ->setSubscribers(['console', $projectId])
+ ->trigger();
}
}
}
diff --git a/src/Appwrite/Platform/Workers/Databases.php b/src/Appwrite/Platform/Workers/Databases.php
index 0f400e0107..4abd035599 100644
--- a/src/Appwrite/Platform/Workers/Databases.php
+++ b/src/Appwrite/Platform/Workers/Databases.php
@@ -2,8 +2,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;
@@ -37,8 +36,9 @@ class Databases extends Action
->inject('project')
->inject('dbForPlatform')
->inject('dbForProject')
+ ->inject('queueForRealtime')
->inject('log')
- ->callback(fn (Message $message, Document $project, Database $dbForPlatform, Database $dbForProject, Log $log) => $this->action($message, $project, $dbForPlatform, $dbForProject, $log));
+ ->callback(fn (Message $message, Document $project, Database $dbForPlatform, Database $dbForProject, Realtime $queueForRealtime, Log $log) => $this->action($message, $project, $dbForPlatform, $dbForProject, $queueForRealtime, $log));
}
/**
@@ -46,16 +46,17 @@ class Databases extends Action
* @param Document $project
* @param Database $dbForPlatform
* @param Database $dbForProject
+ * @param Realtime $queueForRealtime
* @param Log $log
* @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, Realtime $queueForRealtime, Log $log): void
{
$payload = $message->getPayload() ?? [];
if (empty($payload)) {
- throw new \Exception('Missing payload');
+ throw new Exception('Missing payload');
}
$type = $payload['type'];
@@ -75,11 +76,11 @@ 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),
- default => throw new \Exception('No database operation for type: ' . \strval($type)),
+ 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 +91,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 +108,7 @@ 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()
- ]);
+ $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.
@@ -169,7 +166,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');
}
}
@@ -200,7 +197,7 @@ class Databases extends Action
throw $e;
} finally {
- $this->trigger($database, $collection, $attribute, $project, $projectId, $events);
+ $this->trigger($database, $collection, $project, $event, $queueForRealtime, $attribute);
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');
@@ -233,15 +231,9 @@ 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()
- ]);
+ $event = 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].delete';
$collectionId = $collection->getId();
$key = $attribute->getAttribute('key', '');
- $status = $attribute->getAttribute('status', '');
$type = $attribute->getAttribute('type', '');
$project = $dbForPlatform->getDocument('projects', $projectId);
$options = $attribute->getAttribute('options', []);
@@ -312,7 +304,7 @@ class Databases extends Action
throw $e;
} finally {
- $this->trigger($database, $collection, $attribute, $project, $projectId, $events);
+ $this->trigger($database, $collection, $project, $event, $queueForRealtime, $attribute);
}
// The underlying database removes/rebuilds indexes when attribute is removed
@@ -358,7 +350,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 +373,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 +381,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');
@@ -398,12 +391,7 @@ 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()
- ]);
+ $event = 'databases.[databaseId].collections.[collectionId].indexes.[indexId].update';
$collectionId = $collection->getId();
$key = $index->getAttribute('key', '');
$type = $index->getAttribute('type', '');
@@ -430,7 +418,7 @@ class Databases extends Action
throw $e;
} finally {
- $this->trigger($database, $collection, $index, $project, $projectId, $events);
+ $this->trigger($database, $collection, $project, $event, $queueForRealtime, null, $index);
$dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $collectionId);
}
}
@@ -442,6 +430,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 +438,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');
@@ -459,12 +448,7 @@ 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()
- ]);
+ $event = 'databases.[databaseId].collections.[collectionId].indexes.[indexId].delete';
$key = $index->getAttribute('key');
$status = $index->getAttribute('status', '');
$project = $dbForPlatform->getDocument('projects', $projectId);
@@ -490,7 +474,7 @@ class Databases extends Action
throw $e;
} finally {
- $this->trigger($database, $collection, $index, $project, $projectId, $events);
+ $this->trigger($database, $collection, $project, $event, $queueForRealtime, null, $index);
$dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $collection->getId());
}
}
@@ -532,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());
@@ -568,21 +551,21 @@ 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
{
$start = \microtime(true);
try {
- $documents = $database->deleteDocuments($collection, $queries);
+ $documents = $database->deleteDocuments($collectionId, $queries);
} catch (\Throwable $th) {
- Console::error('Failed to delete documents for collection ' . $collection . ': ' . $th->getMessage());
+ Console::error('Failed to delete documents for collection ' . $collectionId . ': ' . $th->getMessage());
return;
}
@@ -598,31 +581,42 @@ class Databases extends Action
Console::info("Deleted {$count} documents by group in " . ($end - $start) . " seconds");
}
+ /**
+ * @param Document $database
+ * @param Document $collection
+ * @param Document $project
+ * @param Realtime $queueForRealtime
+ * @param Document|null $attribute
+ * @param Document|null $index
+ * @return void
+ */
protected function trigger(
Document $database,
Document $collection,
- Document $attribute,
Document $project,
- string $projectId,
- array $events
+ string $event,
+ Realtime $queueForRealtime,
+ Document|null $attribute = null,
+ Document|null $index = null,
): 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)
+ ->setSubscribers(['console'])
+ ->setEvent($event)
+ ->setParam('databaseId', $database->getId())
+ ->setParam('collectionId', $collection->getId());
+
+ if ($attribute !== null && !empty($attribute)) {
+ $queueForRealtime
+ ->setParam('attributeId', $attribute->getId())
+ ->setPayload($attribute->getArrayCopy());
+ }
+ if ($index !== null && !empty($index)) {
+ $queueForRealtime
+ ->setParam('indexId', $index->getId())
+ ->setPayload($index->getArrayCopy());
+ }
+
+ $queueForRealtime->trigger();
}
}
diff --git a/src/Appwrite/Platform/Workers/Deletes.php b/src/Appwrite/Platform/Workers/Deletes.php
index e11181d199..9b0590181a 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:
@@ -387,6 +387,8 @@ class Deletes extends Action
$query = [
Query::lessThan('accessedAt', $datetime),
+ Query::orderDesc('accessedAt'),
+ Query::orderDesc('$internalId'),
];
$this->deleteByGroup(
@@ -412,14 +414,31 @@ 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
{
+ /** @var Database $dbForProject*/
$dbForProject = $getProjectDB($project);
- // Delete Usage stats
+
+ // 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*/
+ $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);
+ }
}
/**
@@ -675,9 +694,12 @@ 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'),
+ Query::orderDesc('$internalId'),
], $dbForProject);
}
@@ -695,7 +717,9 @@ class Deletes extends Action
// Delete Sessions
$this->deleteByGroup('sessions', [
- Query::lessThan('$createdAt', $expired)
+ Query::lessThan('$createdAt', $expired),
+ Query::orderDesc('$createdAt'),
+ Query::orderDesc('$internalId'),
], $dbForProject);
}
@@ -709,7 +733,9 @@ class Deletes extends Action
{
// Delete Dead Realtime Logs
$this->deleteByGroup('realtime', [
- Query::lessThan('timestamp', $datetime)
+ Query::lessThan('timestamp', $datetime),
+ Query::orderDesc('timestamp'),
+ Query::orderDesc('$internalId'),
], $dbForPlatform);
}
@@ -724,10 +750,13 @@ 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'),
+ Query::orderDesc('$internalId'),
+ ], $dbForProject);
} catch (DatabaseException $e) {
Console::error('Failed to delete audit logs for project ' . $projectId . ': ' . $e->getMessage());
}
@@ -851,7 +880,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 +910,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());
@@ -935,7 +964,7 @@ class Deletes extends Action
* @param Database $database
* @param ?callable $callback
* @return void
- * @throws Exception
+ * @throws DatabaseException
*/
protected function deleteByGroup(
string $collection,
@@ -947,7 +976,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;
}
diff --git a/src/Appwrite/Platform/Workers/Functions.php b/src/Appwrite/Platform/Workers/Functions.php
index 0a7c39c02f..a7caa3207f 100644
--- a/src/Appwrite/Platform/Workers/Functions.php
+++ b/src/Appwrite/Platform/Workers/Functions.php
@@ -5,8 +5,9 @@ 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\Event\Webhook;
use Appwrite\Utopia\Response\Model\Execution;
use Exception;
use Executor\Executor;
@@ -44,15 +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, 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, 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, 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() ?? [];
@@ -136,7 +139,9 @@ class Functions extends Action
$this->execute(
log: $log,
dbForProject: $dbForProject,
+ queueForWebhooks: $queueForWebhooks,
queueForFunctions: $queueForFunctions,
+ queueForRealtime: $queueForRealtime,
queueForStatsUsage: $queueForStatsUsage,
queueForEvents: $queueForEvents,
project: $project,
@@ -176,7 +181,9 @@ class Functions extends Action
$this->execute(
log: $log,
dbForProject: $dbForProject,
+ queueForWebhooks: $queueForWebhooks,
queueForFunctions: $queueForFunctions,
+ queueForRealtime: $queueForRealtime,
queueForStatsUsage: $queueForStatsUsage,
queueForEvents: $queueForEvents,
project: $project,
@@ -198,7 +205,9 @@ class Functions extends Action
$this->execute(
log: $log,
dbForProject: $dbForProject,
+ queueForWebhooks: $queueForWebhooks,
queueForFunctions: $queueForFunctions,
+ queueForRealtime: $queueForRealtime,
queueForStatsUsage: $queueForStatsUsage,
queueForEvents: $queueForEvents,
project: $project,
@@ -284,6 +293,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
@@ -307,7 +317,9 @@ class Functions extends Action
private function execute(
Log $log,
Database $dbForProject,
+ Webhook $queueForWebhooks,
Func $queueForFunctions,
+ Realtime $queueForRealtime,
StatsUsage $queueForStatsUsage,
Event $queueForEvents,
Document $project,
@@ -564,20 +576,20 @@ 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 */
+ $queueForWebhooks
+ ->from($queueForEvents)
->trigger();
/** Trigger Functions */
@@ -585,31 +597,11 @@ 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)
+ ->setSubscribers(['console', $project->getId()])
+ ->trigger();
if (!empty($error)) {
throw new Exception($error, $errorCode);
diff --git a/src/Appwrite/Platform/Workers/Migrations.php b/src/Appwrite/Platform/Workers/Migrations.php
index 3bf8e06356..f21a846a0d 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,16 @@ 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(),
- ]);
-
- $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'],
- );
+ /** Trigger Realtime Events */
+ $queueForRealtime
+ ->setProject($project)
+ ->setSubscribers(['console', $project->getId()])
+ ->setEvent('migrations.[migrationId].update')
+ ->setParam('migrationId', $migration->getId())
+ ->setPayload($migration->getArrayCopy())
+ ->trigger();
return $this->dbForProject->updateDocument('migrations', $migration->getId(), $migration);
}
@@ -231,7 +213,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());
@@ -255,7 +237,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);
@@ -269,14 +251,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')
@@ -313,7 +295,7 @@ class Migrations extends Action
}
$migration->setAttribute('errors', $errorMessages);
- $this->updateMigrationDocument($migration, $projectDocument);
+ $this->updateMigrationDocument($migration, $projectDocument, $queueForRealtime);
return;
}
@@ -354,7 +336,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().')');
diff --git a/src/Appwrite/Platform/Workers/StatsResources.php b/src/Appwrite/Platform/Workers/StatsResources.php
index 62046bd186..1140698342 100644
--- a/src/Appwrite/Platform/Workers/StatsResources.php
+++ b/src/Appwrite/Platform/Workers/StatsResources.php
@@ -77,10 +77,13 @@ 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 ' . $executionTime .' seconds');
}
-
protected function countForProject(Database $dbForPlatform, callable $getLogsDB, callable $getProjectDB, Document $project): void
{
Console::info('Begining count for: ' . $project->getId());
@@ -182,7 +185,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 +245,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)
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');
diff --git a/src/Appwrite/Platform/Workers/StatsUsageDump.php b/src/Appwrite/Platform/Workers/StatsUsageDump.php
index 5d7240f4b5..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'
];
/**
@@ -117,7 +119,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;
}
@@ -134,7 +136,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());
}
@@ -142,7 +144,11 @@ class StatsUsageDump extends Action
}
foreach ($this->periods as $period => $format) {
- $time = 'inf' === $period ? null : date($format, time());
+ $time = null;
+
+ if ($period !== 'inf') {
+ $time = !empty($receivedAt) ? (new \DateTime($receivedAt))->format($format) : date($format, time());
+ }
$id = \md5("{$time}_{$period}_{$key}");
$document = new Document([
@@ -171,12 +177,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([
@@ -197,7 +203,11 @@ class StatsUsageDump extends Action
};
foreach ($this->periods as $period => $format) {
- $time = 'inf' === $period ? null : date($format, time());
+ $time = null;
+
+ if ($period !== 'inf') {
+ $time = !empty($receivedAt) ? (new \DateTime($receivedAt))->format($format) : date($format, time());
+ }
$id = \md5("{$time}_{$period}_{$key}");
$value = 0;
@@ -320,7 +330,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;
}
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');
- }
-}
diff --git a/src/Appwrite/Utopia/Response/Model/UsageBuckets.php b/src/Appwrite/Utopia/Response/Model/UsageBuckets.php
index 2f528ac9d1..ee624a29ad 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('imageTransformations', [
+ 'type' => Response::MODEL_METRIC,
+ 'description' => 'Aggregated number of files transformations per period.',
+ 'default' => [],
+ 'example' => [],
+ 'array' => true
+ ])
+ ->addRule('imageTransformationsTotal', [
+ 'type' => self::TYPE_INTEGER,
+ 'description' => 'Total aggregated number of files transformations.',
+ 'default' => 0,
+ 'example' => 0,
+ ])
;
}
diff --git a/src/Appwrite/Utopia/Response/Model/UsageProject.php b/src/Appwrite/Utopia/Response/Model/UsageProject.php
index 1006276b56..395b19b7cd 100644
--- a/src/Appwrite/Utopia/Response/Model/UsageProject.php
+++ b/src/Appwrite/Utopia/Response/Model/UsageProject.php
@@ -197,6 +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' => '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..e614e2e185 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;
@@ -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(
@@ -150,7 +148,7 @@ class UsageTest extends Scope
);
$this->assertEquals(200, $response['headers']['status-code']);
- $this->assertEquals(29, count($response['body']));
+ $this->assertEquals(31, count($response['body']));
$this->validateDates($response['body']['network']);
$this->validateDates($response['body']['requests']);
$this->validateDates($response['body']['users']);
@@ -309,7 +307,7 @@ class UsageTest extends Scope
/**
* @depends testPrepareStorageStats
*/
- #[Retry(count: 1)]
+ #[Retry(count: 10)]
public function testStorageStats(array $data): array
{
$bucketId = $data['bucketId'];
@@ -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',
@@ -331,7 +327,7 @@ class UsageTest extends Scope
]
);
- $this->assertEquals(29, 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']);
@@ -474,10 +470,10 @@ class UsageTest extends Scope
$this->assertEquals('name', $response['body']['key']);
- $requestsTotal += 1;
-
sleep(self::WAIT);
+ $requestsTotal += 1;
+
for ($i = 0; $i < self::CREATE; $i++) {
$name = uniqid() . ' collection';
@@ -552,7 +548,7 @@ class UsageTest extends Scope
]
);
- $this->assertEquals(29, 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']);
@@ -709,8 +705,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 +746,6 @@ class UsageTest extends Scope
// if ($i === 2) {
// throw $e;
// }
- // sleep(self::WAIT);
// continue;
// }
// }
@@ -792,8 +785,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 +826,6 @@ class UsageTest extends Scope
// if ($i === 2) {
// throw $e;
// }
- // sleep(self::WAIT);
// continue;
// }
// }
@@ -1027,8 +1017,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,52 +1140,40 @@ class UsageTest extends Scope
$this->assertEquals(200, $response['headers']['status-code']);
- sleep(self::WAIT + 20);
$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/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)
]);
diff --git a/tests/e2e/Services/Databases/DatabasesCustomServerTest.php b/tests/e2e/Services/Databases/DatabasesCustomServerTest.php
index b501e2119e..57e0b93634 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' => 10,
+ 'max' => 100,
+ ]);
+
+ $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,30 @@ 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']);
+
+ $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' => 23.456,
+ 'max' => 100.0,
+ ]);
+
+ $this->assertEquals(200, $update['headers']['status-code']);
+
/**
* Test against failure
*/
@@ -2631,32 +2653,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'],
diff --git a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php
index 3ed4ca727e..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
{
@@ -1686,9 +1685,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'
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;
}
/**
diff --git a/tests/e2e/Services/Realtime/RealtimeCustomClientTest.php b/tests/e2e/Services/Realtime/RealtimeCustomClientTest.php
index dda524fc7c..e356397408 100644
--- a/tests/e2e/Services/Realtime/RealtimeCustomClientTest.php
+++ b/tests/e2e/Services/Realtime/RealtimeCustomClientTest.php
@@ -1312,22 +1312,15 @@ class RealtimeCustomClientTest extends Scope
$this->assertNotEmpty($deployment['body']['$id']);
// 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;
- }
-
- \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/Storage/StorageConsoleClientTest.php b/tests/e2e/Services/Storage/StorageConsoleClientTest.php
index 5b6731b35e..bbb14fb136 100644
--- a/tests/e2e/Services/Storage/StorageConsoleClientTest.php
+++ b/tests/e2e/Services/Storage/StorageConsoleClientTest.php
@@ -98,11 +98,13 @@ 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']);
$this->assertIsArray($response['body']['files']);
$this->assertIsArray($response['body']['storage']);
+ $this->assertIsArray($response['body']['imageTransformations']);
+ $this->assertIsNumeric($response['body']['imageTransformationsTotal']);
}
}
diff --git a/tests/e2e/Services/Webhooks/WebhooksBase.php b/tests/e2e/Services/Webhooks/WebhooksBase.php
index 2ef41003ee..c743810feb 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,29 +13,20 @@ use Utopia\Database\Validator\Datetime as DatetimeValidator;
trait WebhooksBase
{
- protected function awaitDeploymentIsBuilt($functionId, $deploymentId, $checkForSuccess = true): void
+ use Async;
+
+ protected function awaitDeploymentIsBuilt($functionId, $deploymentId): void
{
- while (true) {
+ $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 (
- $deployment['headers']['status-code'] >= 400
- || \in_array($deployment['body']['status'], ['ready', 'failed'])
- ) {
- break;
- }
-
- \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